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