13259Sdg/* 2200840Sru * APM BIOS utility for FreeBSD 33259Sdg * 433657Shosokawa * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org> 53259Sdg * 63259Sdg * This software may be used, modified, copied, distributed, and sold, 73259Sdg * in both source and binary form provided that the above copyright and 88857Srgrimes * these terms are retained. Under no circumstances is the author 98857Srgrimes * responsible for the proper functioning of this software, nor does 108857Srgrimes * the author assume any responsibility for damages incurred with its 113259Sdg * use. 123259Sdg * 133259Sdg * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) 143259Sdg */ 153259Sdg 16105157Scharnier#include <sys/cdefs.h> 17105157Scharnier__FBSDID("$FreeBSD$"); 1829030Scharnier 1948938Sgreen#include <sys/file.h> 2048938Sgreen#include <sys/ioctl.h> 2148938Sgreen#include <sys/types.h> 2248938Sgreen#include <sys/sysctl.h> 2348938Sgreen 2448938Sgreen#include <machine/apm_bios.h> 2548938Sgreen 2629030Scharnier#include <err.h> 273259Sdg#include <stdio.h> 2821363Snate#include <stdlib.h> 293259Sdg#include <string.h> 3048938Sgreen#include <time.h> 3121363Snate#include <unistd.h> 323259Sdg 3314674Snate#define APMDEV "/dev/apm" 343259Sdg 35131368Simp#define APM_UNKNOWN 255 36131368Simp 3738809Simp#define xh(a) (((a) & 0xff00) >> 8) 3838809Simp#define xl(a) ((a) & 0xff) 3938809Simp#define APMERR(a) xh(a) 4038809Simp 4138809Simpint cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */ 4238809Simp 43129784Snjlstatic void 44129784Snjlusage(void) 453259Sdg{ 46200840Sru fprintf(stderr, 4756399Sgreen "usage: apm [-ablstzZ] [-d enable ] [ -e enable ] " 48200840Sru "[ -h enable ] [-r delta]\n"); 4921363Snate exit(1); 5021363Snate} 5121363Snate 5256399Sgreen/* 5356399Sgreen * Return 1 for boolean true, and 0 for false, according to the 5456399Sgreen * interpretation of the string argument given. 5556399Sgreen */ 56129784Snjlstatic int 57130476Smuxis_true(const char *boolean) 58130476Smux{ 5956399Sgreen char *endp; 6056399Sgreen long val; 6156399Sgreen 6256399Sgreen val = strtoul(boolean, &endp, 0); 6356399Sgreen if (*endp == '\0') 6456399Sgreen return (val != 0 ? 1 : 0); 6556399Sgreen if (strcasecmp(boolean, "true") == 0 || 6656399Sgreen strcasecmp(boolean, "yes") == 0 || 6756399Sgreen strcasecmp(boolean, "enable") == 0) 6856399Sgreen return (1); 6956399Sgreen if (strcasecmp(boolean, "false") == 0 || 7056399Sgreen strcasecmp(boolean, "no") == 0 || 7156399Sgreen strcasecmp(boolean, "disable") == 0) 7256399Sgreen return (0); 7356399Sgreen /* Well, I have no idea what the user wants, so... */ 7456399Sgreen warnx("invalid boolean argument \"%s\"", boolean); 7556399Sgreen usage(); 7656399Sgreen /* NOTREACHED */ 77129784Snjl 78129784Snjl return (0); 7956399Sgreen} 8056399Sgreen 81129784Snjlstatic int 8238809Simpint2bcd(int i) 8338809Simp{ 8438809Simp int retval = 0; 8538809Simp int base = 0; 8638809Simp 8738809Simp if (i >= 10000) 8838809Simp return -1; 8938809Simp 9038809Simp while (i) { 9138809Simp retval |= (i % 10) << base; 9238809Simp i /= 10; 9338809Simp base += 4; 9438809Simp } 9538809Simp return retval; 9638809Simp} 9738809Simp 98129784Snjlstatic int 9938809Simpbcd2int(int bcd) 10038809Simp{ 10138809Simp int retval = 0; 10248939Sgreen int place = 1; 10338809Simp 10438809Simp if (bcd > 0x9999) 10538809Simp return -1; 10638809Simp 10738809Simp while (bcd) { 10848939Sgreen retval += (bcd & 0xf) * place; 10948939Sgreen bcd >>= 4; 11048939Sgreen place *= 10; 11138809Simp } 11238809Simp return retval; 11338809Simp} 11438809Simp 115129784Snjlstatic void 11621363Snateapm_suspend(int fd) 11721363Snate{ 11829030Scharnier if (ioctl(fd, APMIO_SUSPEND, NULL) == -1) 11956399Sgreen err(1, "ioctl(APMIO_SUSPEND)"); 1203259Sdg} 1213259Sdg 122129784Snjlstatic void 12338809Simpapm_standby(int fd) 12438809Simp{ 12538809Simp if (ioctl(fd, APMIO_STANDBY, NULL) == -1) 12656399Sgreen err(1, "ioctl(APMIO_STANDBY)"); 12738809Simp} 12838809Simp 129129784Snjlstatic void 13021363Snateapm_getinfo(int fd, apm_info_t aip) 1313259Sdg{ 13229030Scharnier if (ioctl(fd, APMIO_GETINFO, aip) == -1) 13356399Sgreen err(1, "ioctl(APMIO_GETINFO)"); 1343259Sdg} 1353259Sdg 136129784Snjlstatic void 137130476Smuxapm_enable(int fd, int enable) 138130476Smux{ 13956399Sgreen if (enable) { 14056399Sgreen if (ioctl(fd, APMIO_ENABLE) == -1) 14156399Sgreen err(1, "ioctl(APMIO_ENABLE)"); 14256399Sgreen } else { 14356399Sgreen if (ioctl(fd, APMIO_DISABLE) == -1) 14456399Sgreen err(1, "ioctl(APMIO_DISABLE)"); 14556399Sgreen } 14656123Sgreen} 14756123Sgreen 148130476Smuxstatic void 149130476Smuxprint_batt_time(int batt_time) 150130476Smux{ 151130476Smux printf("Remaining battery time: "); 152130476Smux if (batt_time == -1) 153130476Smux printf("unknown\n"); 154130476Smux else { 155130476Smux int h, m, s; 156130476Smux 157130476Smux h = batt_time; 158130476Smux s = h % 60; 159130476Smux h /= 60; 160130476Smux m = h % 60; 161130476Smux h /= 60; 162130476Smux printf("%2d:%02d:%02d\n", h, m, s); 163130476Smux } 164130476Smux} 165130476Smux 166130476Smuxstatic void 167130476Smuxprint_batt_life(u_int batt_life) 168130476Smux{ 169130476Smux printf("Remaining battery life: "); 170131368Simp if (batt_life == APM_UNKNOWN) 171130476Smux printf("unknown\n"); 172130476Smux else if (batt_life <= 100) 173130476Smux printf("%d%%\n", batt_life); 174130476Smux else 175130476Smux printf("invalid value (0x%x)\n", batt_life); 176130476Smux} 177130476Smux 178130476Smuxstatic void 179130476Smuxprint_batt_stat(u_int batt_stat) 180130476Smux{ 181130476Smux const char *batt_msg[] = { "high", "low", "critical", "charging" }; 182130476Smux 183130476Smux printf("Battery Status: "); 184131368Simp if (batt_stat == APM_UNKNOWN) 185130476Smux printf("unknown\n"); 186130476Smux else if (batt_stat > 3) 187130476Smux printf("invalid value (0x%x)\n", batt_stat); 188130476Smux else 189130476Smux printf("%s\n", batt_msg[batt_stat]); 190130476Smux} 191130476Smux 192129784Snjlstatic void 19364252Sumeprint_all_info(int fd, apm_info_t aip, int bioscall_available) 1943259Sdg{ 19538809Simp struct apm_bios_arg args; 19638809Simp int apmerr; 197146812Swill const char *line_msg[] = { "off-line", "on-line" , "backup power"}; 19838809Simp 1993259Sdg printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor); 200129784Snjl printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled"); 2013259Sdg printf("AC Line status: "); 202131368Simp if (aip->ai_acline == APM_UNKNOWN) 203130476Smux printf("unknown\n"); 204146812Swill else if (aip->ai_acline > 2) 205130476Smux printf("invalid value (0x%x)\n", aip->ai_acline); 206129784Snjl else 207130476Smux printf("%s\n", line_msg[aip->ai_acline]); 20831127Sjdp 209130476Smux print_batt_stat(aip->ai_batt_stat); 210130476Smux print_batt_life(aip->ai_batt_life); 211130476Smux print_batt_time(aip->ai_batt_time); 212130476Smux 21338809Simp if (aip->ai_infoversion >= 1) { 21438809Simp printf("Number of batteries: "); 215131368Simp if (aip->ai_batteries == ~0U) 21638809Simp printf("unknown\n"); 21764615Sume else { 218129784Snjl u_int i; 21964615Sume struct apm_pwstatus aps; 22064615Sume 22138809Simp printf("%d\n", aip->ai_batteries); 22264615Sume for (i = 0; i < aip->ai_batteries; ++i) { 22364615Sume bzero(&aps, sizeof(aps)); 22464615Sume aps.ap_device = PMDV_BATT0 + i; 22564615Sume if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1) 22664615Sume continue; 22764615Sume printf("Battery %d:\n", i); 228131368Simp if (aps.ap_batt_flag & APM_BATT_NOT_PRESENT) { 22964615Sume printf("not present\n"); 23064615Sume continue; 23164615Sume } 232130604Smux printf("\t"); 233130476Smux print_batt_stat(aps.ap_batt_stat); 234130604Smux printf("\t"); 235130476Smux print_batt_life(aps.ap_batt_life); 236130604Smux printf("\t"); 237130476Smux print_batt_time(aps.ap_batt_time); 23864615Sume } 23964615Sume } 24038809Simp } 24138809Simp 24264252Sume if (bioscall_available) { 24364252Sume /* 24464252Sume * try to get the suspend timer 24564252Sume */ 24664252Sume bzero(&args, sizeof(args)); 24764252Sume args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 24864252Sume args.ebx = PMDV_APMBIOS; 24964252Sume args.ecx = 0x0001; 25064252Sume if (ioctl(fd, APMIO_BIOS, &args)) { 25164252Sume printf("Resume timer: unknown\n"); 25264252Sume } else { 25364252Sume apmerr = APMERR(args.eax); 25464252Sume if (apmerr == 0x0d || apmerr == 0x86) 25564252Sume printf("Resume timer: disabled\n"); 25664252Sume else if (apmerr) 257105157Scharnier warnx( 258105157Scharnier "failed to get the resume timer: APM error0x%x", apmerr); 25964252Sume else { 26064252Sume /* 26164252Sume * OK. We have the time (all bcd). 26264252Sume * CH - seconds 26364252Sume * DH - hours 26464252Sume * DL - minutes 26564252Sume * xh(SI) - month (1-12) 26664252Sume * xl(SI) - day of month (1-31) 26764252Sume * DI - year 26864252Sume */ 26964252Sume struct tm tm; 27064252Sume char buf[1024]; 27164252Sume time_t t; 27238809Simp 27364252Sume tm.tm_sec = bcd2int(xh(args.ecx)); 27464252Sume tm.tm_min = bcd2int(xl(args.edx)); 27564252Sume tm.tm_hour = bcd2int(xh(args.edx)); 27664252Sume tm.tm_mday = bcd2int(xl(args.esi)); 27764252Sume tm.tm_mon = bcd2int(xh(args.esi)) - 1; 27864252Sume tm.tm_year = bcd2int(args.edi) - 1900; 27964252Sume if (cmos_wall) 28064252Sume t = mktime(&tm); 28164252Sume else 28264252Sume t = timegm(&tm); 283117517Srwatson if (t != -1) { 284117517Srwatson tm = *localtime(&t); 285117517Srwatson strftime(buf, sizeof(buf), "%c", &tm); 286117517Srwatson printf("Resume timer: %s\n", buf); 287117517Srwatson } else 288117517Srwatson printf("Resume timer: unknown\n"); 28964252Sume } 29038809Simp } 29164252Sume 29264252Sume /* 29364252Sume * Get the ring indicator resume state 29464252Sume */ 29564252Sume bzero(&args, sizeof(args)); 29664252Sume args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING; 29764252Sume args.ebx = PMDV_APMBIOS; 29864252Sume args.ecx = 0x0002; 29964252Sume if (ioctl(fd, APMIO_BIOS, &args) == 0) { 30064252Sume printf("Resume on ring indicator: %sabled\n", 301102403Simp args.ecx ? "en" : "dis"); 30264252Sume } 30338809Simp } 30438809Simp 30538809Simp if (aip->ai_infoversion >= 1) { 306131368Simp if (aip->ai_capabilities == 0xff00) 307131368Simp return; 308102403Simp printf("APM Capabilities:\n"); 30938809Simp if (aip->ai_capabilities & 0x01) 31038809Simp printf("\tglobal standby state\n"); 31138809Simp if (aip->ai_capabilities & 0x02) 31238809Simp printf("\tglobal suspend state\n"); 31338809Simp if (aip->ai_capabilities & 0x04) 31438809Simp printf("\tresume timer from standby\n"); 31538809Simp if (aip->ai_capabilities & 0x08) 31638809Simp printf("\tresume timer from suspend\n"); 31738809Simp if (aip->ai_capabilities & 0x10) 31838809Simp printf("\tRI resume from standby\n"); 31938809Simp if (aip->ai_capabilities & 0x20) 32038809Simp printf("\tRI resume from suspend\n"); 32138809Simp if (aip->ai_capabilities & 0x40) 32238809Simp printf("\tPCMCIA RI resume from standby\n"); 32338809Simp if (aip->ai_capabilities & 0x80) 32438809Simp printf("\tPCMCIA RI resume from suspend\n"); 32538809Simp } 32638809Simp 3273259Sdg} 3283259Sdg 32921363Snate/* 33021363Snate * currently, it can turn off the display, but the display never comes 33121363Snate * back until the machine suspend/resumes :-). 33221363Snate */ 333129784Snjlstatic void 33421363Snateapm_display(int fd, int newstate) 3353259Sdg{ 33629030Scharnier if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1) 33756399Sgreen err(1, "ioctl(APMIO_DISPLAY)"); 33821363Snate} 3398857Srgrimes 340129784Snjlstatic void 341130476Smuxapm_haltcpu(int fd, int enable) 342130476Smux{ 34356399Sgreen if (enable) { 34456399Sgreen if (ioctl(fd, APMIO_HALTCPU, NULL) == -1) 34556399Sgreen err(1, "ioctl(APMIO_HALTCPU)"); 34656399Sgreen } else { 34756399Sgreen if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1) 34856399Sgreen err(1, "ioctl(APMIO_NOTHALTCPU)"); 34956399Sgreen } 35056399Sgreen} 35156399Sgreen 352129784Snjlstatic void 35338809Simpapm_set_timer(int fd, int delta) 35438809Simp{ 35538809Simp time_t tmr; 35638809Simp struct tm *tm; 35738809Simp struct apm_bios_arg args; 35838809Simp 35938809Simp tmr = time(NULL) + delta; 36038809Simp if (cmos_wall) 36138809Simp tm = localtime(&tmr); 36238809Simp else 36338809Simp tm = gmtime(&tmr); 36438809Simp bzero(&args, sizeof(args)); 36538809Simp args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; 36638809Simp args.ebx = PMDV_APMBIOS; 36738809Simp if (delta > 0) { 36838809Simp args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02; 36938809Simp args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min); 37038809Simp args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday); 37138809Simp args.edi = int2bcd(tm->tm_year + 1900); 37238809Simp } else { 37338809Simp args.ecx = 0x0000; 37438809Simp } 37538809Simp if (ioctl(fd, APMIO_BIOS, &args)) { 376105157Scharnier err(1,"set resume timer"); 37738809Simp } 37838809Simp} 37938809Simp 38021363Snateint 38121363Snatemain(int argc, char *argv[]) 38221363Snate{ 38321363Snate int c, fd; 384129784Snjl int dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0; 38556399Sgreen int display = -1, batt_life = 0, ac_status = 0, standby = 0; 38656399Sgreen int batt_time = 0, delta = 0, enable = -1, haltcpu = -1; 38764252Sume int bioscall_available = 0; 38848938Sgreen size_t cmos_wall_len = sizeof(cmos_wall); 38921363Snate 39048938Sgreen if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len, 39148938Sgreen NULL, 0) == -1) 39248938Sgreen err(1, "sysctlbyname(machdep.wall_cmos_clock)"); 3933259Sdg 39456399Sgreen while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) { 39521363Snate switch (c) { 39621363Snate case 'a': 39721363Snate ac_status = 1; 39821363Snate all_info = 0; 39921363Snate break; 40021363Snate case 'b': 40121363Snate batt_status = 1; 40221363Snate all_info = 0; 40321363Snate break; 40421363Snate case 'd': 40556399Sgreen display = is_true(optarg); 40621363Snate all_info = 0; 40721363Snate break; 40821363Snate case 'l': 40921363Snate batt_life = 1; 41021363Snate all_info = 0; 41121363Snate break; 41238809Simp case 'R': 41338809Simp delta = -1; 41438809Simp break; 41538809Simp case 'r': 41638809Simp delta = atoi(optarg); 41738809Simp break; 41821363Snate case 's': 41921363Snate apm_status = 1; 42021363Snate all_info = 0; 42121363Snate break; 42256123Sgreen case 'e': 42356399Sgreen enable = is_true(optarg); 424100714Ssobomax all_info = 0; 42556123Sgreen break; 42656399Sgreen case 'h': 42756399Sgreen haltcpu = is_true(optarg); 428100714Ssobomax all_info = 0; 42956399Sgreen break; 43031127Sjdp case 't': 43131127Sjdp batt_time = 1; 43231127Sjdp all_info = 0; 43331127Sjdp break; 43421363Snate case 'z': 435129784Snjl dosleep = 1; 43621363Snate all_info = 0; 43721363Snate break; 43838809Simp case 'Z': 43938809Simp standby = 1; 44038809Simp all_info = 0; 44138809Simp break; 44221363Snate case '?': 44321363Snate default: 44421363Snate usage(); 4453259Sdg } 44621363Snate argc -= optind; 44721363Snate argv += optind; 4483259Sdg } 449129784Snjl if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep 450129784Snjl || standby) { 45163495Simp fd = open(APMDEV, O_RDWR); 45264252Sume bioscall_available = 1; 45364252Sume } else if ((fd = open(APMDEV, O_RDWR)) >= 0) 45464252Sume bioscall_available = 1; 45563495Simp else 45663495Simp fd = open(APMDEV, O_RDONLY); 45756399Sgreen if (fd == -1) 45856399Sgreen err(1, "can't open %s", APMDEV); 45956399Sgreen if (enable != -1) 46056399Sgreen apm_enable(fd, enable); 46156399Sgreen if (haltcpu != -1) 46256399Sgreen apm_haltcpu(fd, haltcpu); 46338809Simp if (delta) 46438809Simp apm_set_timer(fd, delta); 465129784Snjl if (dosleep) 4663259Sdg apm_suspend(fd); 46738809Simp else if (standby) 46838809Simp apm_standby(fd); 46938809Simp else if (delta == 0) { 47021363Snate struct apm_info info; 4713259Sdg 4723259Sdg apm_getinfo(fd, &info); 47321363Snate if (all_info) 47464252Sume print_all_info(fd, &info, bioscall_available); 47531127Sjdp if (ac_status) 47631127Sjdp printf("%d\n", info.ai_acline); 47721363Snate if (batt_status) 4783259Sdg printf("%d\n", info.ai_batt_stat); 47921363Snate if (batt_life) 4803259Sdg printf("%d\n", info.ai_batt_life); 48121363Snate if (apm_status) 48214609Snate printf("%d\n", info.ai_status); 48331127Sjdp if (batt_time) 48431127Sjdp printf("%d\n", info.ai_batt_time); 48556399Sgreen if (display != -1) 48656399Sgreen apm_display(fd, display); 4873259Sdg } 4883259Sdg close(fd); 48956399Sgreen exit(0); 4903259Sdg} 491