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