apm.c revision 129784
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: head/usr.sbin/apm/apm.c 129784 2004-05-27 19:23:27Z njl $"); 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 41int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */ 42 43static void 44usage(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 */ 57static int 58is_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 return (0); 79} 80 81static int 82int2bcd(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 98static int 99bcd2int(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 115static void 116apm_suspend(int fd) 117{ 118 if (ioctl(fd, APMIO_SUSPEND, NULL) == -1) 119 err(1, "ioctl(APMIO_SUSPEND)"); 120} 121 122static void 123apm_standby(int fd) 124{ 125 if (ioctl(fd, APMIO_STANDBY, NULL) == -1) 126 err(1, "ioctl(APMIO_STANDBY)"); 127} 128 129static void 130apm_getinfo(int fd, apm_info_t aip) 131{ 132 if (ioctl(fd, APMIO_GETINFO, aip) == -1) 133 err(1, "ioctl(APMIO_GETINFO)"); 134} 135 136static void 137apm_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 148static void 149print_all_info(int fd, apm_info_t aip, int bioscall_available) 150{ 151 struct apm_bios_arg args; 152 int apmerr; 153 const char *batt_msg[] = { "high", "low", "critical", "charging" }; 154 const char *line_msg[] = { "off-line", "on-line" }; 155 156 printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor); 157 printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled"); 158 printf("AC Line status: "); 159 if (aip->ai_acline >= 255) 160 printf("unknown"); 161 else if (aip->ai_acline > 1) 162 printf("invalid value (0x%x)", aip->ai_acline); 163 else 164 printf("%s", line_msg[aip->ai_acline]); 165 printf("\n"); 166 printf("Battery status: "); 167 if (aip->ai_batt_stat >= 255) 168 printf("unknown"); 169 else if (aip->ai_batt_stat > 3) 170 printf("invalid value (0x%x)", aip->ai_batt_stat); 171 else 172 printf("%s", batt_msg[aip->ai_batt_stat]); 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 >= 255) 198 printf("unknown\n"); 199 else { 200 u_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 printf("%s\n", 223 batt_msg[aps.ap_batt_stat]); 224 printf("\tRemaining battery life: "); 225 if (aps.ap_batt_life >= 255) 226 printf("unknown\n"); 227 else if (aps.ap_batt_life <= 100) 228 printf("%d%%\n", aps.ap_batt_life); 229 else 230 printf("invalid value (0x%x)\n", 231 aps.ap_batt_life); 232 printf("\tRemaining battery time: "); 233 if (aps.ap_batt_time == -1) 234 printf("unknown\n"); 235 else { 236 int t, h, m, s; 237 238 t = aps.ap_batt_time; 239 s = t % 60; 240 t /= 60; 241 m = t % 60; 242 t /= 60; 243 h = t; 244 printf("%2d:%02d:%02d\n", h, m, s); 245 } 246 } 247 } 248 } 249 250 if (bioscall_available) { 251 /* 252 * try to get the suspend timer 253 */ 254 bzero(&args, sizeof(args)); 255 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 256 args.ebx = PMDV_APMBIOS; 257 args.ecx = 0x0001; 258 if (ioctl(fd, APMIO_BIOS, &args)) { 259 printf("Resume timer: unknown\n"); 260 } else { 261 apmerr = APMERR(args.eax); 262 if (apmerr == 0x0d || apmerr == 0x86) 263 printf("Resume timer: disabled\n"); 264 else if (apmerr) 265 warnx( 266 "failed to get the resume timer: APM error0x%x", apmerr); 267 else { 268 /* 269 * OK. We have the time (all bcd). 270 * CH - seconds 271 * DH - hours 272 * DL - minutes 273 * xh(SI) - month (1-12) 274 * xl(SI) - day of month (1-31) 275 * DI - year 276 */ 277 struct tm tm; 278 char buf[1024]; 279 time_t t; 280 281 tm.tm_sec = bcd2int(xh(args.ecx)); 282 tm.tm_min = bcd2int(xl(args.edx)); 283 tm.tm_hour = bcd2int(xh(args.edx)); 284 tm.tm_mday = bcd2int(xl(args.esi)); 285 tm.tm_mon = bcd2int(xh(args.esi)) - 1; 286 tm.tm_year = bcd2int(args.edi) - 1900; 287 if (cmos_wall) 288 t = mktime(&tm); 289 else 290 t = timegm(&tm); 291 if (t != -1) { 292 tm = *localtime(&t); 293 strftime(buf, sizeof(buf), "%c", &tm); 294 printf("Resume timer: %s\n", buf); 295 } else 296 printf("Resume timer: unknown\n"); 297 } 298 } 299 300 /* 301 * Get the ring indicator resume state 302 */ 303 bzero(&args, sizeof(args)); 304 args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING; 305 args.ebx = PMDV_APMBIOS; 306 args.ecx = 0x0002; 307 if (ioctl(fd, APMIO_BIOS, &args) == 0) { 308 printf("Resume on ring indicator: %sabled\n", 309 args.ecx ? "en" : "dis"); 310 } 311 } 312 313 if (aip->ai_infoversion >= 1) { 314 printf("APM Capabilities:\n"); 315 if (aip->ai_capabilities == 0xff00) 316 printf("\tunknown\n"); 317 if (aip->ai_capabilities & 0x01) 318 printf("\tglobal standby state\n"); 319 if (aip->ai_capabilities & 0x02) 320 printf("\tglobal suspend state\n"); 321 if (aip->ai_capabilities & 0x04) 322 printf("\tresume timer from standby\n"); 323 if (aip->ai_capabilities & 0x08) 324 printf("\tresume timer from suspend\n"); 325 if (aip->ai_capabilities & 0x10) 326 printf("\tRI resume from standby\n"); 327 if (aip->ai_capabilities & 0x20) 328 printf("\tRI resume from suspend\n"); 329 if (aip->ai_capabilities & 0x40) 330 printf("\tPCMCIA RI resume from standby\n"); 331 if (aip->ai_capabilities & 0x80) 332 printf("\tPCMCIA RI resume from suspend\n"); 333 } 334 335} 336 337/* 338 * currently, it can turn off the display, but the display never comes 339 * back until the machine suspend/resumes :-). 340 */ 341static void 342apm_display(int fd, int newstate) 343{ 344 if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1) 345 err(1, "ioctl(APMIO_DISPLAY)"); 346} 347 348static void 349apm_haltcpu(int fd, int enable) { 350 351 if (enable) { 352 if (ioctl(fd, APMIO_HALTCPU, NULL) == -1) 353 err(1, "ioctl(APMIO_HALTCPU)"); 354 } else { 355 if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1) 356 err(1, "ioctl(APMIO_NOTHALTCPU)"); 357 } 358} 359 360static void 361apm_set_timer(int fd, int delta) 362{ 363 time_t tmr; 364 struct tm *tm; 365 struct apm_bios_arg args; 366 367 tmr = time(NULL) + delta; 368 if (cmos_wall) 369 tm = localtime(&tmr); 370 else 371 tm = gmtime(&tmr); 372 bzero(&args, sizeof(args)); 373 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 374 args.ebx = PMDV_APMBIOS; 375 if (delta > 0) { 376 args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02; 377 args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min); 378 args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday); 379 args.edi = int2bcd(tm->tm_year + 1900); 380 } else { 381 args.ecx = 0x0000; 382 } 383 if (ioctl(fd, APMIO_BIOS, &args)) { 384 err(1,"set resume timer"); 385 } 386} 387 388int 389main(int argc, char *argv[]) 390{ 391 int c, fd; 392 int dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0; 393 int display = -1, batt_life = 0, ac_status = 0, standby = 0; 394 int batt_time = 0, delta = 0, enable = -1, haltcpu = -1; 395 char *cmdname; 396 int bioscall_available = 0; 397 size_t cmos_wall_len = sizeof(cmos_wall); 398 399 if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len, 400 NULL, 0) == -1) 401 err(1, "sysctlbyname(machdep.wall_cmos_clock)"); 402 if ((cmdname = strrchr(argv[0], '/')) != NULL) 403 cmdname++; 404 else 405 cmdname = argv[0]; 406 407 if (strcmp(cmdname, "zzz") == 0) { 408 dosleep = 1; 409 all_info = 0; 410 goto finish_option; 411 } 412 while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) { 413 switch (c) { 414 case 'a': 415 ac_status = 1; 416 all_info = 0; 417 break; 418 case 'b': 419 batt_status = 1; 420 all_info = 0; 421 break; 422 case 'd': 423 display = is_true(optarg); 424 all_info = 0; 425 break; 426 case 'l': 427 batt_life = 1; 428 all_info = 0; 429 break; 430 case 'R': 431 delta = -1; 432 break; 433 case 'r': 434 delta = atoi(optarg); 435 break; 436 case 's': 437 apm_status = 1; 438 all_info = 0; 439 break; 440 case 'e': 441 enable = is_true(optarg); 442 all_info = 0; 443 break; 444 case 'h': 445 haltcpu = is_true(optarg); 446 all_info = 0; 447 break; 448 case 't': 449 batt_time = 1; 450 all_info = 0; 451 break; 452 case 'z': 453 dosleep = 1; 454 all_info = 0; 455 break; 456 case 'Z': 457 standby = 1; 458 all_info = 0; 459 break; 460 case '?': 461 default: 462 usage(); 463 } 464 argc -= optind; 465 argv += optind; 466 } 467finish_option: 468 if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep 469 || standby) { 470 fd = open(APMDEV, O_RDWR); 471 bioscall_available = 1; 472 } else if ((fd = open(APMDEV, O_RDWR)) >= 0) 473 bioscall_available = 1; 474 else 475 fd = open(APMDEV, O_RDONLY); 476 if (fd == -1) 477 err(1, "can't open %s", APMDEV); 478 if (enable != -1) 479 apm_enable(fd, enable); 480 if (haltcpu != -1) 481 apm_haltcpu(fd, haltcpu); 482 if (delta) 483 apm_set_timer(fd, delta); 484 if (dosleep) 485 apm_suspend(fd); 486 else if (standby) 487 apm_standby(fd); 488 else if (delta == 0) { 489 struct apm_info info; 490 491 apm_getinfo(fd, &info); 492 if (all_info) 493 print_all_info(fd, &info, bioscall_available); 494 if (ac_status) 495 printf("%d\n", info.ai_acline); 496 if (batt_status) 497 printf("%d\n", info.ai_batt_stat); 498 if (batt_life) 499 printf("%d\n", info.ai_batt_life); 500 if (apm_status) 501 printf("%d\n", info.ai_status); 502 if (batt_time) 503 printf("%d\n", info.ai_batt_time); 504 if (display != -1) 505 apm_display(fd, display); 506 } 507 close(fd); 508 exit(0); 509} 510