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