apm.c revision 129784
1/*
2 * apm / zzz	APM BIOS utility for FreeBSD
3 *
4 * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
5 *
6 * This software may be used, modified, copied, distributed, and sold,
7 * in both source and binary form provided that the above copyright and
8 * these terms are retained. Under no circumstances is the author
9 * responsible for the proper functioning of this software, nor does
10 * the author assume any responsibility for damages incurred with its
11 * use.
12 *
13 * Sep., 1994	Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
14 */
15
16#include <sys/cdefs.h>
17__FBSDID("$FreeBSD: head/usr.sbin/apm/apm.c 129784 2004-05-27 19:23:27Z njl $");
18
19#include <sys/file.h>
20#include <sys/ioctl.h>
21#include <sys/types.h>
22#include <sys/sysctl.h>
23
24#include <machine/apm_bios.h>
25
26#include <err.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/file.h>
31#include <sys/ioctl.h>
32#include <time.h>
33#include <unistd.h>
34
35#define APMDEV	"/dev/apm"
36
37#define xh(a)	(((a) & 0xff00) >> 8)
38#define xl(a)	((a) & 0xff)
39#define APMERR(a) xh(a)
40
41int cmos_wall = 0;	/* True when wall time is in cmos clock, else UTC */
42
43static void
44usage(void)
45{
46	fprintf(stderr, "%s\n%s\n",
47		"usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
48		"[ -h enable ] [-r delta]",
49		"       zzz");
50	exit(1);
51}
52
53/*
54 * Return 1 for boolean true, and 0 for false, according to the
55 * interpretation of the string argument given.
56 */
57static int
58is_true(const char *boolean) {
59	char *endp;
60	long val;
61
62	val = strtoul(boolean, &endp, 0);
63	if (*endp == '\0')
64		return (val != 0 ? 1 : 0);
65	if (strcasecmp(boolean, "true") == 0 ||
66	    strcasecmp(boolean, "yes") == 0 ||
67	    strcasecmp(boolean, "enable") == 0)
68		return (1);
69	if (strcasecmp(boolean, "false") == 0 ||
70	    strcasecmp(boolean, "no") == 0 ||
71	    strcasecmp(boolean, "disable") == 0)
72		return (0);
73	/* Well, I have no idea what the user wants, so... */
74	warnx("invalid boolean argument \"%s\"", boolean);
75	usage();
76	/* NOTREACHED */
77
78	return (0);
79}
80
81static int
82int2bcd(int i)
83{
84	int retval = 0;
85	int base = 0;
86
87	if (i >= 10000)
88		return -1;
89
90	while (i) {
91		retval |= (i % 10) << base;
92		i /= 10;
93		base += 4;
94	}
95	return retval;
96}
97
98static int
99bcd2int(int bcd)
100{
101	int retval = 0;
102	int place = 1;
103
104	if (bcd > 0x9999)
105		return -1;
106
107	while (bcd) {
108		retval += (bcd & 0xf) * place;
109		bcd >>= 4;
110		place *= 10;
111	}
112	return retval;
113}
114
115static void
116apm_suspend(int fd)
117{
118	if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
119		err(1, "ioctl(APMIO_SUSPEND)");
120}
121
122static void
123apm_standby(int fd)
124{
125	if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
126		err(1, "ioctl(APMIO_STANDBY)");
127}
128
129static void
130apm_getinfo(int fd, apm_info_t aip)
131{
132	if (ioctl(fd, APMIO_GETINFO, aip) == -1)
133		err(1, "ioctl(APMIO_GETINFO)");
134}
135
136static void
137apm_enable(int fd, int enable) {
138
139	if (enable) {
140		if (ioctl(fd, APMIO_ENABLE) == -1)
141			err(1, "ioctl(APMIO_ENABLE)");
142	} else {
143		if (ioctl(fd, APMIO_DISABLE) == -1)
144			err(1, "ioctl(APMIO_DISABLE)");
145	}
146}
147
148static void
149print_all_info(int fd, apm_info_t aip, int bioscall_available)
150{
151	struct apm_bios_arg args;
152	int apmerr;
153	const char *batt_msg[] = { "high", "low", "critical", "charging" };
154	const char *line_msg[] = { "off-line", "on-line" };
155
156	printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
157	printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled");
158	printf("AC Line status: ");
159	if (aip->ai_acline >= 255)
160		printf("unknown");
161	else if (aip->ai_acline > 1)
162		printf("invalid value (0x%x)", aip->ai_acline);
163	else
164		printf("%s", line_msg[aip->ai_acline]);
165	printf("\n");
166	printf("Battery status: ");
167	if (aip->ai_batt_stat >= 255)
168		printf("unknown");
169	else if (aip->ai_batt_stat > 3)
170		printf("invalid value (0x%x)", aip->ai_batt_stat);
171	else
172		printf("%s", batt_msg[aip->ai_batt_stat]);
173	printf("\n");
174	printf("Remaining battery life: ");
175	if (aip->ai_batt_life >= 255)
176		printf("unknown\n");
177	else if (aip->ai_batt_life <= 100)
178		printf("%d%%\n", aip->ai_batt_life);
179	else
180		printf("invalid value (0x%x)\n", aip->ai_batt_life);
181	printf("Remaining battery time: ");
182	if (aip->ai_batt_time == -1)
183		printf("unknown\n");
184	else {
185		int t, h, m, s;
186
187		t = aip->ai_batt_time;
188		s = t % 60;
189		t /= 60;
190		m = t % 60;
191		t /= 60;
192		h = t;
193		printf("%2d:%02d:%02d\n", h, m, s);
194	}
195	if (aip->ai_infoversion >= 1) {
196		printf("Number of batteries: ");
197		if (aip->ai_batteries >= 255)
198			printf("unknown\n");
199		else {
200			u_int i;
201			struct apm_pwstatus aps;
202
203			printf("%d\n", aip->ai_batteries);
204			for (i = 0; i < aip->ai_batteries; ++i) {
205				bzero(&aps, sizeof(aps));
206				aps.ap_device = PMDV_BATT0 + i;
207				if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
208					continue;
209				printf("Battery %d:\n", i);
210				printf("\tBattery status: ");
211				if (aps.ap_batt_flag <= 255 &&
212				    (aps.ap_batt_flag & APM_BATT_NOT_PRESENT)) {
213					printf("not present\n");
214					continue;
215				}
216				if (aps.ap_batt_stat >= 255)
217					printf("unknown\n");
218				else if (aps.ap_batt_stat > 3)
219					printf("invalid value (0x%x)\n",
220					       aps.ap_batt_stat);
221				else
222					printf("%s\n",
223					       batt_msg[aps.ap_batt_stat]);
224				printf("\tRemaining battery life: ");
225				if (aps.ap_batt_life >= 255)
226					printf("unknown\n");
227				else if (aps.ap_batt_life <= 100)
228					printf("%d%%\n", aps.ap_batt_life);
229				else
230					printf("invalid value (0x%x)\n",
231					       aps.ap_batt_life);
232				printf("\tRemaining battery time: ");
233				if (aps.ap_batt_time == -1)
234					printf("unknown\n");
235				else {
236					int t, h, m, s;
237
238					t = aps.ap_batt_time;
239					s = t % 60;
240					t /= 60;
241					m = t % 60;
242					t /= 60;
243					h = t;
244					printf("%2d:%02d:%02d\n", h, m, s);
245				}
246			}
247		}
248	}
249
250	if (bioscall_available) {
251		/*
252		 * try to get the suspend timer
253		 */
254		bzero(&args, sizeof(args));
255		args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
256		args.ebx = PMDV_APMBIOS;
257		args.ecx = 0x0001;
258		if (ioctl(fd, APMIO_BIOS, &args)) {
259			printf("Resume timer: unknown\n");
260		} else {
261			apmerr = APMERR(args.eax);
262			if (apmerr == 0x0d || apmerr == 0x86)
263				printf("Resume timer: disabled\n");
264			else if (apmerr)
265				warnx(
266		"failed to get the resume timer: APM error0x%x", apmerr);
267			else {
268				/*
269				 * OK.  We have the time (all bcd).
270				 * CH - seconds
271				 * DH - hours
272				 * DL - minutes
273				 * xh(SI) - month (1-12)
274				 * xl(SI) - day of month (1-31)
275				 * DI - year
276				 */
277				struct tm tm;
278				char buf[1024];
279				time_t t;
280
281				tm.tm_sec = bcd2int(xh(args.ecx));
282				tm.tm_min = bcd2int(xl(args.edx));
283				tm.tm_hour = bcd2int(xh(args.edx));
284				tm.tm_mday = bcd2int(xl(args.esi));
285				tm.tm_mon = bcd2int(xh(args.esi)) - 1;
286				tm.tm_year = bcd2int(args.edi) - 1900;
287				if (cmos_wall)
288					t = mktime(&tm);
289				else
290					t = timegm(&tm);
291				if (t != -1) {
292					tm = *localtime(&t);
293					strftime(buf, sizeof(buf), "%c", &tm);
294					printf("Resume timer: %s\n", buf);
295				} else
296					printf("Resume timer: unknown\n");
297			}
298		}
299
300		/*
301		 * Get the ring indicator resume state
302		 */
303		bzero(&args, sizeof(args));
304		args.eax  = (APM_BIOS) << 8 | APM_RESUMEONRING;
305		args.ebx = PMDV_APMBIOS;
306		args.ecx = 0x0002;
307		if (ioctl(fd, APMIO_BIOS, &args) == 0) {
308			printf("Resume on ring indicator: %sabled\n",
309			    args.ecx ? "en" : "dis");
310		}
311	}
312
313	if (aip->ai_infoversion >= 1) {
314		printf("APM Capabilities:\n");
315		if (aip->ai_capabilities == 0xff00)
316			printf("\tunknown\n");
317		if (aip->ai_capabilities & 0x01)
318			printf("\tglobal standby state\n");
319		if (aip->ai_capabilities & 0x02)
320			printf("\tglobal suspend state\n");
321		if (aip->ai_capabilities & 0x04)
322			printf("\tresume timer from standby\n");
323		if (aip->ai_capabilities & 0x08)
324			printf("\tresume timer from suspend\n");
325		if (aip->ai_capabilities & 0x10)
326			printf("\tRI resume from standby\n");
327		if (aip->ai_capabilities & 0x20)
328			printf("\tRI resume from suspend\n");
329		if (aip->ai_capabilities & 0x40)
330			printf("\tPCMCIA RI resume from standby\n");
331		if (aip->ai_capabilities & 0x80)
332			printf("\tPCMCIA RI resume from suspend\n");
333	}
334
335}
336
337/*
338 * currently, it can turn off the display, but the display never comes
339 * back until the machine suspend/resumes :-).
340 */
341static void
342apm_display(int fd, int newstate)
343{
344	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
345		err(1, "ioctl(APMIO_DISPLAY)");
346}
347
348static void
349apm_haltcpu(int fd, int enable) {
350
351	if (enable) {
352		if (ioctl(fd, APMIO_HALTCPU, NULL) == -1)
353			err(1, "ioctl(APMIO_HALTCPU)");
354	} else {
355		if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1)
356			err(1, "ioctl(APMIO_NOTHALTCPU)");
357	}
358}
359
360static void
361apm_set_timer(int fd, int delta)
362{
363	time_t tmr;
364	struct tm *tm;
365	struct apm_bios_arg args;
366
367	tmr = time(NULL) + delta;
368	if (cmos_wall)
369		tm = localtime(&tmr);
370	else
371		tm = gmtime(&tmr);
372	bzero(&args, sizeof(args));
373	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
374	args.ebx = PMDV_APMBIOS;
375	if (delta > 0) {
376		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
377		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
378		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
379		args.edi = int2bcd(tm->tm_year + 1900);
380	} else {
381		args.ecx = 0x0000;
382	}
383	if (ioctl(fd, APMIO_BIOS, &args)) {
384		err(1,"set resume timer");
385	}
386}
387
388int
389main(int argc, char *argv[])
390{
391	int	c, fd;
392	int     dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
393	int     display = -1, batt_life = 0, ac_status = 0, standby = 0;
394	int	batt_time = 0, delta = 0, enable = -1, haltcpu = -1;
395	char	*cmdname;
396	int	bioscall_available = 0;
397	size_t	cmos_wall_len = sizeof(cmos_wall);
398
399	if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
400	    NULL, 0) == -1)
401		err(1, "sysctlbyname(machdep.wall_cmos_clock)");
402	if ((cmdname = strrchr(argv[0], '/')) != NULL)
403		cmdname++;
404	else
405		cmdname = argv[0];
406
407	if (strcmp(cmdname, "zzz") == 0) {
408		dosleep = 1;
409		all_info = 0;
410		goto finish_option;
411	}
412	while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) {
413		switch (c) {
414		case 'a':
415			ac_status = 1;
416			all_info = 0;
417			break;
418		case 'b':
419			batt_status = 1;
420			all_info = 0;
421			break;
422		case 'd':
423			display = is_true(optarg);
424			all_info = 0;
425			break;
426		case 'l':
427			batt_life = 1;
428			all_info = 0;
429			break;
430		case 'R':
431			delta = -1;
432			break;
433		case 'r':
434			delta = atoi(optarg);
435			break;
436		case 's':
437			apm_status = 1;
438			all_info = 0;
439			break;
440		case 'e':
441			enable = is_true(optarg);
442			all_info = 0;
443			break;
444		case 'h':
445			haltcpu = is_true(optarg);
446			all_info = 0;
447			break;
448		case 't':
449			batt_time = 1;
450			all_info = 0;
451			break;
452		case 'z':
453			dosleep = 1;
454			all_info = 0;
455			break;
456		case 'Z':
457			standby = 1;
458			all_info = 0;
459			break;
460		case '?':
461		default:
462			usage();
463		}
464		argc -= optind;
465		argv += optind;
466	}
467finish_option:
468	if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep
469	    || standby) {
470		fd = open(APMDEV, O_RDWR);
471		bioscall_available = 1;
472	} else if ((fd = open(APMDEV, O_RDWR)) >= 0)
473		bioscall_available = 1;
474	else
475		fd = open(APMDEV, O_RDONLY);
476	if (fd == -1)
477		err(1, "can't open %s", APMDEV);
478	if (enable != -1)
479		apm_enable(fd, enable);
480	if (haltcpu != -1)
481		apm_haltcpu(fd, haltcpu);
482	if (delta)
483		apm_set_timer(fd, delta);
484	if (dosleep)
485		apm_suspend(fd);
486	else if (standby)
487		apm_standby(fd);
488	else if (delta == 0) {
489		struct apm_info info;
490
491		apm_getinfo(fd, &info);
492		if (all_info)
493			print_all_info(fd, &info, bioscall_available);
494		if (ac_status)
495			printf("%d\n", info.ai_acline);
496		if (batt_status)
497			printf("%d\n", info.ai_batt_stat);
498		if (batt_life)
499			printf("%d\n", info.ai_batt_life);
500		if (apm_status)
501			printf("%d\n", info.ai_status);
502		if (batt_time)
503			printf("%d\n", info.ai_batt_time);
504		if (display != -1)
505			apm_display(fd, display);
506	}
507	close(fd);
508	exit(0);
509}
510