apm.c revision 48939
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.15 1999/07/20 15:31:23 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	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		err(1,"Get resume timer");
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
227	if (aip->ai_infoversion >= 1) {
228		printf("APM Capacities:\n", aip->ai_capabilities);
229		if (aip->ai_capabilities == 0xff00)
230			printf("\tunknown\n");
231		if (aip->ai_capabilities & 0x01)
232			printf("\tglobal standby state\n");
233		if (aip->ai_capabilities & 0x02)
234			printf("\tglobal suspend state\n");
235		if (aip->ai_capabilities & 0x04)
236			printf("\tresume timer from standby\n");
237		if (aip->ai_capabilities & 0x08)
238			printf("\tresume timer from suspend\n");
239		if (aip->ai_capabilities & 0x10)
240			printf("\tRI resume from standby\n");
241		if (aip->ai_capabilities & 0x20)
242			printf("\tRI resume from suspend\n");
243		if (aip->ai_capabilities & 0x40)
244			printf("\tPCMCIA RI resume from standby\n");
245		if (aip->ai_capabilities & 0x80)
246			printf("\tPCMCIA RI resume from suspend\n");
247	}
248
249}
250
251/*
252 * currently, it can turn off the display, but the display never comes
253 * back until the machine suspend/resumes :-).
254 */
255void
256apm_display(int fd, int newstate)
257{
258	if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
259		err(1, NULL);
260}
261
262
263void
264apm_set_timer(int fd, int delta)
265{
266	time_t tmr;
267	struct tm *tm;
268	struct apm_bios_arg args;
269
270	tmr = time(NULL) + delta;
271	if (cmos_wall)
272		tm = localtime(&tmr);
273	else
274		tm = gmtime(&tmr);
275	bzero(&args, sizeof(args));
276	args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
277	args.ebx = PMDV_APMBIOS;
278	if (delta > 0) {
279		args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
280		args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
281		args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
282		args.edi = int2bcd(tm->tm_year + 1900);
283	} else {
284		args.ecx = 0x0000;
285	}
286	if (ioctl(fd, APMIO_BIOS, &args)) {
287		err(1,"Set resume timer");
288	}
289}
290
291int
292main(int argc, char *argv[])
293{
294	int	c, fd;
295	int     sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
296	int     display = 0, batt_life = 0, ac_status = 0, standby = 0;
297	int	batt_time = 0, delta = 0;
298	char	*cmdname;
299	size_t	cmos_wall_len = sizeof(cmos_wall);
300
301	if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
302	    NULL, 0) == -1)
303		err(1, "sysctlbyname(machdep.wall_cmos_clock)");
304	if ((cmdname = strrchr(argv[0], '/')) != NULL)
305		cmdname++;
306	else
307		cmdname = argv[0];
308
309	if (strcmp(cmdname, "zzz") == 0) {
310		sleep = 1;
311		all_info = 0;
312		goto finish_option;
313	}
314	while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) {
315		switch (c) {
316		case 'a':
317			ac_status = 1;
318			all_info = 0;
319			break;
320		case 'b':
321			batt_status = 1;
322			all_info = 0;
323			break;
324		case 'd':
325			display = *optarg - '0';
326			if (display < 0 || display > 1) {
327				warnx("argument of option '-%c' is invalid", c);
328				usage();
329			}
330			display++;
331			all_info = 0;
332			break;
333		case 'l':
334			batt_life = 1;
335			all_info = 0;
336			break;
337		case 'R':
338			delta = -1;
339			break;
340		case 'r':
341			delta = atoi(optarg);
342			break;
343		case 's':
344			apm_status = 1;
345			all_info = 0;
346			break;
347		case 't':
348			batt_time = 1;
349			all_info = 0;
350			break;
351		case 'z':
352			sleep = 1;
353			all_info = 0;
354			break;
355		case 'Z':
356			standby = 1;
357			all_info = 0;
358			break;
359		case '?':
360		default:
361			usage();
362		}
363		argc -= optind;
364		argv += optind;
365	}
366finish_option:
367	fd = open(APMDEV, O_RDWR);
368	if (fd == -1) {
369		warn("can't open %s", APMDEV);
370		return 1;
371	}
372	if (delta)
373		apm_set_timer(fd, delta);
374	if (sleep)
375		apm_suspend(fd);
376	else if (standby)
377		apm_standby(fd);
378	else if (delta == 0) {
379		struct apm_info info;
380
381		apm_getinfo(fd, &info);
382		if (all_info)
383			print_all_info(fd, &info);
384		if (ac_status)
385			printf("%d\n", info.ai_acline);
386		if (batt_status)
387			printf("%d\n", info.ai_batt_stat);
388		if (batt_life)
389			printf("%d\n", info.ai_batt_life);
390		if (apm_status)
391			printf("%d\n", info.ai_status);
392		if (batt_time)
393			printf("%d\n", info.ai_batt_time);
394		if (display)
395			apm_display(fd, display - 1);
396	}
397	close(fd);
398	return 0;
399}
400