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