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