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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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