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 "$Id: apm.c,v 1.18 1999/07/30 19:35:21 msmith 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 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 1|0] [-r delta]", 50 " zzz"); 51 exit(1); 52 } 53 54 int 55 int2bcd(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 71 int 72 bcd2int(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 88 void 89 apm_suspend(int fd) 90 { 91 if (ioctl(fd, APMIO_SUSPEND, NULL) == -1) 92 err(1, NULL); 93 } 94 95 void 96 apm_standby(int fd) 97 { 98 if (ioctl(fd, APMIO_STANDBY, NULL) == -1) 99 err(1, NULL); 100 } 101 102 void 103 apm_getinfo(int fd, apm_info_t aip) 104 { 105 if (ioctl(fd, APMIO_GETINFO, aip) == -1) 106 err(1, NULL); 107 } 108 109 void 110 print_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 bzero(&args, sizeof(args)); 172 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 173 args.ebx = PMDV_APMBIOS; 174 args.ecx = 0x0001; 175 if (ioctl(fd, APMIO_BIOS, &args)) { 176 printf("Resume timer: unknown\n"); 177 } else { 178 apmerr = APMERR(args.eax); 179 if (apmerr == 0x0d || apmerr == 0x86) 180 printf("Resume timer: disabled\n"); 181 else if (apmerr) 182 fprintf(stderr, 183 "Failed to get the resume timer: APM error0x%x\n", 184 apmerr); 185 else { 186 /* 187 * OK. We have the time (all bcd). 188 * CH - seconds 189 * DH - hours 190 * DL - minutes 191 * xh(SI) - month (1-12) 192 * xl(SI) - day of month (1-31) 193 * DI - year 194 */ 195 struct tm tm; 196 char buf[1024]; 197 time_t t; 198 199 tm.tm_sec = bcd2int(xh(args.ecx)); 200 tm.tm_min = bcd2int(xl(args.edx)); 201 tm.tm_hour = bcd2int(xh(args.edx)); 202 tm.tm_mday = bcd2int(xl(args.esi)); 203 tm.tm_mon = bcd2int(xh(args.esi)) - 1; 204 tm.tm_year = bcd2int(args.edi) - 1900; 205 if (cmos_wall) 206 t = mktime(&tm); 207 else 208 t = timegm(&tm); 209 tm = *localtime(&t); 210 strftime(buf, sizeof(buf), "%c", &tm); 211 printf("Resume timer: %s\n", buf); 212 } 213 } 214 215 /* 216 * Get the ring indicator resume state 217 */ 218 bzero(&args, sizeof(args)); 219 args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING; 220 args.ebx = PMDV_APMBIOS; 221 args.ecx = 0x0002; 222 if (ioctl(fd, APMIO_BIOS, &args) == 0) { 223 printf("Resume on ring indicator: %sabled\n", 224 args.ecx ? "en" : "dis"); 225 } 226 if (aip->ai_infoversion >= 1) { 227 printf("APM Capacities:\n", aip->ai_capabilities); 228 if (aip->ai_capabilities == 0xff00) 229 printf("\tunknown\n"); 230 if (aip->ai_capabilities & 0x01) 231 printf("\tglobal standby state\n"); 232 if (aip->ai_capabilities & 0x02) 233 printf("\tglobal suspend state\n"); 234 if (aip->ai_capabilities & 0x04) 235 printf("\tresume timer from standby\n"); 236 if (aip->ai_capabilities & 0x08) 237 printf("\tresume timer from suspend\n"); 238 if (aip->ai_capabilities & 0x10) 239 printf("\tRI resume from standby\n"); 240 if (aip->ai_capabilities & 0x20) 241 printf("\tRI resume from suspend\n"); 242 if (aip->ai_capabilities & 0x40) 243 printf("\tPCMCIA RI resume from standby\n"); 244 if (aip->ai_capabilities & 0x80) 245 printf("\tPCMCIA RI resume from suspend\n"); 246 } 247 248 } 249 250 /* 251 * currently, it can turn off the display, but the display never comes 252 * back until the machine suspend/resumes :-). 253 */ 254 void 255 apm_display(int fd, int newstate) 256 { 257 if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1) 258 err(1, NULL); 259 } 260 261 262 void 263 apm_set_timer(int fd, int delta) 264 { 265 time_t tmr; 266 struct tm *tm; 267 struct apm_bios_arg args; 268 269 tmr = time(NULL) + delta; 270 if (cmos_wall) 271 tm = localtime(&tmr); 272 else 273 tm = gmtime(&tmr); 274 bzero(&args, sizeof(args)); 275 args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 276 args.ebx = PMDV_APMBIOS; 277 if (delta > 0) { 278 args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02; 279 args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min); 280 args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday); 281 args.edi = int2bcd(tm->tm_year + 1900); 282 } else { 283 args.ecx = 0x0000; 284 } 285 if (ioctl(fd, APMIO_BIOS, &args)) { 286 err(1,"Set resume timer"); 287 } 288 } 289 290 int 291 main(int argc, char *argv[]) 292 { 293 int c, fd; 294 int sleep = 0, all_info = 1, apm_status = 0, batt_status = 0; 295 int display = 0, batt_life = 0, ac_status = 0, standby = 0; 296 int batt_time = 0, delta = 0; 297 char *cmdname; 298 size_t cmos_wall_len = sizeof(cmos_wall); 299 300 if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len, 301 NULL, 0) == -1) 302 err(1, "sysctlbyname(machdep.wall_cmos_clock)"); 303 if ((cmdname = strrchr(argv[0], '/')) != NULL) 304 cmdname++; 305 else 306 cmdname = argv[0]; 307 308 if (strcmp(cmdname, "zzz") == 0) { 309 sleep = 1; 310 all_info = 0; 311 goto finish_option; 312 } 313 while ((c = getopt(argc, argv, "ablRr:stzd:Z")) != -1) { 314 switch (c) { 315 case 'a': 316 ac_status = 1; 317 all_info = 0; 318 break; 319 case 'b': 320 batt_status = 1; 321 all_info = 0; 322 break; 323 case 'd': 324 display = *optarg - '0'; 325 if (display < 0 || display > 1) { 326 warnx("argument of option '-%c' is invalid", c); 327 usage(); 328 } 329 display++; 330 all_info = 0; 331 break; 332 case 'l': 333 batt_life = 1; 334 all_info = 0; 335 break; 336 case 'R': 337 delta = -1; 338 break; 339 case 'r': 340 delta = atoi(optarg); 341 break; 342 case 's': 343 apm_status = 1; 344 all_info = 0; 345 break; 346 case 't': 347 batt_time = 1; 348 all_info = 0; 349 break; 350 case 'z': 351 sleep = 1; 352 all_info = 0; 353 break; 354 case 'Z': 355 standby = 1; 356 all_info = 0; 357 break; 358 case '?': 359 default: 360 usage(); 361 } 362 argc -= optind; 363 argv += optind; 364 } 365 finish_option: 366 fd = open(APMDEV, O_RDWR); 367 if (fd == -1) { 368 warn("can't open %s", APMDEV); 369 return 1; 370 } 371 if (delta) 372 apm_set_timer(fd, delta); 373 if (sleep) 374 apm_suspend(fd); 375 else if (standby) 376 apm_standby(fd); 377 else if (delta == 0) { 378 struct apm_info info; 379 380 apm_getinfo(fd, &info); 381 if (all_info) 382 print_all_info(fd, &info); 383 if (ac_status) 384 printf("%d\n", info.ai_acline); 385 if (batt_status) 386 printf("%d\n", info.ai_batt_stat); 387 if (batt_life) 388 printf("%d\n", info.ai_batt_life); 389 if (apm_status) 390 printf("%d\n", info.ai_status); 391 if (batt_time) 392 printf("%d\n", info.ai_batt_time); 393 if (display) 394 apm_display(fd, display - 1); 395 } 396 close(fd); 397 return 0; 398 } 399