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