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