Initial import from FreeBSD RELENG_4:
[games.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
16 #ifndef lint
17 static const char rcsid[] =
18   "$FreeBSD: src/usr.sbin/apm/apm.c,v 1.22.2.6 2003/04/29 08:53:04 maxim Exp $";
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 Management: %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                         all_info = 0;
448                         break;
449                 case 'h':
450                         haltcpu = is_true(optarg);
451                         all_info = 0;
452                         break;
453                 case 't':
454                         batt_time = 1;
455                         all_info = 0;
456                         break;
457                 case 'z':
458                         sleep = 1;
459                         all_info = 0;
460                         break;
461                 case 'Z':
462                         standby = 1;
463                         all_info = 0;
464                         break;
465                 case '?':
466                 default:
467                         usage();
468                 }
469                 argc -= optind;
470                 argv += optind;
471         }
472 finish_option:
473         if (haltcpu != -1 || enable != -1 || display != -1 || delta || sleep || standby) {
474                 fd = open(APMDEV, O_RDWR);
475                 bioscall_available = 1;
476         } else if ((fd = open(APMDEV, O_RDWR)) >= 0)
477                 bioscall_available = 1;
478         else
479                 fd = open(APMDEV, O_RDONLY);
480         if (fd == -1)
481                 err(1, "can't open %s", APMDEV);
482         if (enable != -1)
483                 apm_enable(fd, enable);
484         if (haltcpu != -1)
485                 apm_haltcpu(fd, haltcpu);
486         if (delta)
487                 apm_set_timer(fd, delta);
488         if (sleep)
489                 apm_suspend(fd);
490         else if (standby)
491                 apm_standby(fd);
492         else if (delta == 0) {
493                 struct apm_info info;
494
495                 apm_getinfo(fd, &info);
496                 if (all_info)
497                         print_all_info(fd, &info, bioscall_available);
498                 if (ac_status)
499                         printf("%d\n", info.ai_acline);
500                 if (batt_status)
501                         printf("%d\n", info.ai_batt_stat);
502                 if (batt_life)
503                         printf("%d\n", info.ai_batt_life);
504                 if (apm_status)
505                         printf("%d\n", info.ai_status);
506                 if (batt_time)
507                         printf("%d\n", info.ai_batt_time);
508                 if (display != -1)
509                         apm_display(fd, display);
510         }
511         close(fd);
512         exit(0);
513 }