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