13259Sdg/*
2200840Sru * 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
16105157Scharnier#include <sys/cdefs.h>
17105157Scharnier__FBSDID("$FreeBSD$");
1829030Scharnier
1948938Sgreen#include <sys/file.h>
2048938Sgreen#include <sys/ioctl.h>
2148938Sgreen#include <sys/types.h>
2248938Sgreen#include <sys/sysctl.h>
2348938Sgreen
2448938Sgreen#include <machine/apm_bios.h>
2548938Sgreen
2629030Scharnier#include <err.h>
273259Sdg#include <stdio.h>
2821363Snate#include <stdlib.h>
293259Sdg#include <string.h>
3048938Sgreen#include <time.h>
3121363Snate#include <unistd.h>
323259Sdg
3314674Snate#define APMDEV	"/dev/apm"
343259Sdg
35131368Simp#define APM_UNKNOWN	255
36131368Simp
3738809Simp#define xh(a)	(((a) & 0xff00) >> 8)
3838809Simp#define xl(a)	((a) & 0xff)
3938809Simp#define APMERR(a) xh(a)
4038809Simp
4138809Simpint cmos_wall = 0;	/* True when wall time is in cmos clock, else UTC */
4238809Simp
43129784Snjlstatic void
44129784Snjlusage(void)
453259Sdg{
46200840Sru	fprintf(stderr,
4756399Sgreen		"usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
48200840Sru		"[ -h enable ] [-r delta]\n");
4921363Snate	exit(1);
5021363Snate}
5121363Snate
5256399Sgreen/*
5356399Sgreen * Return 1 for boolean true, and 0 for false, according to the
5456399Sgreen * interpretation of the string argument given.
5556399Sgreen */
56129784Snjlstatic int
57130476Smuxis_true(const char *boolean)
58130476Smux{
5956399Sgreen	char *endp;
6056399Sgreen	long val;
6156399Sgreen
6256399Sgreen	val = strtoul(boolean, &endp, 0);
6356399Sgreen	if (*endp == '\0')
6456399Sgreen		return (val != 0 ? 1 : 0);
6556399Sgreen	if (strcasecmp(boolean, "true") == 0 ||
6656399Sgreen	    strcasecmp(boolean, "yes") == 0 ||
6756399Sgreen	    strcasecmp(boolean, "enable") == 0)
6856399Sgreen		return (1);
6956399Sgreen	if (strcasecmp(boolean, "false") == 0 ||
7056399Sgreen	    strcasecmp(boolean, "no") == 0 ||
7156399Sgreen	    strcasecmp(boolean, "disable") == 0)
7256399Sgreen		return (0);
7356399Sgreen	/* Well, I have no idea what the user wants, so... */
7456399Sgreen	warnx("invalid boolean argument \"%s\"", boolean);
7556399Sgreen	usage();
7656399Sgreen	/* NOTREACHED */
77129784Snjl
78129784Snjl	return (0);
7956399Sgreen}
8056399Sgreen
81129784Snjlstatic int
8238809Simpint2bcd(int i)
8338809Simp{
8438809Simp	int retval = 0;
8538809Simp	int base = 0;
8638809Simp
8738809Simp	if (i >= 10000)
8838809Simp		return -1;
8938809Simp
9038809Simp	while (i) {
9138809Simp		retval |= (i % 10) << base;
9238809Simp		i /= 10;
9338809Simp		base += 4;
9438809Simp	}
9538809Simp	return retval;
9638809Simp}
9738809Simp
98129784Snjlstatic int
9938809Simpbcd2int(int bcd)
10038809Simp{
10138809Simp	int retval = 0;
10248939Sgreen	int place = 1;
10338809Simp
10438809Simp	if (bcd > 0x9999)
10538809Simp		return -1;
10638809Simp
10738809Simp	while (bcd) {
10848939Sgreen		retval += (bcd & 0xf) * place;
10948939Sgreen		bcd >>= 4;
11048939Sgreen		place *= 10;
11138809Simp	}
11238809Simp	return retval;
11338809Simp}
11438809Simp
115129784Snjlstatic void
11621363Snateapm_suspend(int fd)
11721363Snate{
11829030Scharnier	if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
11956399Sgreen		err(1, "ioctl(APMIO_SUSPEND)");
1203259Sdg}
1213259Sdg
122129784Snjlstatic void
12338809Simpapm_standby(int fd)
12438809Simp{
12538809Simp	if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
12656399Sgreen		err(1, "ioctl(APMIO_STANDBY)");
12738809Simp}
12838809Simp
129129784Snjlstatic void
13021363Snateapm_getinfo(int fd, apm_info_t aip)
1313259Sdg{
13229030Scharnier	if (ioctl(fd, APMIO_GETINFO, aip) == -1)
13356399Sgreen		err(1, "ioctl(APMIO_GETINFO)");
1343259Sdg}
1353259Sdg
136129784Snjlstatic void
137130476Smuxapm_enable(int fd, int enable)
138130476Smux{
13956399Sgreen	if (enable) {
14056399Sgreen		if (ioctl(fd, APMIO_ENABLE) == -1)
14156399Sgreen			err(1, "ioctl(APMIO_ENABLE)");
14256399Sgreen	} else {
14356399Sgreen		if (ioctl(fd, APMIO_DISABLE) == -1)
14456399Sgreen			err(1, "ioctl(APMIO_DISABLE)");
14556399Sgreen	}
14656123Sgreen}
14756123Sgreen
148130476Smuxstatic void
149130476Smuxprint_batt_time(int batt_time)
150130476Smux{
151130476Smux	printf("Remaining battery time: ");
152130476Smux	if (batt_time == -1)
153130476Smux		printf("unknown\n");
154130476Smux	else {
155130476Smux		int h, m, s;
156130476Smux
157130476Smux		h = batt_time;
158130476Smux		s = h % 60;
159130476Smux		h /= 60;
160130476Smux		m = h % 60;
161130476Smux		h /= 60;
162130476Smux		printf("%2d:%02d:%02d\n", h, m, s);
163130476Smux	}
164130476Smux}
165130476Smux
166130476Smuxstatic void
167130476Smuxprint_batt_life(u_int batt_life)
168130476Smux{
169130476Smux	printf("Remaining battery life: ");
170131368Simp	if (batt_life == APM_UNKNOWN)
171130476Smux		printf("unknown\n");
172130476Smux	else if (batt_life <= 100)
173130476Smux		printf("%d%%\n", batt_life);
174130476Smux	else
175130476Smux		printf("invalid value (0x%x)\n", batt_life);
176130476Smux}
177130476Smux
178130476Smuxstatic void
179130476Smuxprint_batt_stat(u_int batt_stat)
180130476Smux{
181130476Smux	const char *batt_msg[] = { "high", "low", "critical", "charging" };
182130476Smux
183130476Smux	printf("Battery Status: ");
184131368Simp	if (batt_stat == APM_UNKNOWN)
185130476Smux		printf("unknown\n");
186130476Smux	else if (batt_stat > 3)
187130476Smux		printf("invalid value (0x%x)\n", batt_stat);
188130476Smux	else
189130476Smux		printf("%s\n", batt_msg[batt_stat]);
190130476Smux}
191130476Smux
192129784Snjlstatic void
19364252Sumeprint_all_info(int fd, apm_info_t aip, int bioscall_available)
1943259Sdg{
19538809Simp	struct apm_bios_arg args;
19638809Simp	int apmerr;
197146812Swill	const char *line_msg[] = { "off-line", "on-line" , "backup power"};
19838809Simp
1993259Sdg	printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
200129784Snjl	printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled");
2013259Sdg	printf("AC Line status: ");
202131368Simp	if (aip->ai_acline == APM_UNKNOWN)
203130476Smux		printf("unknown\n");
204146812Swill	else if (aip->ai_acline > 2)
205130476Smux		printf("invalid value (0x%x)\n", aip->ai_acline);
206129784Snjl	else
207130476Smux		printf("%s\n", line_msg[aip->ai_acline]);
20831127Sjdp
209130476Smux	print_batt_stat(aip->ai_batt_stat);
210130476Smux	print_batt_life(aip->ai_batt_life);
211130476Smux	print_batt_time(aip->ai_batt_time);
212130476Smux
21338809Simp	if (aip->ai_infoversion >= 1) {
21438809Simp		printf("Number of batteries: ");
215131368Simp		if (aip->ai_batteries == ~0U)
21638809Simp			printf("unknown\n");
21764615Sume		else {
218129784Snjl			u_int i;
21964615Sume			struct apm_pwstatus aps;
22064615Sume
22138809Simp			printf("%d\n", aip->ai_batteries);
22264615Sume			for (i = 0; i < aip->ai_batteries; ++i) {
22364615Sume				bzero(&aps, sizeof(aps));
22464615Sume				aps.ap_device = PMDV_BATT0 + i;
22564615Sume				if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
22664615Sume					continue;
22764615Sume				printf("Battery %d:\n", i);
228131368Simp				if (aps.ap_batt_flag & APM_BATT_NOT_PRESENT) {
22964615Sume					printf("not present\n");
23064615Sume					continue;
23164615Sume				}
232130604Smux				printf("\t");
233130476Smux				print_batt_stat(aps.ap_batt_stat);
234130604Smux				printf("\t");
235130476Smux				print_batt_life(aps.ap_batt_life);
236130604Smux				printf("\t");
237130476Smux				print_batt_time(aps.ap_batt_time);
23864615Sume			}
23964615Sume		}
24038809Simp	}
24138809Simp
24264252Sume	if (bioscall_available) {
24364252Sume		/*
24464252Sume		 * try to get the suspend timer
24564252Sume		 */
24664252Sume		bzero(&args, sizeof(args));
24764252Sume		args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
24864252Sume		args.ebx = PMDV_APMBIOS;
24964252Sume		args.ecx = 0x0001;
25064252Sume		if (ioctl(fd, APMIO_BIOS, &args)) {
25164252Sume			printf("Resume timer: unknown\n");
25264252Sume		} else {
25364252Sume			apmerr = APMERR(args.eax);
25464252Sume			if (apmerr == 0x0d || apmerr == 0x86)
25564252Sume				printf("Resume timer: disabled\n");
25664252Sume			else if (apmerr)
257105157Scharnier				warnx(
258105157Scharnier		"failed to get the resume timer: APM error0x%x", apmerr);
25964252Sume			else {
26064252Sume				/*
26164252Sume				 * OK.  We have the time (all bcd).
26264252Sume				 * CH - seconds
26364252Sume				 * DH - hours
26464252Sume				 * DL - minutes
26564252Sume				 * xh(SI) - month (1-12)
26664252Sume				 * xl(SI) - day of month (1-31)
26764252Sume				 * DI - year
26864252Sume				 */
26964252Sume				struct tm tm;
27064252Sume				char buf[1024];
27164252Sume				time_t t;
27238809Simp
27364252Sume				tm.tm_sec = bcd2int(xh(args.ecx));
27464252Sume				tm.tm_min = bcd2int(xl(args.edx));
27564252Sume				tm.tm_hour = bcd2int(xh(args.edx));
27664252Sume				tm.tm_mday = bcd2int(xl(args.esi));
27764252Sume				tm.tm_mon = bcd2int(xh(args.esi)) - 1;
27864252Sume				tm.tm_year = bcd2int(args.edi) - 1900;
27964252Sume				if (cmos_wall)
28064252Sume					t = mktime(&tm);
28164252Sume				else
28264252Sume					t = timegm(&tm);
283117517Srwatson				if (t != -1) {
284117517Srwatson					tm = *localtime(&t);
285117517Srwatson					strftime(buf, sizeof(buf), "%c", &tm);
286117517Srwatson					printf("Resume timer: %s\n", buf);
287117517Srwatson				} else
288117517Srwatson					printf("Resume timer: unknown\n");
28964252Sume			}
29038809Simp		}
29164252Sume
29264252Sume		/*
29364252Sume		 * Get the ring indicator resume state
29464252Sume		 */
29564252Sume		bzero(&args, sizeof(args));
29664252Sume		args.eax  = (APM_BIOS) << 8 | APM_RESUMEONRING;
29764252Sume		args.ebx = PMDV_APMBIOS;
29864252Sume		args.ecx = 0x0002;
29964252Sume		if (ioctl(fd, APMIO_BIOS, &args) == 0) {
30064252Sume			printf("Resume on ring indicator: %sabled\n",
301102403Simp			    args.ecx ? "en" : "dis");
30264252Sume		}
30338809Simp	}
30438809Simp
30538809Simp	if (aip->ai_infoversion >= 1) {
306131368Simp		if (aip->ai_capabilities == 0xff00)
307131368Simp		    return;
308102403Simp		printf("APM Capabilities:\n");
30938809Simp		if (aip->ai_capabilities & 0x01)
31038809Simp			printf("\tglobal standby state\n");
31138809Simp		if (aip->ai_capabilities & 0x02)
31238809Simp			printf("\tglobal suspend state\n");
31338809Simp		if (aip->ai_capabilities & 0x04)
31438809Simp			printf("\tresume timer from standby\n");
31538809Simp		if (aip->ai_capabilities & 0x08)
31638809Simp			printf("\tresume timer from suspend\n");
31738809Simp		if (aip->ai_capabilities & 0x10)
31838809Simp			printf("\tRI resume from standby\n");
31938809Simp		if (aip->ai_capabilities & 0x20)
32038809Simp			printf("\tRI resume from suspend\n");
32138809Simp		if (aip->ai_capabilities & 0x40)
32238809Simp			printf("\tPCMCIA RI resume from standby\n");
32338809Simp		if (aip->ai_capabilities & 0x80)
32438809Simp			printf("\tPCMCIA RI resume from suspend\n");
32538809Simp	}
32638809Simp
3273259Sdg}
3283259Sdg
32921363Snate/*
33021363Snate * currently, it can turn off the display, but the display never comes
33121363Snate * back until the machine suspend/resumes :-).
33221363Snate */
333129784Snjlstatic void
33421363Snateapm_display(int fd, int newstate)
3353259Sdg{
33629030Scharnier	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
33756399Sgreen		err(1, "ioctl(APMIO_DISPLAY)");
33821363Snate}
3398857Srgrimes
340129784Snjlstatic void
341130476Smuxapm_haltcpu(int fd, int enable)
342130476Smux{
34356399Sgreen	if (enable) {
34456399Sgreen		if (ioctl(fd, APMIO_HALTCPU, NULL) == -1)
34556399Sgreen			err(1, "ioctl(APMIO_HALTCPU)");
34656399Sgreen	} else {
34756399Sgreen		if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1)
34856399Sgreen			err(1, "ioctl(APMIO_NOTHALTCPU)");
34956399Sgreen	}
35056399Sgreen}
35156399Sgreen
352129784Snjlstatic void
35338809Simpapm_set_timer(int fd, int delta)
35438809Simp{
35538809Simp	time_t tmr;
35638809Simp	struct tm *tm;
35738809Simp	struct apm_bios_arg args;
35838809Simp
35938809Simp	tmr = time(NULL) + delta;
36038809Simp	if (cmos_wall)
36138809Simp		tm = localtime(&tmr);
36238809Simp	else
36338809Simp		tm = gmtime(&tmr);
36438809Simp	bzero(&args, sizeof(args));
36538809Simp	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
36638809Simp	args.ebx = PMDV_APMBIOS;
36738809Simp	if (delta > 0) {
36838809Simp		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
36938809Simp		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
37038809Simp		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
37138809Simp		args.edi = int2bcd(tm->tm_year + 1900);
37238809Simp	} else {
37338809Simp		args.ecx = 0x0000;
37438809Simp	}
37538809Simp	if (ioctl(fd, APMIO_BIOS, &args)) {
376105157Scharnier		err(1,"set resume timer");
37738809Simp	}
37838809Simp}
37938809Simp
38021363Snateint
38121363Snatemain(int argc, char *argv[])
38221363Snate{
38321363Snate	int	c, fd;
384129784Snjl	int     dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
38556399Sgreen	int     display = -1, batt_life = 0, ac_status = 0, standby = 0;
38656399Sgreen	int	batt_time = 0, delta = 0, enable = -1, haltcpu = -1;
38764252Sume	int	bioscall_available = 0;
38848938Sgreen	size_t	cmos_wall_len = sizeof(cmos_wall);
38921363Snate
39048938Sgreen	if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
39148938Sgreen	    NULL, 0) == -1)
39248938Sgreen		err(1, "sysctlbyname(machdep.wall_cmos_clock)");
3933259Sdg
39456399Sgreen	while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) {
39521363Snate		switch (c) {
39621363Snate		case 'a':
39721363Snate			ac_status = 1;
39821363Snate			all_info = 0;
39921363Snate			break;
40021363Snate		case 'b':
40121363Snate			batt_status = 1;
40221363Snate			all_info = 0;
40321363Snate			break;
40421363Snate		case 'd':
40556399Sgreen			display = is_true(optarg);
40621363Snate			all_info = 0;
40721363Snate			break;
40821363Snate		case 'l':
40921363Snate			batt_life = 1;
41021363Snate			all_info = 0;
41121363Snate			break;
41238809Simp		case 'R':
41338809Simp			delta = -1;
41438809Simp			break;
41538809Simp		case 'r':
41638809Simp			delta = atoi(optarg);
41738809Simp			break;
41821363Snate		case 's':
41921363Snate			apm_status = 1;
42021363Snate			all_info = 0;
42121363Snate			break;
42256123Sgreen		case 'e':
42356399Sgreen			enable = is_true(optarg);
424100714Ssobomax			all_info = 0;
42556123Sgreen			break;
42656399Sgreen		case 'h':
42756399Sgreen			haltcpu = is_true(optarg);
428100714Ssobomax			all_info = 0;
42956399Sgreen			break;
43031127Sjdp		case 't':
43131127Sjdp			batt_time = 1;
43231127Sjdp			all_info = 0;
43331127Sjdp			break;
43421363Snate		case 'z':
435129784Snjl			dosleep = 1;
43621363Snate			all_info = 0;
43721363Snate			break;
43838809Simp		case 'Z':
43938809Simp			standby = 1;
44038809Simp			all_info = 0;
44138809Simp			break;
44221363Snate		case '?':
44321363Snate		default:
44421363Snate			usage();
4453259Sdg		}
44621363Snate		argc -= optind;
44721363Snate		argv += optind;
4483259Sdg	}
449129784Snjl	if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep
450129784Snjl	    || standby) {
45163495Simp		fd = open(APMDEV, O_RDWR);
45264252Sume		bioscall_available = 1;
45364252Sume	} else if ((fd = open(APMDEV, O_RDWR)) >= 0)
45464252Sume		bioscall_available = 1;
45563495Simp	else
45663495Simp		fd = open(APMDEV, O_RDONLY);
45756399Sgreen	if (fd == -1)
45856399Sgreen		err(1, "can't open %s", APMDEV);
45956399Sgreen	if (enable != -1)
46056399Sgreen		apm_enable(fd, enable);
46156399Sgreen	if (haltcpu != -1)
46256399Sgreen		apm_haltcpu(fd, haltcpu);
46338809Simp	if (delta)
46438809Simp		apm_set_timer(fd, delta);
465129784Snjl	if (dosleep)
4663259Sdg		apm_suspend(fd);
46738809Simp	else if (standby)
46838809Simp		apm_standby(fd);
46938809Simp	else if (delta == 0) {
47021363Snate		struct apm_info info;
4713259Sdg
4723259Sdg		apm_getinfo(fd, &info);
47321363Snate		if (all_info)
47464252Sume			print_all_info(fd, &info, bioscall_available);
47531127Sjdp		if (ac_status)
47631127Sjdp			printf("%d\n", info.ai_acline);
47721363Snate		if (batt_status)
4783259Sdg			printf("%d\n", info.ai_batt_stat);
47921363Snate		if (batt_life)
4803259Sdg			printf("%d\n", info.ai_batt_life);
48121363Snate		if (apm_status)
48214609Snate			printf("%d\n", info.ai_status);
48331127Sjdp		if (batt_time)
48431127Sjdp			printf("%d\n", info.ai_batt_time);
48556399Sgreen		if (display != -1)
48656399Sgreen			apm_display(fd, display);
4873259Sdg	}
4883259Sdg	close(fd);
48956399Sgreen	exit(0);
4903259Sdg}
491