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