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