MFC r1.6 r1.30 r1.28 (HEAD):
[dragonfly.git] / usr.sbin / apm / apm.c
blob7fdc1a2e93532eff33abea871258921f88de75c7
1 /*
2 * apm / zzz APM BIOS utility for FreeBSD
4 * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
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.
13 * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
15 * $FreeBSD: src/usr.sbin/apm/apm.c,v 1.22.2.6 2003/04/29 08:53:04 maxim Exp $
16 * $DragonFly: src/usr.sbin/apm/apm.c,v 1.13 2006/08/13 02:18:51 swildner Exp $
19 #include <sys/file.h>
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <sys/sysctl.h>
24 #include <machine/apm_bios.h>
26 #include <err.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
33 #define APMDEV "/dev/apm"
35 #define xh(a) (((a) & 0xff00) >> 8)
36 #define xl(a) ((a) & 0xff)
37 #define APMERR(a) xh(a)
39 static void apm_display(int, int);
40 static void apm_enable(int, int);
41 static void apm_getinfo(int, apm_info_t);
42 static void apm_haltcpu(int, int);
43 static void apm_set_timer(int, int);
44 static void apm_standby(int);
45 static void apm_suspend(int);
46 static int bcd2int(int);
47 static int int2bcd(int);
48 static int is_true(const char *);
49 static void print_all_info(int, apm_info_t, int);
50 static void print_batt_life(u_int);
51 static void print_batt_stat(u_int);
52 static void print_batt_time(int);
53 static void usage(void) __dead2;
55 int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */
57 static void
58 usage(void)
60 fprintf(stderr, "%s\n%s\n",
61 "usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
62 "[ -h enable ] [-r delta]",
63 " zzz");
64 exit(EXIT_FAILURE);
68 * Return 1 for boolean true, and 0 for false, according to the
69 * interpretation of the string argument given.
71 static int
72 is_true(const char *boolean)
74 char *endp;
75 long val;
77 val = strtoul(boolean, &endp, 0);
78 if (*endp == '\0')
79 return (val != 0 ? 1 : 0);
80 if (strcasecmp(boolean, "true") == 0 ||
81 strcasecmp(boolean, "yes") == 0 ||
82 strcasecmp(boolean, "enable") == 0)
83 return (1);
84 if (strcasecmp(boolean, "false") == 0 ||
85 strcasecmp(boolean, "no") == 0 ||
86 strcasecmp(boolean, "disable") == 0)
87 return (0);
88 /* Well, I have no idea what the user wants, so... */
89 warnx("invalid boolean argument \"%s\"", boolean);
90 usage();
91 /* NOTREACHED */
94 static int
95 int2bcd(int i)
97 int retval = 0;
98 int base = 0;
100 if (i >= 10000)
101 return -1;
103 while (i) {
104 retval |= (i % 10) << base;
105 i /= 10;
106 base += 4;
108 return retval;
111 static int
112 bcd2int(int bcd)
114 int retval = 0;
115 int place = 1;
117 if (bcd > 0x9999)
118 return -1;
120 while (bcd) {
121 retval += (bcd & 0xf) * place;
122 bcd >>= 4;
123 place *= 10;
125 return retval;
128 static void
129 apm_suspend(int fd)
131 if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
132 err(1, "ioctl(APMIO_SUSPEND)");
135 static void
136 apm_standby(int fd)
138 if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
139 err(1, "ioctl(APMIO_STANDBY)");
142 static void
143 apm_getinfo(int fd, apm_info_t aip)
145 if (ioctl(fd, APMIO_GETINFO, aip) == -1)
146 err(1, "ioctl(APMIO_GETINFO)");
149 static void
150 apm_enable(int fd, int enable)
153 if (enable) {
154 if (ioctl(fd, APMIO_ENABLE) == -1)
155 err(1, "ioctl(APMIO_ENABLE)");
156 } else {
157 if (ioctl(fd, APMIO_DISABLE) == -1)
158 err(1, "ioctl(APMIO_DISABLE)");
162 static void
163 print_batt_life(u_int batt_life)
165 printf("Remaining battery life: ");
166 if (batt_life >= 255)
167 printf("unknown\n");
168 else if (batt_life <= 100)
169 printf("%d%%\n", batt_life);
170 else
171 printf("invalid value (0x%x)\n", batt_life);
174 static void
175 print_batt_stat(u_int batt_stat)
177 const char *batt_msg[] = { "high", "low", "critical", "charging" };
179 printf("Battery Status: ");
180 if (batt_stat >= 255)
181 printf("unknown\n");
182 else if (batt_stat > 3)
183 printf("invalid value (0x%x)\n", batt_stat);
184 else
185 printf("%s\n", batt_msg[batt_stat]);
188 static void
189 print_batt_time(int batt_time)
191 printf("Remaining battery time: ");
192 if (batt_time == -1)
193 printf("unknown\n");
194 else {
195 int h, m, s;
197 h = batt_time;
198 s = h % 60;
199 h /= 60;
200 m = h % 60;
201 h /= 60;
202 printf("%2d:%02d:%02d\n", h, m, s);
206 static void
207 print_all_info(int fd, apm_info_t aip, int bioscall_available)
209 struct apm_bios_arg args;
210 int apmerr;
211 const char *line_msg[] = { "off-line", "on-line" , "backup power"};
213 printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
214 printf("APM Management: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
215 printf("AC Line status: ");
216 if (aip->ai_acline == 255)
217 printf("unknown\n");
218 else if (aip->ai_acline > 2)
219 printf("invalid value (0x%x)\n", aip->ai_acline);
220 else
221 printf("%s\n", line_msg[aip->ai_acline]);
223 print_batt_stat(aip->ai_batt_stat);
224 print_batt_life(aip->ai_batt_life);
225 print_batt_time(aip->ai_batt_time);
227 if (aip->ai_infoversion >= 1) {
228 printf("Number of batteries: ");
229 if (aip->ai_batteries == (u_int) -1)
230 printf("unknown\n");
231 else {
232 u_int i;
233 struct apm_pwstatus aps;
235 printf("%d\n", aip->ai_batteries);
236 for (i = 0; i < aip->ai_batteries; ++i) {
237 bzero(&aps, sizeof(aps));
238 aps.ap_device = PMDV_BATT0 + i;
239 if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
240 continue;
241 printf("Battery %d:\n", i);
242 if (aps.ap_batt_flag != 255 &&
243 (aps.ap_batt_flag & APM_BATT_NOT_PRESENT)) {
244 printf("\tnot present\n");
245 continue;
248 printf("\t");
249 print_batt_stat(aps.ap_batt_stat);
250 printf("\t");
251 print_batt_life(aps.ap_batt_life);
252 printf("\t");
253 print_batt_time(aps.ap_batt_time);
258 if (bioscall_available) {
260 * try to get the suspend timer
262 bzero(&args, sizeof(args));
263 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
264 args.ebx = PMDV_APMBIOS;
265 args.ecx = 0x0001;
266 if (ioctl(fd, APMIO_BIOS, &args)) {
267 printf("Resume timer: unknown\n");
268 } else {
269 apmerr = APMERR(args.eax);
270 if (apmerr == 0x0d || apmerr == 0x86)
271 printf("Resume timer: disabled\n");
272 else if (apmerr)
273 fprintf(stderr,
274 "Failed to get the resume timer: APM error0x%x\n",
275 apmerr);
276 else {
278 * OK. We have the time (all bcd).
279 * CH - seconds
280 * DH - hours
281 * DL - minutes
282 * xh(SI) - month (1-12)
283 * xl(SI) - day of month (1-31)
284 * DI - year
286 struct tm tm;
287 char buf[1024];
288 time_t t;
290 tm.tm_sec = bcd2int(xh(args.ecx));
291 tm.tm_min = bcd2int(xl(args.edx));
292 tm.tm_hour = bcd2int(xh(args.edx));
293 tm.tm_mday = bcd2int(xl(args.esi));
294 tm.tm_mon = bcd2int(xh(args.esi)) - 1;
295 tm.tm_year = bcd2int(args.edi) - 1900;
296 if (cmos_wall)
297 t = mktime(&tm);
298 else
299 t = timegm(&tm);
300 tm = *localtime(&t);
301 strftime(buf, sizeof(buf), "%s", &tm);
302 printf("Resume timer: %s\n", buf);
307 * Get the ring indicator resume state
309 bzero(&args, sizeof(args));
310 args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING;
311 args.ebx = PMDV_APMBIOS;
312 args.ecx = 0x0002;
313 if (ioctl(fd, APMIO_BIOS, &args) == 0) {
314 printf("Resume on ring indicator: %sabled\n",
315 args.ecx ? "en" : "dis");
319 if (aip->ai_infoversion >= 1) {
320 printf("APM Capacities:\n");
321 if (aip->ai_capabilities == 0xff00)
322 printf("\tunknown\n");
323 if (aip->ai_capabilities & 0x01)
324 printf("\tglobal standby state\n");
325 if (aip->ai_capabilities & 0x02)
326 printf("\tglobal suspend state\n");
327 if (aip->ai_capabilities & 0x04)
328 printf("\tresume timer from standby\n");
329 if (aip->ai_capabilities & 0x08)
330 printf("\tresume timer from suspend\n");
331 if (aip->ai_capabilities & 0x10)
332 printf("\tRI resume from standby\n");
333 if (aip->ai_capabilities & 0x20)
334 printf("\tRI resume from suspend\n");
335 if (aip->ai_capabilities & 0x40)
336 printf("\tPCMCIA RI resume from standby\n");
337 if (aip->ai_capabilities & 0x80)
338 printf("\tPCMCIA RI resume from suspend\n");
344 * currently, it can turn off the display, but the display never comes
345 * back until the machine suspend/resumes :-).
347 static void
348 apm_display(int fd, int newstate)
350 if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
351 err(1, "ioctl(APMIO_DISPLAY)");
354 static void
355 apm_haltcpu(int fd, int enable)
358 if (enable) {
359 if (ioctl(fd, APMIO_HALTCPU, NULL) == -1)
360 err(1, "ioctl(APMIO_HALTCPU)");
361 } else {
362 if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1)
363 err(1, "ioctl(APMIO_NOTHALTCPU)");
367 static void
368 apm_set_timer(int fd, int delta)
370 time_t tmr;
371 struct tm *tm;
372 struct apm_bios_arg args;
374 tmr = time(NULL) + delta;
375 if (cmos_wall)
376 tm = localtime(&tmr);
377 else
378 tm = gmtime(&tmr);
379 bzero(&args, sizeof(args));
380 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
381 args.ebx = PMDV_APMBIOS;
382 if (delta > 0) {
383 args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
384 args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
385 args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
386 args.edi = int2bcd(tm->tm_year + 1900);
387 } else {
388 args.ecx = 0x0000;
390 if (ioctl(fd, APMIO_BIOS, &args)) {
391 err(1,"Set resume timer");
395 int
396 main(int argc, char *argv[])
398 int c, fd;
399 int apm_sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
400 int display = -1, batt_life = 0, ac_status = 0, standby = 0;
401 int batt_time = 0, delta = 0, enable = -1, haltcpu = -1;
402 const char *cmdname;
403 int bioscall_available = 0;
404 size_t cmos_wall_len = sizeof(cmos_wall);
406 if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
407 NULL, 0) == -1)
408 err(1, "sysctlbyname(machdep.wall_cmos_clock)");
409 if ((cmdname = strrchr(argv[0], '/')) != NULL)
410 cmdname++;
411 else
412 cmdname = argv[0];
414 if (strcmp(cmdname, "zzz") == 0) {
415 apm_sleep = 1;
416 all_info = 0;
417 goto finish_option;
419 while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) {
420 switch (c) {
421 case 'a':
422 ac_status = 1;
423 all_info = 0;
424 break;
425 case 'b':
426 batt_status = 1;
427 all_info = 0;
428 break;
429 case 'd':
430 display = is_true(optarg);
431 all_info = 0;
432 break;
433 case 'l':
434 batt_life = 1;
435 all_info = 0;
436 break;
437 case 'R':
438 delta = -1;
439 break;
440 case 'r':
441 delta = atoi(optarg);
442 break;
443 case 's':
444 apm_status = 1;
445 all_info = 0;
446 break;
447 case 'e':
448 enable = is_true(optarg);
449 all_info = 0;
450 break;
451 case 'h':
452 haltcpu = is_true(optarg);
453 all_info = 0;
454 break;
455 case 't':
456 batt_time = 1;
457 all_info = 0;
458 break;
459 case 'z':
460 apm_sleep = 1;
461 all_info = 0;
462 break;
463 case 'Z':
464 standby = 1;
465 all_info = 0;
466 break;
467 default:
468 usage();
470 argc -= optind;
471 argv += optind;
473 finish_option:
474 if (haltcpu != -1 || enable != -1 || display != -1 || delta || apm_sleep || standby) {
475 fd = open(APMDEV, O_RDWR);
476 bioscall_available = 1;
477 } else if ((fd = open(APMDEV, O_RDWR)) >= 0)
478 bioscall_available = 1;
479 else
480 fd = open(APMDEV, O_RDONLY);
481 if (fd == -1)
482 err(1, "can't open %s", APMDEV);
483 if (enable != -1)
484 apm_enable(fd, enable);
485 if (haltcpu != -1)
486 apm_haltcpu(fd, haltcpu);
487 if (delta)
488 apm_set_timer(fd, delta);
489 if (apm_sleep)
490 apm_suspend(fd);
491 else if (standby)
492 apm_standby(fd);
493 else if (delta == 0) {
494 struct apm_info info;
496 apm_getinfo(fd, &info);
497 if (all_info)
498 print_all_info(fd, &info, bioscall_available);
499 if (ac_status)
500 printf("%d\n", info.ai_acline);
501 if (batt_status)
502 printf("%d\n", info.ai_batt_stat);
503 if (batt_life)
504 printf("%d\n", info.ai_batt_life);
505 if (apm_status)
506 printf("%d\n", info.ai_status);
507 if (batt_time)
508 printf("%d\n", info.ai_batt_time);
509 if (display != -1)
510 apm_display(fd, display);
512 close(fd);
513 exit(EXIT_SUCCESS);