xref: /freebsd/usr.sbin/apm/apm.c (revision 1b6c76a2fe091c74f08427e6c870851025a9cf67)
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
17 static const char rcsid[] =
18   "$FreeBSD$";
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 
43 int cmos_wall = 0;	/* True when wall time is in cmos clock, else UTC */
44 
45 void
46 usage()
47 {
48 	fprintf(stderr, "%s\n%s\n",
49 		"usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
50 		"[ -h enable ] [-r delta]",
51 		"       zzz");
52 	exit(1);
53 }
54 
55 /*
56  * Return 1 for boolean true, and 0 for false, according to the
57  * interpretation of the string argument given.
58  */
59 int
60 is_true(const char *boolean) {
61 	char *endp;
62 	long val;
63 
64 	val = strtoul(boolean, &endp, 0);
65 	if (*endp == '\0')
66 		return (val != 0 ? 1 : 0);
67 	if (strcasecmp(boolean, "true") == 0 ||
68 	    strcasecmp(boolean, "yes") == 0 ||
69 	    strcasecmp(boolean, "enable") == 0)
70 		return (1);
71 	if (strcasecmp(boolean, "false") == 0 ||
72 	    strcasecmp(boolean, "no") == 0 ||
73 	    strcasecmp(boolean, "disable") == 0)
74 		return (0);
75 	/* Well, I have no idea what the user wants, so... */
76 	warnx("invalid boolean argument \"%s\"", boolean);
77 	usage();
78 	/* NOTREACHED */
79 }
80 
81 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 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 void
116 apm_suspend(int fd)
117 {
118 	if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
119 		err(1, "ioctl(APMIO_SUSPEND)");
120 }
121 
122 void
123 apm_standby(int fd)
124 {
125 	if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
126 		err(1, "ioctl(APMIO_STANDBY)");
127 }
128 
129 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 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 void
149 print_all_info(int fd, apm_info_t aip, int bioscall_available)
150 {
151 	struct apm_bios_arg args;
152 	int apmerr;
153 
154 	printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
155 	printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
156 	printf("AC Line status: ");
157 	if (aip->ai_acline == 255)
158 		printf("unknown");
159 	else if (aip->ai_acline > 1)
160 		printf("invalid value (0x%x)", aip->ai_acline);
161 	else {
162 		char *messages[] = { "off-line", "on-line" };
163 		printf("%s", messages[aip->ai_acline]);
164 	}
165 	printf("\n");
166 	printf("Battery status: ");
167 	if (aip->ai_batt_stat == 255)
168 		printf("unknown");
169 	else if (aip->ai_batt_stat > 3)
170 			printf("invalid value (0x%x)", aip->ai_batt_stat);
171 	else {
172 		char *messages[] = { "high", "low", "critical", "charging" };
173 		printf("%s", messages[aip->ai_batt_stat]);
174 	}
175 	printf("\n");
176 	printf("Remaining battery life: ");
177 	if (aip->ai_batt_life == 255)
178 		printf("unknown\n");
179 	else if (aip->ai_batt_life <= 100)
180 		printf("%d%%\n", aip->ai_batt_life);
181 	else
182 		printf("invalid value (0x%x)\n", aip->ai_batt_life);
183 	printf("Remaining battery time: ");
184 	if (aip->ai_batt_time == -1)
185 		printf("unknown\n");
186 	else {
187 		int t, h, m, s;
188 
189 		t = aip->ai_batt_time;
190 		s = t % 60;
191 		t /= 60;
192 		m = t % 60;
193 		t /= 60;
194 		h = t;
195 		printf("%2d:%02d:%02d\n", h, m, s);
196 	}
197 	if (aip->ai_infoversion >= 1) {
198 		printf("Number of batteries: ");
199 		if (aip->ai_batteries == (u_int) -1)
200 			printf("unknown\n");
201 		else {
202 			int i;
203 			struct apm_pwstatus aps;
204 
205 			printf("%d\n", aip->ai_batteries);
206 			for (i = 0; i < aip->ai_batteries; ++i) {
207 				bzero(&aps, sizeof(aps));
208 				aps.ap_device = PMDV_BATT0 + i;
209 				if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
210 					continue;
211 				printf("Battery %d:\n", i);
212 				printf("\tBattery status: ");
213 				if (aps.ap_batt_flag != 255 &&
214 				    (aps.ap_batt_flag & APM_BATT_NOT_PRESENT)) {
215 					printf("not present\n");
216 					continue;
217 				}
218 				if (aps.ap_batt_stat == 255)
219 					printf("unknown\n");
220 				else if (aps.ap_batt_stat > 3)
221 					printf("invalid value (0x%x)\n",
222 					       aps.ap_batt_stat);
223 				else {
224 					char *messages[] = { "high",
225 							     "low",
226 							     "critical",
227 							     "charging" };
228 					printf("%s\n",
229 					       messages[aps.ap_batt_stat]);
230 				}
231 				printf("\tRemaining battery life: ");
232 				if (aps.ap_batt_life == 255)
233 					printf("unknown\n");
234 				else if (aps.ap_batt_life <= 100)
235 					printf("%d%%\n", aps.ap_batt_life);
236 				else
237 					printf("invalid value (0x%x)\n",
238 					       aps.ap_batt_life);
239 				printf("\tRemaining battery time: ");
240 				if (aps.ap_batt_time == -1)
241 					printf("unknown\n");
242 				else {
243 					int t, h, m, s;
244 
245 					t = aps.ap_batt_time;
246 					s = t % 60;
247 					t /= 60;
248 					m = t % 60;
249 					t /= 60;
250 					h = t;
251 					printf("%2d:%02d:%02d\n", h, m, s);
252 				}
253 			}
254 		}
255 	}
256 
257 	if (bioscall_available) {
258 		/*
259 		 * try to get the suspend timer
260 		 */
261 		bzero(&args, sizeof(args));
262 		args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
263 		args.ebx = PMDV_APMBIOS;
264 		args.ecx = 0x0001;
265 		if (ioctl(fd, APMIO_BIOS, &args)) {
266 			printf("Resume timer: unknown\n");
267 		} else {
268 			apmerr = APMERR(args.eax);
269 			if (apmerr == 0x0d || apmerr == 0x86)
270 				printf("Resume timer: disabled\n");
271 			else if (apmerr)
272 				fprintf(stderr,
273 					"Failed to get the resume timer: APM error0x%x\n",
274 					apmerr);
275 			else {
276 				/*
277 				 * OK.  We have the time (all bcd).
278 				 * CH - seconds
279 				 * DH - hours
280 				 * DL - minutes
281 				 * xh(SI) - month (1-12)
282 				 * xl(SI) - day of month (1-31)
283 				 * DI - year
284 				 */
285 				struct tm tm;
286 				char buf[1024];
287 				time_t t;
288 
289 				tm.tm_sec = bcd2int(xh(args.ecx));
290 				tm.tm_min = bcd2int(xl(args.edx));
291 				tm.tm_hour = bcd2int(xh(args.edx));
292 				tm.tm_mday = bcd2int(xl(args.esi));
293 				tm.tm_mon = bcd2int(xh(args.esi)) - 1;
294 				tm.tm_year = bcd2int(args.edi) - 1900;
295 				if (cmos_wall)
296 					t = mktime(&tm);
297 				else
298 					t = timegm(&tm);
299 				tm = *localtime(&t);
300 				strftime(buf, sizeof(buf), "%c", &tm);
301 				printf("Resume timer: %s\n", buf);
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 Capacities:\n", aip->ai_capabilities);
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 			break;
448 		case 'h':
449 			haltcpu = is_true(optarg);
450 			break;
451 		case 't':
452 			batt_time = 1;
453 			all_info = 0;
454 			break;
455 		case 'z':
456 			sleep = 1;
457 			all_info = 0;
458 			break;
459 		case 'Z':
460 			standby = 1;
461 			all_info = 0;
462 			break;
463 		case '?':
464 		default:
465 			usage();
466 		}
467 		argc -= optind;
468 		argv += optind;
469 	}
470 finish_option:
471 	if (haltcpu != -1 || enable != -1 || display != -1 || delta || sleep || standby) {
472 		fd = open(APMDEV, O_RDWR);
473 		bioscall_available = 1;
474 	} else if ((fd = open(APMDEV, O_RDWR)) >= 0)
475 		bioscall_available = 1;
476 	else
477 		fd = open(APMDEV, O_RDONLY);
478 	if (fd == -1)
479 		err(1, "can't open %s", APMDEV);
480 	if (enable != -1)
481 		apm_enable(fd, enable);
482 	if (haltcpu != -1)
483 		apm_haltcpu(fd, haltcpu);
484 	if (delta)
485 		apm_set_timer(fd, delta);
486 	if (sleep)
487 		apm_suspend(fd);
488 	else if (standby)
489 		apm_standby(fd);
490 	else if (delta == 0) {
491 		struct apm_info info;
492 
493 		apm_getinfo(fd, &info);
494 		if (all_info)
495 			print_all_info(fd, &info, bioscall_available);
496 		if (ac_status)
497 			printf("%d\n", info.ai_acline);
498 		if (batt_status)
499 			printf("%d\n", info.ai_batt_stat);
500 		if (batt_life)
501 			printf("%d\n", info.ai_batt_life);
502 		if (apm_status)
503 			printf("%d\n", info.ai_status);
504 		if (batt_time)
505 			printf("%d\n", info.ai_batt_time);
506 		if (display != -1)
507 			apm_display(fd, display);
508 	}
509 	close(fd);
510 	exit(0);
511 }
512