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