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