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 #ifndef lint 17 static const char rcsid[] = 18 "$FreeBSD$"; 19 #endif /* not lint */ 20 21 #include <sys/file.h> 22 #include <sys/ioctl.h> 23 #include <sys/types.h> 24 #include <sys/sysctl.h> 25 26 #include <machine/apm_bios.h> 27 28 #include <err.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <sys/file.h> 33 #include <sys/ioctl.h> 34 #include <time.h> 35 #include <unistd.h> 36 37 #define APMDEV "/dev/apm" 38 39 #define xh(a) (((a) & 0xff00) >> 8) 40 #define xl(a) ((a) & 0xff) 41 #define APMERR(a) xh(a) 42 43 int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */ 44 45 void 46 usage() 47 { 48 fprintf(stderr, "%s\n%s\n", 49 "usage: apm [-ablstzZ] [-d enable ] [ -e enable ] " 50 "[ -h enable ] [-r delta]", 51 " zzz"); 52 exit(1); 53 } 54 55 /* 56 * Return 1 for boolean true, and 0 for false, according to the 57 * interpretation of the string argument given. 58 */ 59 int 60 is_true(const char *boolean) { 61 char *endp; 62 long val; 63 64 val = strtoul(boolean, &endp, 0); 65 if (*endp == '\0') 66 return (val != 0 ? 1 : 0); 67 if (strcasecmp(boolean, "true") == 0 || 68 strcasecmp(boolean, "yes") == 0 || 69 strcasecmp(boolean, "enable") == 0) 70 return (1); 71 if (strcasecmp(boolean, "false") == 0 || 72 strcasecmp(boolean, "no") == 0 || 73 strcasecmp(boolean, "disable") == 0) 74 return (0); 75 /* Well, I have no idea what the user wants, so... */ 76 warnx("invalid boolean argument \"%s\"", boolean); 77 usage(); 78 /* NOTREACHED */ 79 } 80 81 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 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 void 116 apm_suspend(int fd) 117 { 118 if (ioctl(fd, APMIO_SUSPEND, NULL) == -1) 119 err(1, "ioctl(APMIO_SUSPEND)"); 120 } 121 122 void 123 apm_standby(int fd) 124 { 125 if (ioctl(fd, APMIO_STANDBY, NULL) == -1) 126 err(1, "ioctl(APMIO_STANDBY)"); 127 } 128 129 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 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 void 149 print_all_info(int fd, apm_info_t aip) 150 { 151 struct apm_bios_arg args; 152 int apmerr; 153 154 printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor); 155 printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled")); 156 printf("AC Line status: "); 157 if (aip->ai_acline == 255) 158 printf("unknown"); 159 else if (aip->ai_acline > 1) 160 printf("invalid value (0x%x)", aip->ai_acline); 161 else { 162 char *messages[] = { "off-line", "on-line" }; 163 printf("%s", messages[aip->ai_acline]); 164 } 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 char *messages[] = { "high", "low", "critical", "charging" }; 173 printf("%s", messages[aip->ai_batt_stat]); 174 } 175 printf("\n"); 176 printf("Remaining battery life: "); 177 if (aip->ai_batt_life == 255) 178 printf("unknown\n"); 179 else if (aip->ai_batt_life <= 100) 180 printf("%d%%\n", aip->ai_batt_life); 181 else 182 printf("invalid value (0x%x)\n", aip->ai_batt_life); 183 printf("Remaining battery time: "); 184 if (aip->ai_batt_time == -1) 185 printf("unknown\n"); 186 else { 187 int t, h, m, s; 188 189 t = aip->ai_batt_time; 190 s = t % 60; 191 t /= 60; 192 m = t % 60; 193 t /= 60; 194 h = t; 195 printf("%2d:%02d:%02d\n", h, m, s); 196 } 197 if (aip->ai_infoversion >= 1) { 198 printf("Number of batteries: "); 199 if (aip->ai_batteries == (u_int) -1) 200 printf("unknown\n"); 201 else 202 printf("%d\n", aip->ai_batteries); 203 } 204 205 /* 206 * try to get the suspend timer 207 */ 208 bzero(&args, sizeof(args)); 209 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 210 args.ebx = PMDV_APMBIOS; 211 args.ecx = 0x0001; 212 if (ioctl(fd, APMIO_BIOS, &args)) { 213 printf("Resume timer: unknown\n"); 214 } else { 215 apmerr = APMERR(args.eax); 216 if (apmerr == 0x0d || apmerr == 0x86) 217 printf("Resume timer: disabled\n"); 218 else if (apmerr) 219 fprintf(stderr, 220 "Failed to get the resume timer: APM error0x%x\n", 221 apmerr); 222 else { 223 /* 224 * OK. We have the time (all bcd). 225 * CH - seconds 226 * DH - hours 227 * DL - minutes 228 * xh(SI) - month (1-12) 229 * xl(SI) - day of month (1-31) 230 * DI - year 231 */ 232 struct tm tm; 233 char buf[1024]; 234 time_t t; 235 236 tm.tm_sec = bcd2int(xh(args.ecx)); 237 tm.tm_min = bcd2int(xl(args.edx)); 238 tm.tm_hour = bcd2int(xh(args.edx)); 239 tm.tm_mday = bcd2int(xl(args.esi)); 240 tm.tm_mon = bcd2int(xh(args.esi)) - 1; 241 tm.tm_year = bcd2int(args.edi) - 1900; 242 if (cmos_wall) 243 t = mktime(&tm); 244 else 245 t = timegm(&tm); 246 tm = *localtime(&t); 247 strftime(buf, sizeof(buf), "%c", &tm); 248 printf("Resume timer: %s\n", buf); 249 } 250 } 251 252 /* 253 * Get the ring indicator resume state 254 */ 255 bzero(&args, sizeof(args)); 256 args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING; 257 args.ebx = PMDV_APMBIOS; 258 args.ecx = 0x0002; 259 if (ioctl(fd, APMIO_BIOS, &args) == 0) { 260 printf("Resume on ring indicator: %sabled\n", 261 args.ecx ? "en" : "dis"); 262 } 263 if (aip->ai_infoversion >= 1) { 264 printf("APM Capacities:\n", aip->ai_capabilities); 265 if (aip->ai_capabilities == 0xff00) 266 printf("\tunknown\n"); 267 if (aip->ai_capabilities & 0x01) 268 printf("\tglobal standby state\n"); 269 if (aip->ai_capabilities & 0x02) 270 printf("\tglobal suspend state\n"); 271 if (aip->ai_capabilities & 0x04) 272 printf("\tresume timer from standby\n"); 273 if (aip->ai_capabilities & 0x08) 274 printf("\tresume timer from suspend\n"); 275 if (aip->ai_capabilities & 0x10) 276 printf("\tRI resume from standby\n"); 277 if (aip->ai_capabilities & 0x20) 278 printf("\tRI resume from suspend\n"); 279 if (aip->ai_capabilities & 0x40) 280 printf("\tPCMCIA RI resume from standby\n"); 281 if (aip->ai_capabilities & 0x80) 282 printf("\tPCMCIA RI resume from suspend\n"); 283 } 284 285 } 286 287 /* 288 * currently, it can turn off the display, but the display never comes 289 * back until the machine suspend/resumes :-). 290 */ 291 void 292 apm_display(int fd, int newstate) 293 { 294 if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1) 295 err(1, "ioctl(APMIO_DISPLAY)"); 296 } 297 298 void 299 apm_haltcpu(int fd, int enable) { 300 301 if (enable) { 302 if (ioctl(fd, APMIO_HALTCPU, NULL) == -1) 303 err(1, "ioctl(APMIO_HALTCPU)"); 304 } else { 305 if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1) 306 err(1, "ioctl(APMIO_NOTHALTCPU)"); 307 } 308 } 309 310 void 311 apm_set_timer(int fd, int delta) 312 { 313 time_t tmr; 314 struct tm *tm; 315 struct apm_bios_arg args; 316 317 tmr = time(NULL) + delta; 318 if (cmos_wall) 319 tm = localtime(&tmr); 320 else 321 tm = gmtime(&tmr); 322 bzero(&args, sizeof(args)); 323 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 324 args.ebx = PMDV_APMBIOS; 325 if (delta > 0) { 326 args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02; 327 args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min); 328 args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday); 329 args.edi = int2bcd(tm->tm_year + 1900); 330 } else { 331 args.ecx = 0x0000; 332 } 333 if (ioctl(fd, APMIO_BIOS, &args)) { 334 err(1,"Set resume timer"); 335 } 336 } 337 338 int 339 main(int argc, char *argv[]) 340 { 341 int c, fd; 342 int sleep = 0, all_info = 1, apm_status = 0, batt_status = 0; 343 int display = -1, batt_life = 0, ac_status = 0, standby = 0; 344 int batt_time = 0, delta = 0, enable = -1, haltcpu = -1; 345 char *cmdname; 346 size_t cmos_wall_len = sizeof(cmos_wall); 347 348 if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len, 349 NULL, 0) == -1) 350 err(1, "sysctlbyname(machdep.wall_cmos_clock)"); 351 if ((cmdname = strrchr(argv[0], '/')) != NULL) 352 cmdname++; 353 else 354 cmdname = argv[0]; 355 356 if (strcmp(cmdname, "zzz") == 0) { 357 sleep = 1; 358 all_info = 0; 359 goto finish_option; 360 } 361 while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) { 362 switch (c) { 363 case 'a': 364 ac_status = 1; 365 all_info = 0; 366 break; 367 case 'b': 368 batt_status = 1; 369 all_info = 0; 370 break; 371 case 'd': 372 display = is_true(optarg); 373 all_info = 0; 374 break; 375 case 'l': 376 batt_life = 1; 377 all_info = 0; 378 break; 379 case 'R': 380 delta = -1; 381 break; 382 case 'r': 383 delta = atoi(optarg); 384 break; 385 case 's': 386 apm_status = 1; 387 all_info = 0; 388 break; 389 case 'e': 390 enable = is_true(optarg); 391 break; 392 case 'h': 393 haltcpu = is_true(optarg); 394 break; 395 case 't': 396 batt_time = 1; 397 all_info = 0; 398 break; 399 case 'z': 400 sleep = 1; 401 all_info = 0; 402 break; 403 case 'Z': 404 standby = 1; 405 all_info = 0; 406 break; 407 case '?': 408 default: 409 usage(); 410 } 411 argc -= optind; 412 argv += optind; 413 } 414 finish_option: 415 fd = open(APMDEV, O_RDWR); 416 if (fd == -1) 417 err(1, "can't open %s", APMDEV); 418 if (enable != -1) 419 apm_enable(fd, enable); 420 if (haltcpu != -1) 421 apm_haltcpu(fd, haltcpu); 422 if (delta) 423 apm_set_timer(fd, delta); 424 if (sleep) 425 apm_suspend(fd); 426 else if (standby) 427 apm_standby(fd); 428 else if (delta == 0) { 429 struct apm_info info; 430 431 apm_getinfo(fd, &info); 432 if (all_info) 433 print_all_info(fd, &info); 434 if (ac_status) 435 printf("%d\n", info.ai_acline); 436 if (batt_status) 437 printf("%d\n", info.ai_batt_stat); 438 if (batt_life) 439 printf("%d\n", info.ai_batt_life); 440 if (apm_status) 441 printf("%d\n", info.ai_status); 442 if (batt_time) 443 printf("%d\n", info.ai_batt_time); 444 if (display != -1) 445 apm_display(fd, display); 446 } 447 close(fd); 448 exit(0); 449 } 450