apm.c revision 49286
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	"$Id: apm.c,v 1.16 1999/07/20 15:37:28 green Exp $";
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 1|0] [-r delta]",
50		"       zzz");
51	exit(1);
52}
53
54int
55int2bcd(int i)
56{
57	int retval = 0;
58	int base = 0;
59
60	if (i >= 10000)
61		return -1;
62
63	while (i) {
64		retval |= (i % 10) << base;
65		i /= 10;
66		base += 4;
67	}
68	return retval;
69}
70
71int
72bcd2int(int bcd)
73{
74	int retval = 0;
75	int place = 1;
76
77	if (bcd > 0x9999)
78		return -1;
79
80	while (bcd) {
81		retval += (bcd & 0xf) * place;
82		bcd >>= 4;
83		place *= 10;
84	}
85	return retval;
86}
87
88void
89apm_suspend(int fd)
90{
91	if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
92		err(1, NULL);
93}
94
95void
96apm_standby(int fd)
97{
98	if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
99		err(1, NULL);
100}
101
102void
103apm_getinfo(int fd, apm_info_t aip)
104{
105	if (ioctl(fd, APMIO_GETINFO, aip) == -1)
106		err(1, NULL);
107}
108
109void
110print_all_info(int fd, apm_info_t aip)
111{
112	struct apm_bios_arg args;
113	int apmerr;
114
115	printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
116	printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
117	printf("AC Line status: ");
118	if (aip->ai_acline == 255)
119		printf("unknown");
120	else if (aip->ai_acline > 1)
121		printf("invalid value (0x%x)", aip->ai_acline);
122	else {
123		char messages[][10] = {"off-line", "on-line"};
124		printf("%s", messages[aip->ai_acline]);
125	}
126	printf("\n");
127	printf("Battery status: ");
128	if (aip->ai_batt_stat == 255)
129		printf("unknown");
130	else if (aip->ai_batt_stat > 3)
131			printf("invalid value (0x%x)", aip->ai_batt_stat);
132	else {
133		char messages[][10] = {"high", "low", "critical", "charging"};
134		printf("%s", messages[aip->ai_batt_stat]);
135	}
136	printf("\n");
137	printf("Remaining battery life: ");
138	if (aip->ai_batt_life == 255)
139		printf("unknown");
140	else if (aip->ai_batt_life <= 100)
141		printf("%d%%", aip->ai_batt_life);
142	else
143		printf("invalid value (0x%x)", aip->ai_batt_life);
144	printf("\n");
145	printf("Remaining battery time: ");
146	if (aip->ai_batt_time == -1)
147		printf("unknown");
148	else {
149		int t, h, m, s;
150
151		t = aip->ai_batt_time;
152		s = t % 60;
153		t /= 60;
154		m = t % 60;
155		t /= 60;
156		h = t;
157		printf("%2d:%02d:%02d", h, m, s);
158	}
159	printf("\n");
160	if (aip->ai_infoversion >= 1) {
161		printf("Number of batteries: ");
162		if (aip->ai_batteries == (u_int) -1)
163			printf("unknown\n");
164		else
165			printf("%d\n", aip->ai_batteries);
166	}
167
168	/*
169	 * try to get the suspend timer
170	 */
171#if 0
172	bzero(&args, sizeof(args));
173	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
174	args.ebx = PMDV_APMBIOS;
175	args.ecx = 0x0001;
176	if (ioctl(fd, APMIO_BIOS, &args)) {
177		err(1,"Get resume timer");
178	} else {
179		apmerr = APMERR(args.eax);
180		 if (apmerr == 0x0d || apmerr == 0x86)
181			printf("Resume timer: disabled\n");
182		else if (apmerr)
183			fprintf(stderr,
184			    "Failed to get the resume timer: APM error0x%x\n",
185			    apmerr);
186		else {
187			/*
188			 * OK.  We have the time (all bcd).
189			 * CH - seconds
190			 * DH - hours
191			 * DL - minutes
192			 * xh(SI) - month (1-12)
193			 * xl(SI) - day of month (1-31)
194			 * DI - year
195			 */
196			struct tm tm;
197			char buf[1024];
198			time_t t;
199
200			tm.tm_sec = bcd2int(xh(args.ecx));
201			tm.tm_min = bcd2int(xl(args.edx));
202			tm.tm_hour = bcd2int(xh(args.edx));
203			tm.tm_mday = bcd2int(xl(args.esi));
204			tm.tm_mon = bcd2int(xh(args.esi)) - 1;
205			tm.tm_year = bcd2int(args.edi) - 1900;
206			if (cmos_wall)
207				t = mktime(&tm);
208			else
209				t = timegm(&tm);
210			tm = *localtime(&t);
211			strftime(buf, sizeof(buf), "%c", &tm);
212			printf("Resume timer: %s\n", buf);
213		}
214	}
215
216	/*
217	 * Get the ring indicator resume state
218	 */
219	bzero(&args, sizeof(args));
220	args.eax  = (APM_BIOS) << 8 | APM_RESUMEONRING;
221	args.ebx = PMDV_APMBIOS;
222	args.ecx = 0x0002;
223	if (ioctl(fd, APMIO_BIOS, &args) == 0) {
224		printf("Resume on ring indicator: %sabled\n",
225			args.ecx ? "en" : "dis");
226	}
227#endif
228	if (aip->ai_infoversion >= 1) {
229		printf("APM Capacities:\n", aip->ai_capabilities);
230		if (aip->ai_capabilities == 0xff00)
231			printf("\tunknown\n");
232		if (aip->ai_capabilities & 0x01)
233			printf("\tglobal standby state\n");
234		if (aip->ai_capabilities & 0x02)
235			printf("\tglobal suspend state\n");
236		if (aip->ai_capabilities & 0x04)
237			printf("\tresume timer from standby\n");
238		if (aip->ai_capabilities & 0x08)
239			printf("\tresume timer from suspend\n");
240		if (aip->ai_capabilities & 0x10)
241			printf("\tRI resume from standby\n");
242		if (aip->ai_capabilities & 0x20)
243			printf("\tRI resume from suspend\n");
244		if (aip->ai_capabilities & 0x40)
245			printf("\tPCMCIA RI resume from standby\n");
246		if (aip->ai_capabilities & 0x80)
247			printf("\tPCMCIA RI resume from suspend\n");
248	}
249
250}
251
252/*
253 * currently, it can turn off the display, but the display never comes
254 * back until the machine suspend/resumes :-).
255 */
256void
257apm_display(int fd, int newstate)
258{
259	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
260		err(1, NULL);
261}
262
263
264void
265apm_set_timer(int fd, int delta)
266{
267	time_t tmr;
268	struct tm *tm;
269	struct apm_bios_arg args;
270
271	tmr = time(NULL) + delta;
272	if (cmos_wall)
273		tm = localtime(&tmr);
274	else
275		tm = gmtime(&tmr);
276	bzero(&args, sizeof(args));
277	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
278	args.ebx = PMDV_APMBIOS;
279	if (delta > 0) {
280		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
281		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
282		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
283		args.edi = int2bcd(tm->tm_year + 1900);
284	} else {
285		args.ecx = 0x0000;
286	}
287#if 0
288	if (ioctl(fd, APMIO_BIOS, &args)) {
289		err(1,"Set resume timer");
290	}
291#else
292	errx(1,"set resume timer not supported");
293#endif
294}
295
296int
297main(int argc, char *argv[])
298{
299	int	c, fd;
300	int     sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
301	int     display = 0, batt_life = 0, ac_status = 0, standby = 0;
302	int	batt_time = 0, delta = 0;
303	char	*cmdname;
304	size_t	cmos_wall_len = sizeof(cmos_wall);
305
306	if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
307	    NULL, 0) == -1)
308		err(1, "sysctlbyname(machdep.wall_cmos_clock)");
309	if ((cmdname = strrchr(argv[0], '/')) != NULL)
310		cmdname++;
311	else
312		cmdname = argv[0];
313
314	if (strcmp(cmdname, "zzz") == 0) {
315		sleep = 1;
316		all_info = 0;
317		goto finish_option;
318	}
319	while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) {
320		switch (c) {
321		case 'a':
322			ac_status = 1;
323			all_info = 0;
324			break;
325		case 'b':
326			batt_status = 1;
327			all_info = 0;
328			break;
329		case 'd':
330			display = *optarg - '0';
331			if (display < 0 || display > 1) {
332				warnx("argument of option '-%c' is invalid", c);
333				usage();
334			}
335			display++;
336			all_info = 0;
337			break;
338		case 'l':
339			batt_life = 1;
340			all_info = 0;
341			break;
342		case 'R':
343			delta = -1;
344			break;
345		case 'r':
346			delta = atoi(optarg);
347			break;
348		case 's':
349			apm_status = 1;
350			all_info = 0;
351			break;
352		case 't':
353			batt_time = 1;
354			all_info = 0;
355			break;
356		case 'z':
357			sleep = 1;
358			all_info = 0;
359			break;
360		case 'Z':
361			standby = 1;
362			all_info = 0;
363			break;
364		case '?':
365		default:
366			usage();
367		}
368		argc -= optind;
369		argv += optind;
370	}
371finish_option:
372	fd = open(APMDEV, O_RDWR);
373	if (fd == -1) {
374		warn("can't open %s", APMDEV);
375		return 1;
376	}
377	if (delta)
378		apm_set_timer(fd, delta);
379	if (sleep)
380		apm_suspend(fd);
381	else if (standby)
382		apm_standby(fd);
383	else if (delta == 0) {
384		struct apm_info info;
385
386		apm_getinfo(fd, &info);
387		if (all_info)
388			print_all_info(fd, &info);
389		if (ac_status)
390			printf("%d\n", info.ai_acline);
391		if (batt_status)
392			printf("%d\n", info.ai_batt_stat);
393		if (batt_life)
394			printf("%d\n", info.ai_batt_life);
395		if (apm_status)
396			printf("%d\n", info.ai_status);
397		if (batt_time)
398			printf("%d\n", info.ai_batt_time);
399		if (display)
400			apm_display(fd, display - 1);
401	}
402	close(fd);
403	return 0;
404}
405