apm.c revision 49286
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 17static const char rcsid[] = 18 "$Id: apm.c,v 1.16 1999/07/20 15:37:28 green Exp $"; 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 43int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */ 44 45void 46usage() 47{ 48 fprintf(stderr, "%s\n%s\n", 49 "usage: apm [-ablstzZ] [-d 1|0] [-r delta]", 50 " zzz"); 51 exit(1); 52} 53 54int 55int2bcd(int i) 56{ 57 int retval = 0; 58 int base = 0; 59 60 if (i >= 10000) 61 return -1; 62 63 while (i) { 64 retval |= (i % 10) << base; 65 i /= 10; 66 base += 4; 67 } 68 return retval; 69} 70 71int 72bcd2int(int bcd) 73{ 74 int retval = 0; 75 int place = 1; 76 77 if (bcd > 0x9999) 78 return -1; 79 80 while (bcd) { 81 retval += (bcd & 0xf) * place; 82 bcd >>= 4; 83 place *= 10; 84 } 85 return retval; 86} 87 88void 89apm_suspend(int fd) 90{ 91 if (ioctl(fd, APMIO_SUSPEND, NULL) == -1) 92 err(1, NULL); 93} 94 95void 96apm_standby(int fd) 97{ 98 if (ioctl(fd, APMIO_STANDBY, NULL) == -1) 99 err(1, NULL); 100} 101 102void 103apm_getinfo(int fd, apm_info_t aip) 104{ 105 if (ioctl(fd, APMIO_GETINFO, aip) == -1) 106 err(1, NULL); 107} 108 109void 110print_all_info(int fd, apm_info_t aip) 111{ 112 struct apm_bios_arg args; 113 int apmerr; 114 115 printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor); 116 printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled")); 117 printf("AC Line status: "); 118 if (aip->ai_acline == 255) 119 printf("unknown"); 120 else if (aip->ai_acline > 1) 121 printf("invalid value (0x%x)", aip->ai_acline); 122 else { 123 char messages[][10] = {"off-line", "on-line"}; 124 printf("%s", messages[aip->ai_acline]); 125 } 126 printf("\n"); 127 printf("Battery status: "); 128 if (aip->ai_batt_stat == 255) 129 printf("unknown"); 130 else if (aip->ai_batt_stat > 3) 131 printf("invalid value (0x%x)", aip->ai_batt_stat); 132 else { 133 char messages[][10] = {"high", "low", "critical", "charging"}; 134 printf("%s", messages[aip->ai_batt_stat]); 135 } 136 printf("\n"); 137 printf("Remaining battery life: "); 138 if (aip->ai_batt_life == 255) 139 printf("unknown"); 140 else if (aip->ai_batt_life <= 100) 141 printf("%d%%", aip->ai_batt_life); 142 else 143 printf("invalid value (0x%x)", aip->ai_batt_life); 144 printf("\n"); 145 printf("Remaining battery time: "); 146 if (aip->ai_batt_time == -1) 147 printf("unknown"); 148 else { 149 int t, h, m, s; 150 151 t = aip->ai_batt_time; 152 s = t % 60; 153 t /= 60; 154 m = t % 60; 155 t /= 60; 156 h = t; 157 printf("%2d:%02d:%02d", h, m, s); 158 } 159 printf("\n"); 160 if (aip->ai_infoversion >= 1) { 161 printf("Number of batteries: "); 162 if (aip->ai_batteries == (u_int) -1) 163 printf("unknown\n"); 164 else 165 printf("%d\n", aip->ai_batteries); 166 } 167 168 /* 169 * try to get the suspend timer 170 */ 171#if 0 172 bzero(&args, sizeof(args)); 173 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 174 args.ebx = PMDV_APMBIOS; 175 args.ecx = 0x0001; 176 if (ioctl(fd, APMIO_BIOS, &args)) { 177 err(1,"Get resume timer"); 178 } else { 179 apmerr = APMERR(args.eax); 180 if (apmerr == 0x0d || apmerr == 0x86) 181 printf("Resume timer: disabled\n"); 182 else if (apmerr) 183 fprintf(stderr, 184 "Failed to get the resume timer: APM error0x%x\n", 185 apmerr); 186 else { 187 /* 188 * OK. We have the time (all bcd). 189 * CH - seconds 190 * DH - hours 191 * DL - minutes 192 * xh(SI) - month (1-12) 193 * xl(SI) - day of month (1-31) 194 * DI - year 195 */ 196 struct tm tm; 197 char buf[1024]; 198 time_t t; 199 200 tm.tm_sec = bcd2int(xh(args.ecx)); 201 tm.tm_min = bcd2int(xl(args.edx)); 202 tm.tm_hour = bcd2int(xh(args.edx)); 203 tm.tm_mday = bcd2int(xl(args.esi)); 204 tm.tm_mon = bcd2int(xh(args.esi)) - 1; 205 tm.tm_year = bcd2int(args.edi) - 1900; 206 if (cmos_wall) 207 t = mktime(&tm); 208 else 209 t = timegm(&tm); 210 tm = *localtime(&t); 211 strftime(buf, sizeof(buf), "%c", &tm); 212 printf("Resume timer: %s\n", buf); 213 } 214 } 215 216 /* 217 * Get the ring indicator resume state 218 */ 219 bzero(&args, sizeof(args)); 220 args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING; 221 args.ebx = PMDV_APMBIOS; 222 args.ecx = 0x0002; 223 if (ioctl(fd, APMIO_BIOS, &args) == 0) { 224 printf("Resume on ring indicator: %sabled\n", 225 args.ecx ? "en" : "dis"); 226 } 227#endif 228 if (aip->ai_infoversion >= 1) { 229 printf("APM Capacities:\n", aip->ai_capabilities); 230 if (aip->ai_capabilities == 0xff00) 231 printf("\tunknown\n"); 232 if (aip->ai_capabilities & 0x01) 233 printf("\tglobal standby state\n"); 234 if (aip->ai_capabilities & 0x02) 235 printf("\tglobal suspend state\n"); 236 if (aip->ai_capabilities & 0x04) 237 printf("\tresume timer from standby\n"); 238 if (aip->ai_capabilities & 0x08) 239 printf("\tresume timer from suspend\n"); 240 if (aip->ai_capabilities & 0x10) 241 printf("\tRI resume from standby\n"); 242 if (aip->ai_capabilities & 0x20) 243 printf("\tRI resume from suspend\n"); 244 if (aip->ai_capabilities & 0x40) 245 printf("\tPCMCIA RI resume from standby\n"); 246 if (aip->ai_capabilities & 0x80) 247 printf("\tPCMCIA RI resume from suspend\n"); 248 } 249 250} 251 252/* 253 * currently, it can turn off the display, but the display never comes 254 * back until the machine suspend/resumes :-). 255 */ 256void 257apm_display(int fd, int newstate) 258{ 259 if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1) 260 err(1, NULL); 261} 262 263 264void 265apm_set_timer(int fd, int delta) 266{ 267 time_t tmr; 268 struct tm *tm; 269 struct apm_bios_arg args; 270 271 tmr = time(NULL) + delta; 272 if (cmos_wall) 273 tm = localtime(&tmr); 274 else 275 tm = gmtime(&tmr); 276 bzero(&args, sizeof(args)); 277 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 278 args.ebx = PMDV_APMBIOS; 279 if (delta > 0) { 280 args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02; 281 args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min); 282 args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday); 283 args.edi = int2bcd(tm->tm_year + 1900); 284 } else { 285 args.ecx = 0x0000; 286 } 287#if 0 288 if (ioctl(fd, APMIO_BIOS, &args)) { 289 err(1,"Set resume timer"); 290 } 291#else 292 errx(1,"set resume timer not supported"); 293#endif 294} 295 296int 297main(int argc, char *argv[]) 298{ 299 int c, fd; 300 int sleep = 0, all_info = 1, apm_status = 0, batt_status = 0; 301 int display = 0, batt_life = 0, ac_status = 0, standby = 0; 302 int batt_time = 0, delta = 0; 303 char *cmdname; 304 size_t cmos_wall_len = sizeof(cmos_wall); 305 306 if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len, 307 NULL, 0) == -1) 308 err(1, "sysctlbyname(machdep.wall_cmos_clock)"); 309 if ((cmdname = strrchr(argv[0], '/')) != NULL) 310 cmdname++; 311 else 312 cmdname = argv[0]; 313 314 if (strcmp(cmdname, "zzz") == 0) { 315 sleep = 1; 316 all_info = 0; 317 goto finish_option; 318 } 319 while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) { 320 switch (c) { 321 case 'a': 322 ac_status = 1; 323 all_info = 0; 324 break; 325 case 'b': 326 batt_status = 1; 327 all_info = 0; 328 break; 329 case 'd': 330 display = *optarg - '0'; 331 if (display < 0 || display > 1) { 332 warnx("argument of option '-%c' is invalid", c); 333 usage(); 334 } 335 display++; 336 all_info = 0; 337 break; 338 case 'l': 339 batt_life = 1; 340 all_info = 0; 341 break; 342 case 'R': 343 delta = -1; 344 break; 345 case 'r': 346 delta = atoi(optarg); 347 break; 348 case 's': 349 apm_status = 1; 350 all_info = 0; 351 break; 352 case 't': 353 batt_time = 1; 354 all_info = 0; 355 break; 356 case 'z': 357 sleep = 1; 358 all_info = 0; 359 break; 360 case 'Z': 361 standby = 1; 362 all_info = 0; 363 break; 364 case '?': 365 default: 366 usage(); 367 } 368 argc -= optind; 369 argv += optind; 370 } 371finish_option: 372 fd = open(APMDEV, O_RDWR); 373 if (fd == -1) { 374 warn("can't open %s", APMDEV); 375 return 1; 376 } 377 if (delta) 378 apm_set_timer(fd, delta); 379 if (sleep) 380 apm_suspend(fd); 381 else if (standby) 382 apm_standby(fd); 383 else if (delta == 0) { 384 struct apm_info info; 385 386 apm_getinfo(fd, &info); 387 if (all_info) 388 print_all_info(fd, &info); 389 if (ac_status) 390 printf("%d\n", info.ai_acline); 391 if (batt_status) 392 printf("%d\n", info.ai_batt_stat); 393 if (batt_life) 394 printf("%d\n", info.ai_batt_life); 395 if (apm_status) 396 printf("%d\n", info.ai_status); 397 if (batt_time) 398 printf("%d\n", info.ai_batt_time); 399 if (display) 400 apm_display(fd, display - 1); 401 } 402 close(fd); 403 return 0; 404} 405