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