apm.c revision 48938
13259Sdg/*
221363Snate * apm / zzz	APM BIOS utility for FreeBSD
33259Sdg *
433657Shosokawa * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
53259Sdg *
63259Sdg * This software may be used, modified, copied, distributed, and sold,
73259Sdg * in both source and binary form provided that the above copyright and
88857Srgrimes * these terms are retained. Under no circumstances is the author
98857Srgrimes * responsible for the proper functioning of this software, nor does
108857Srgrimes * the author assume any responsibility for damages incurred with its
113259Sdg * use.
123259Sdg *
133259Sdg * Sep., 1994	Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
143259Sdg */
153259Sdg
1629030Scharnier#ifndef lint
1729030Scharnierstatic const char rcsid[] =
1848938Sgreen	"$Id: apm.c,v 1.14 1998/09/04 16:08:54 imp Exp $";
1929030Scharnier#endif /* not lint */
2029030Scharnier
2148938Sgreen#include <sys/file.h>
2248938Sgreen#include <sys/ioctl.h>
2348938Sgreen#include <sys/types.h>
2448938Sgreen#include <sys/sysctl.h>
2548938Sgreen
2648938Sgreen#include <machine/apm_bios.h>
2748938Sgreen
2829030Scharnier#include <err.h>
293259Sdg#include <stdio.h>
3021363Snate#include <stdlib.h>
313259Sdg#include <string.h>
323259Sdg#include <sys/file.h>
333259Sdg#include <sys/ioctl.h>
3448938Sgreen#include <time.h>
3521363Snate#include <unistd.h>
363259Sdg
3714674Snate#define APMDEV	"/dev/apm"
383259Sdg
3938809Simp#define xh(a)	(((a) & 0xff00) >> 8)
4038809Simp#define xl(a)	((a) & 0xff)
4138809Simp#define APMERR(a) xh(a)
4238809Simp
4338809Simpint cmos_wall = 0;	/* True when wall time is in cmos clock, else UTC */
4438809Simp
4521363Snatevoid
4621363Snateusage()
473259Sdg{
4829030Scharnier	fprintf(stderr, "%s\n%s\n",
4938809Simp		"usage: apm [-ablstzZ] [-d 1|0] [-r delta]",
5029030Scharnier		"       zzz");
5121363Snate	exit(1);
5221363Snate}
5321363Snate
5438809Simpint
5538809Simpint2bcd(int i)
5638809Simp{
5738809Simp	int retval = 0;
5838809Simp	int base = 0;
5938809Simp
6038809Simp	if (i >= 10000)
6138809Simp		return -1;
6238809Simp
6338809Simp	while (i) {
6438809Simp		retval |= (i % 10) << base;
6538809Simp		i /= 10;
6638809Simp		base += 4;
6738809Simp	}
6838809Simp	return retval;
6938809Simp}
7038809Simp
7138809Simpint
7238809Simpbcd2int(int bcd)
7338809Simp{
7438809Simp	int retval = 0;
7538809Simp
7638809Simp	if (bcd > 0x9999)
7738809Simp		return -1;
7838809Simp
7938809Simp	while (bcd) {
8038809Simp		retval = retval * 10 + ((bcd & 0xf000) >> 12);
8138809Simp		bcd = (bcd & 0xfff) << 4;
8238809Simp	}
8338809Simp	return retval;
8438809Simp}
8538809Simp
8621363Snatevoid
8721363Snateapm_suspend(int fd)
8821363Snate{
8929030Scharnier	if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
9029030Scharnier		err(1, NULL);
913259Sdg}
923259Sdg
9321363Snatevoid
9438809Simpapm_standby(int fd)
9538809Simp{
9638809Simp	if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
9738809Simp		err(1, NULL);
9838809Simp}
9938809Simp
10038809Simpvoid
10121363Snateapm_getinfo(int fd, apm_info_t aip)
1023259Sdg{
10329030Scharnier	if (ioctl(fd, APMIO_GETINFO, aip) == -1)
10429030Scharnier		err(1, NULL);
1053259Sdg}
1063259Sdg
10721363Snatevoid
10838809Simpprint_all_info(int fd, apm_info_t aip)
1093259Sdg{
11038809Simp	struct apm_bios_arg args;
11138809Simp	int apmerr;
11238809Simp
1133259Sdg	printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
11421363Snate	printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
1153259Sdg	printf("AC Line status: ");
11621363Snate	if (aip->ai_acline == 255)
1173259Sdg		printf("unknown");
11821363Snate	else if (aip->ai_acline > 1)
1193259Sdg		printf("invalid value (0x%x)", aip->ai_acline);
1203259Sdg	else {
12121364Snate		char messages[][10] = {"off-line", "on-line"};
1223259Sdg		printf("%s", messages[aip->ai_acline]);
1233259Sdg	}
1243259Sdg	printf("\n");
1253259Sdg	printf("Battery status: ");
12621363Snate	if (aip->ai_batt_stat == 255)
1273259Sdg		printf("unknown");
12821363Snate	else if (aip->ai_batt_stat > 3)
12921363Snate			printf("invalid value (0x%x)", aip->ai_batt_stat);
1303259Sdg	else {
13121363Snate		char messages[][10] = {"high", "low", "critical", "charging"};
1323259Sdg		printf("%s", messages[aip->ai_batt_stat]);
1333259Sdg	}
1343259Sdg	printf("\n");
1353259Sdg	printf("Remaining battery life: ");
13621363Snate	if (aip->ai_batt_life == 255)
1373259Sdg		printf("unknown");
13821363Snate	else if (aip->ai_batt_life <= 100)
13930996Simp		printf("%d%%", aip->ai_batt_life);
14021363Snate	else
1413259Sdg		printf("invalid value (0x%x)", aip->ai_batt_life);
1423259Sdg	printf("\n");
14331127Sjdp	printf("Remaining battery time: ");
14431127Sjdp	if (aip->ai_batt_time == -1)
14531127Sjdp		printf("unknown");
14631127Sjdp	else {
14731127Sjdp		int t, h, m, s;
14831127Sjdp
14931127Sjdp		t = aip->ai_batt_time;
15031127Sjdp		s = t % 60;
15131127Sjdp		t /= 60;
15231127Sjdp		m = t % 60;
15331127Sjdp		t /= 60;
15431127Sjdp		h = t;
15531127Sjdp		printf("%2d:%02d:%02d", h, m, s);
15631127Sjdp	}
15731127Sjdp	printf("\n");
15838809Simp	if (aip->ai_infoversion >= 1) {
15938809Simp		printf("Number of batteries: ");
16038809Simp		if (aip->ai_batteries == (u_int) -1)
16138809Simp			printf("unknown\n");
16238809Simp		else
16338809Simp			printf("%d\n", aip->ai_batteries);
16438809Simp	}
16538809Simp
16638809Simp	/*
16738809Simp	 * try to get the suspend timer
16838809Simp	 */
16938809Simp	bzero(&args, sizeof(args));
17038809Simp	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
17138809Simp	args.ebx = PMDV_APMBIOS;
17238809Simp	args.ecx = 0x0001;
17338809Simp	if (ioctl(fd, APMIO_BIOS, &args)) {
17438809Simp		err(1,"Get resume timer");
17538809Simp	} else {
17638809Simp		apmerr = APMERR(args.eax);
17738809Simp		 if (apmerr == 0x0d || apmerr == 0x86)
17838809Simp			printf("Resume timer: disabled\n");
17938809Simp		else if (apmerr)
18038809Simp			fprintf(stderr,
18138809Simp			    "Failed to get the resume timer: APM error0x%x\n",
18238809Simp			    apmerr);
18338809Simp		else {
18438809Simp			/*
18538809Simp			 * OK.  We have the time (all bcd).
18638809Simp			 * CH - seconds
18738809Simp			 * DH - hours
18838809Simp			 * DL - minutes
18938809Simp			 * xh(SI) - month (1-12)
19038809Simp			 * xl(SI) - day of month (1-31)
19138809Simp			 * DI - year
19238809Simp			 */
19338809Simp			struct tm tm;
19438809Simp			char buf[1024];
19538809Simp			time_t t;
19638809Simp
19738809Simp			tm.tm_sec = bcd2int(xh(args.ecx));
19838809Simp			tm.tm_min = bcd2int(xl(args.edx));
19938809Simp			tm.tm_hour = bcd2int(xh(args.edx));
20038809Simp			tm.tm_mday = bcd2int(xl(args.esi));
20138809Simp			tm.tm_mon = bcd2int(xh(args.esi)) - 1;
20238809Simp			tm.tm_year = bcd2int(args.edi) - 1900;
20338809Simp			if (cmos_wall)
20438809Simp				t = mktime(&tm);
20538809Simp			else
20638809Simp				t = timegm(&tm);
20738809Simp			tm = *localtime(&t);
20838809Simp			strftime(buf, sizeof(buf), "%c", &tm);
20938809Simp			printf("Resume timer: %s\n", buf);
21038809Simp		}
21138809Simp	}
21238809Simp
21338809Simp	/*
21438809Simp	 * Get the ring indicator resume state
21538809Simp	 */
21638809Simp	bzero(&args, sizeof(args));
21738809Simp	args.eax  = (APM_BIOS) << 8 | APM_RESUMEONRING;
21838809Simp	args.ebx = PMDV_APMBIOS;
21938809Simp	args.ecx = 0x0002;
22038809Simp	if (ioctl(fd, APMIO_BIOS, &args) == 0) {
22138809Simp		printf("Resume on ring indicator: %sabled\n",
22238809Simp			args.ecx ? "en" : "dis");
22338809Simp	}
22438809Simp
22538809Simp	if (aip->ai_infoversion >= 1) {
22638809Simp		printf("APM Capacities:\n", aip->ai_capabilities);
22738809Simp		if (aip->ai_capabilities == 0xff00)
22838809Simp			printf("\tunknown\n");
22938809Simp		if (aip->ai_capabilities & 0x01)
23038809Simp			printf("\tglobal standby state\n");
23138809Simp		if (aip->ai_capabilities & 0x02)
23238809Simp			printf("\tglobal suspend state\n");
23338809Simp		if (aip->ai_capabilities & 0x04)
23438809Simp			printf("\tresume timer from standby\n");
23538809Simp		if (aip->ai_capabilities & 0x08)
23638809Simp			printf("\tresume timer from suspend\n");
23738809Simp		if (aip->ai_capabilities & 0x10)
23838809Simp			printf("\tRI resume from standby\n");
23938809Simp		if (aip->ai_capabilities & 0x20)
24038809Simp			printf("\tRI resume from suspend\n");
24138809Simp		if (aip->ai_capabilities & 0x40)
24238809Simp			printf("\tPCMCIA RI resume from standby\n");
24338809Simp		if (aip->ai_capabilities & 0x80)
24438809Simp			printf("\tPCMCIA RI resume from suspend\n");
24538809Simp	}
24638809Simp
2473259Sdg}
2483259Sdg
24921363Snate/*
25021363Snate * currently, it can turn off the display, but the display never comes
25121363Snate * back until the machine suspend/resumes :-).
25221363Snate */
25321363Snatevoid
25421363Snateapm_display(int fd, int newstate)
2553259Sdg{
25629030Scharnier	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
25729030Scharnier		err(1, NULL);
25821363Snate}
2598857Srgrimes
26021363Snate
26138809Simpvoid
26238809Simpapm_set_timer(int fd, int delta)
26338809Simp{
26438809Simp	time_t tmr;
26538809Simp	struct tm *tm;
26638809Simp	struct apm_bios_arg args;
26738809Simp
26838809Simp	tmr = time(NULL) + delta;
26938809Simp	if (cmos_wall)
27038809Simp		tm = localtime(&tmr);
27138809Simp	else
27238809Simp		tm = gmtime(&tmr);
27338809Simp	bzero(&args, sizeof(args));
27438809Simp	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
27538809Simp	args.ebx = PMDV_APMBIOS;
27638809Simp	if (delta > 0) {
27738809Simp		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
27838809Simp		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
27938809Simp		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
28038809Simp		args.edi = int2bcd(tm->tm_year + 1900);
28138809Simp	} else {
28238809Simp		args.ecx = 0x0000;
28338809Simp	}
28438809Simp	if (ioctl(fd, APMIO_BIOS, &args)) {
28538809Simp		err(1,"Set resume timer");
28638809Simp	}
28738809Simp}
28838809Simp
28921363Snateint
29021363Snatemain(int argc, char *argv[])
29121363Snate{
29221363Snate	int	c, fd;
29321363Snate	int     sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
29438809Simp	int     display = 0, batt_life = 0, ac_status = 0, standby = 0;
29538809Simp	int	batt_time = 0, delta = 0;
29629030Scharnier	char	*cmdname;
29748938Sgreen	size_t	cmos_wall_len = sizeof(cmos_wall);
29821363Snate
29948938Sgreen	if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
30048938Sgreen	    NULL, 0) == -1)
30148938Sgreen		err(1, "sysctlbyname(machdep.wall_cmos_clock)");
30221363Snate	if ((cmdname = strrchr(argv[0], '/')) != NULL)
3033259Sdg		cmdname++;
30421363Snate	else
3053259Sdg		cmdname = argv[0];
3063259Sdg
3073259Sdg	if (strcmp(cmdname, "zzz") == 0) {
3083259Sdg		sleep = 1;
3093259Sdg		all_info = 0;
3103259Sdg		goto finish_option;
3113259Sdg	}
31238809Simp	while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) {
31321363Snate		switch (c) {
31421363Snate		case 'a':
31521363Snate			ac_status = 1;
31621363Snate			all_info = 0;
31721363Snate			break;
31821363Snate		case 'b':
31921363Snate			batt_status = 1;
32021363Snate			all_info = 0;
32121363Snate			break;
32221363Snate		case 'd':
32321363Snate			display = *optarg - '0';
32421363Snate			if (display < 0 || display > 1) {
32529030Scharnier				warnx("argument of option '-%c' is invalid", c);
32621363Snate				usage();
3273259Sdg			}
32821363Snate			display++;
32921363Snate			all_info = 0;
33021363Snate			break;
33121363Snate		case 'l':
33221363Snate			batt_life = 1;
33321363Snate			all_info = 0;
33421363Snate			break;
33538809Simp		case 'R':
33638809Simp			delta = -1;
33738809Simp			break;
33838809Simp		case 'r':
33938809Simp			delta = atoi(optarg);
34038809Simp			break;
34121363Snate		case 's':
34221363Snate			apm_status = 1;
34321363Snate			all_info = 0;
34421363Snate			break;
34531127Sjdp		case 't':
34631127Sjdp			batt_time = 1;
34731127Sjdp			all_info = 0;
34831127Sjdp			break;
34921363Snate		case 'z':
35021363Snate			sleep = 1;
35121363Snate			all_info = 0;
35221363Snate			break;
35338809Simp		case 'Z':
35438809Simp			standby = 1;
35538809Simp			all_info = 0;
35638809Simp			break;
35721363Snate		case '?':
35821363Snate		default:
35921363Snate			usage();
3603259Sdg		}
36121363Snate		argc -= optind;
36221363Snate		argv += optind;
3633259Sdg	}
3643259Sdgfinish_option:
3653259Sdg	fd = open(APMDEV, O_RDWR);
3663259Sdg	if (fd == -1) {
36730996Simp		warn("can't open %s", APMDEV);
3683259Sdg		return 1;
3693259Sdg	}
37038809Simp	if (delta)
37138809Simp		apm_set_timer(fd, delta);
37221363Snate	if (sleep)
3733259Sdg		apm_suspend(fd);
37438809Simp	else if (standby)
37538809Simp		apm_standby(fd);
37638809Simp	else if (delta == 0) {
37721363Snate		struct apm_info info;
3783259Sdg
3793259Sdg		apm_getinfo(fd, &info);
38021363Snate		if (all_info)
38138809Simp			print_all_info(fd, &info);
38231127Sjdp		if (ac_status)
38331127Sjdp			printf("%d\n", info.ai_acline);
38421363Snate		if (batt_status)
3853259Sdg			printf("%d\n", info.ai_batt_stat);
38621363Snate		if (batt_life)
3873259Sdg			printf("%d\n", info.ai_batt_life);
38821363Snate		if (apm_status)
38914609Snate			printf("%d\n", info.ai_status);
39031127Sjdp		if (batt_time)
39131127Sjdp			printf("%d\n", info.ai_batt_time);
39221363Snate		if (display)
39321363Snate			apm_display(fd, display - 1);
3943259Sdg	}
3953259Sdg	close(fd);
3963259Sdg	return 0;
3973259Sdg}
398