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