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
usage(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
is_true(const char * boolean)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
int2bcd(int i)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
bcd2int(int bcd)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
apm_suspend(int fd)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
apm_standby(int fd)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
apm_getinfo(int fd,apm_info_t aip)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
apm_enable(int fd,int enable)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
print_batt_time(int batt_time)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
print_batt_life(u_int batt_life)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
print_batt_stat(u_int batt_stat)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
print_all_info(int fd,apm_info_t aip,int bioscall_available)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
apm_display(int fd,int newstate)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
apm_haltcpu(int fd,int enable)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
apm_set_timer(int fd,int delta)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
main(int argc,char * argv[])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