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