apm.c revision 50479
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 50479 1999-08-28 01:35:59Z peter $";
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	bzero(&args, sizeof(args));
172	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
173	args.ebx = PMDV_APMBIOS;
174	args.ecx = 0x0001;
175	if (ioctl(fd, APMIO_BIOS, &args)) {
176		printf("Resume timer: unknown\n");
177	} else {
178		apmerr = APMERR(args.eax);
179		 if (apmerr == 0x0d || apmerr == 0x86)
180			printf("Resume timer: disabled\n");
181		else if (apmerr)
182			fprintf(stderr,
183			    "Failed to get the resume timer: APM error0x%x\n",
184			    apmerr);
185		else {
186			/*
187			 * OK.  We have the time (all bcd).
188			 * CH - seconds
189			 * DH - hours
190			 * DL - minutes
191			 * xh(SI) - month (1-12)
192			 * xl(SI) - day of month (1-31)
193			 * DI - year
194			 */
195			struct tm tm;
196			char buf[1024];
197			time_t t;
198
199			tm.tm_sec = bcd2int(xh(args.ecx));
200			tm.tm_min = bcd2int(xl(args.edx));
201			tm.tm_hour = bcd2int(xh(args.edx));
202			tm.tm_mday = bcd2int(xl(args.esi));
203			tm.tm_mon = bcd2int(xh(args.esi)) - 1;
204			tm.tm_year = bcd2int(args.edi) - 1900;
205			if (cmos_wall)
206				t = mktime(&tm);
207			else
208				t = timegm(&tm);
209			tm = *localtime(&t);
210			strftime(buf, sizeof(buf), "%c", &tm);
211			printf("Resume timer: %s\n", buf);
212		}
213	}
214
215	/*
216	 * Get the ring indicator resume state
217	 */
218	bzero(&args, sizeof(args));
219	args.eax  = (APM_BIOS) << 8 | APM_RESUMEONRING;
220	args.ebx = PMDV_APMBIOS;
221	args.ecx = 0x0002;
222	if (ioctl(fd, APMIO_BIOS, &args) == 0) {
223		printf("Resume on ring indicator: %sabled\n",
224			args.ecx ? "en" : "dis");
225	}
226	if (aip->ai_infoversion >= 1) {
227		printf("APM Capacities:\n", aip->ai_capabilities);
228		if (aip->ai_capabilities == 0xff00)
229			printf("\tunknown\n");
230		if (aip->ai_capabilities & 0x01)
231			printf("\tglobal standby state\n");
232		if (aip->ai_capabilities & 0x02)
233			printf("\tglobal suspend state\n");
234		if (aip->ai_capabilities & 0x04)
235			printf("\tresume timer from standby\n");
236		if (aip->ai_capabilities & 0x08)
237			printf("\tresume timer from suspend\n");
238		if (aip->ai_capabilities & 0x10)
239			printf("\tRI resume from standby\n");
240		if (aip->ai_capabilities & 0x20)
241			printf("\tRI resume from suspend\n");
242		if (aip->ai_capabilities & 0x40)
243			printf("\tPCMCIA RI resume from standby\n");
244		if (aip->ai_capabilities & 0x80)
245			printf("\tPCMCIA RI resume from suspend\n");
246	}
247
248}
249
250/*
251 * currently, it can turn off the display, but the display never comes
252 * back until the machine suspend/resumes :-).
253 */
254void
255apm_display(int fd, int newstate)
256{
257	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
258		err(1, NULL);
259}
260
261
262void
263apm_set_timer(int fd, int delta)
264{
265	time_t tmr;
266	struct tm *tm;
267	struct apm_bios_arg args;
268
269	tmr = time(NULL) + delta;
270	if (cmos_wall)
271		tm = localtime(&tmr);
272	else
273		tm = gmtime(&tmr);
274	bzero(&args, sizeof(args));
275	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
276	args.ebx = PMDV_APMBIOS;
277	if (delta > 0) {
278		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
279		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
280		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
281		args.edi = int2bcd(tm->tm_year + 1900);
282	} else {
283		args.ecx = 0x0000;
284	}
285	if (ioctl(fd, APMIO_BIOS, &args)) {
286		err(1,"Set resume timer");
287	}
288}
289
290int
291main(int argc, char *argv[])
292{
293	int	c, fd;
294	int     sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
295	int     display = 0, batt_life = 0, ac_status = 0, standby = 0;
296	int	batt_time = 0, delta = 0;
297	char	*cmdname;
298	size_t	cmos_wall_len = sizeof(cmos_wall);
299
300	if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
301	    NULL, 0) == -1)
302		err(1, "sysctlbyname(machdep.wall_cmos_clock)");
303	if ((cmdname = strrchr(argv[0], '/')) != NULL)
304		cmdname++;
305	else
306		cmdname = argv[0];
307
308	if (strcmp(cmdname, "zzz") == 0) {
309		sleep = 1;
310		all_info = 0;
311		goto finish_option;
312	}
313	while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) {
314		switch (c) {
315		case 'a':
316			ac_status = 1;
317			all_info = 0;
318			break;
319		case 'b':
320			batt_status = 1;
321			all_info = 0;
322			break;
323		case 'd':
324			display = *optarg - '0';
325			if (display < 0 || display > 1) {
326				warnx("argument of option '-%c' is invalid", c);
327				usage();
328			}
329			display++;
330			all_info = 0;
331			break;
332		case 'l':
333			batt_life = 1;
334			all_info = 0;
335			break;
336		case 'R':
337			delta = -1;
338			break;
339		case 'r':
340			delta = atoi(optarg);
341			break;
342		case 's':
343			apm_status = 1;
344			all_info = 0;
345			break;
346		case 't':
347			batt_time = 1;
348			all_info = 0;
349			break;
350		case 'z':
351			sleep = 1;
352			all_info = 0;
353			break;
354		case 'Z':
355			standby = 1;
356			all_info = 0;
357			break;
358		case '?':
359		default:
360			usage();
361		}
362		argc -= optind;
363		argv += optind;
364	}
365finish_option:
366	fd = open(APMDEV, O_RDWR);
367	if (fd == -1) {
368		warn("can't open %s", APMDEV);
369		return 1;
370	}
371	if (delta)
372		apm_set_timer(fd, delta);
373	if (sleep)
374		apm_suspend(fd);
375	else if (standby)
376		apm_standby(fd);
377	else if (delta == 0) {
378		struct apm_info info;
379
380		apm_getinfo(fd, &info);
381		if (all_info)
382			print_all_info(fd, &info);
383		if (ac_status)
384			printf("%d\n", info.ai_acline);
385		if (batt_status)
386			printf("%d\n", info.ai_batt_stat);
387		if (batt_life)
388			printf("%d\n", info.ai_batt_life);
389		if (apm_status)
390			printf("%d\n", info.ai_status);
391		if (batt_time)
392			printf("%d\n", info.ai_batt_time);
393		if (display)
394			apm_display(fd, display - 1);
395	}
396	close(fd);
397	return 0;
398}
399