/* * apm / zzz APM BIOS utility for FreeBSD * * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org> * * This software may be used, modified, copied, distributed, and sold, * in both source and binary form provided that the above copyright and * these terms are retained. Under no circumstances is the author * responsible for the proper functioning of this software, nor does * the author assume any responsibility for damages incurred with its * use. * * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD) */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <sys/file.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/sysctl.h> #include <machine/apm_bios.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #define APMDEV "/dev/apm" #define APM_UNKNOWN 255 #define xh(a) (((a) & 0xff00) >> 8) #define xl(a) ((a) & 0xff) #define APMERR(a) xh(a) int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */ static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: apm [-ablstzZ] [-d enable ] [ -e enable ] " "[ -h enable ] [-r delta]", " zzz"); exit(1); } /* * Return 1 for boolean true, and 0 for false, according to the * interpretation of the string argument given. */ static int is_true(const char *boolean) { char *endp; long val; val = strtoul(boolean, &endp, 0); if (*endp == '\0') return (val != 0 ? 1 : 0); if (strcasecmp(boolean, "true") == 0 || strcasecmp(boolean, "yes") == 0 || strcasecmp(boolean, "enable") == 0) return (1); if (strcasecmp(boolean, "false") == 0 || strcasecmp(boolean, "no") == 0 || strcasecmp(boolean, "disable") == 0) return (0); /* Well, I have no idea what the user wants, so... */ warnx("invalid boolean argument \"%s\"", boolean); usage(); /* NOTREACHED */ return (0); } static int int2bcd(int i) { int retval = 0; int base = 0; if (i >= 10000) return -1; while (i) { retval |= (i % 10) << base; i /= 10; base += 4; } return retval; } static int bcd2int(int bcd) { int retval = 0; int place = 1; if (bcd > 0x9999) return -1; while (bcd) { retval += (bcd & 0xf) * place; bcd >>= 4; place *= 10; } return retval; } static void apm_suspend(int fd) { if (ioctl(fd, APMIO_SUSPEND, NULL) == -1) err(1, "ioctl(APMIO_SUSPEND)"); } static void apm_standby(int fd) { if (ioctl(fd, APMIO_STANDBY, NULL) == -1) err(1, "ioctl(APMIO_STANDBY)"); } static void apm_getinfo(int fd, apm_info_t aip) { if (ioctl(fd, APMIO_GETINFO, aip) == -1) err(1, "ioctl(APMIO_GETINFO)"); } static void apm_enable(int fd, int enable) { if (enable) { if (ioctl(fd, APMIO_ENABLE) == -1) err(1, "ioctl(APMIO_ENABLE)"); } else { if (ioctl(fd, APMIO_DISABLE) == -1) err(1, "ioctl(APMIO_DISABLE)"); } } static void print_batt_time(int batt_time) { printf("Remaining battery time: "); if (batt_time == -1) printf("unknown\n"); else { int h, m, s; h = batt_time; s = h % 60; h /= 60; m = h % 60; h /= 60; printf("%2d:%02d:%02d\n", h, m, s); } } static void print_batt_life(u_int batt_life) { printf("Remaining battery life: "); if (batt_life == APM_UNKNOWN) printf("unknown\n"); else if (batt_life <= 100) printf("%d%%\n", batt_life); else printf("invalid value (0x%x)\n", batt_life); } static void print_batt_stat(u_int batt_stat) { const char *batt_msg[] = { "high", "low", "critical", "charging" }; printf("Battery Status: "); if (batt_stat == APM_UNKNOWN) printf("unknown\n"); else if (batt_stat > 3) printf("invalid value (0x%x)\n", batt_stat); else printf("%s\n", batt_msg[batt_stat]); } static void print_all_info(int fd, apm_info_t aip, int bioscall_available) { struct apm_bios_arg args; int apmerr; const char *line_msg[] = { "off-line", "on-line" , "backup power"}; printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor); printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled"); printf("AC Line status: "); if (aip->ai_acline == APM_UNKNOWN) printf("unknown\n"); else if (aip->ai_acline > 2) printf("invalid value (0x%x)\n", aip->ai_acline); else printf("%s\n", line_msg[aip->ai_acline]); print_batt_stat(aip->ai_batt_stat); print_batt_life(aip->ai_batt_life); print_batt_time(aip->ai_batt_time); if (aip->ai_infoversion >= 1) { printf("Number of batteries: "); if (aip->ai_batteries == ~0U) printf("unknown\n"); else { u_int i; struct apm_pwstatus aps; printf("%d\n", aip->ai_batteries); for (i = 0; i < aip->ai_batteries; ++i) { bzero(&aps, sizeof(aps)); aps.ap_device = PMDV_BATT0 + i; if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1) continue; printf("Battery %d:\n", i); if (aps.ap_batt_flag & APM_BATT_NOT_PRESENT) { printf("not present\n"); continue; } printf("\t"); print_batt_stat(aps.ap_batt_stat); printf("\t"); print_batt_life(aps.ap_batt_life); printf("\t"); print_batt_time(aps.ap_batt_time); } } } if (bioscall_available) { /* * try to get the suspend timer */ bzero(&args, sizeof(args)); args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; args.ebx = PMDV_APMBIOS; args.ecx = 0x0001; if (ioctl(fd, APMIO_BIOS, &args)) { printf("Resume timer: unknown\n"); } else { apmerr = APMERR(args.eax); if (apmerr == 0x0d || apmerr == 0x86) printf("Resume timer: disabled\n"); else if (apmerr) warnx( "failed to get the resume timer: APM error0x%x", apmerr); else { /* * OK. We have the time (all bcd). * CH - seconds * DH - hours * DL - minutes * xh(SI) - month (1-12) * xl(SI) - day of month (1-31) * DI - year */ struct tm tm; char buf[1024]; time_t t; tm.tm_sec = bcd2int(xh(args.ecx)); tm.tm_min = bcd2int(xl(args.edx)); tm.tm_hour = bcd2int(xh(args.edx)); tm.tm_mday = bcd2int(xl(args.esi)); tm.tm_mon = bcd2int(xh(args.esi)) - 1; tm.tm_year = bcd2int(args.edi) - 1900; if (cmos_wall) t = mktime(&tm); else t = timegm(&tm); if (t != -1) { tm = *localtime(&t); strftime(buf, sizeof(buf), "%c", &tm); printf("Resume timer: %s\n", buf); } else printf("Resume timer: unknown\n"); } } /* * Get the ring indicator resume state */ bzero(&args, sizeof(args)); args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING; args.ebx = PMDV_APMBIOS; args.ecx = 0x0002; if (ioctl(fd, APMIO_BIOS, &args) == 0) { printf("Resume on ring indicator: %sabled\n", args.ecx ? "en" : "dis"); } } if (aip->ai_infoversion >= 1) { if (aip->ai_capabilities == 0xff00) return; printf("APM Capabilities:\n"); if (aip->ai_capabilities & 0x01) printf("\tglobal standby state\n"); if (aip->ai_capabilities & 0x02) printf("\tglobal suspend state\n"); if (aip->ai_capabilities & 0x04) printf("\tresume timer from standby\n"); if (aip->ai_capabilities & 0x08) printf("\tresume timer from suspend\n"); if (aip->ai_capabilities & 0x10) printf("\tRI resume from standby\n"); if (aip->ai_capabilities & 0x20) printf("\tRI resume from suspend\n"); if (aip->ai_capabilities & 0x40) printf("\tPCMCIA RI resume from standby\n"); if (aip->ai_capabilities & 0x80) printf("\tPCMCIA RI resume from suspend\n"); } } /* * currently, it can turn off the display, but the display never comes * back until the machine suspend/resumes :-). */ static void apm_display(int fd, int newstate) { if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1) err(1, "ioctl(APMIO_DISPLAY)"); } static void apm_haltcpu(int fd, int enable) { if (enable) { if (ioctl(fd, APMIO_HALTCPU, NULL) == -1) err(1, "ioctl(APMIO_HALTCPU)"); } else { if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1) err(1, "ioctl(APMIO_NOTHALTCPU)"); } } static void apm_set_timer(int fd, int delta) { time_t tmr; struct tm *tm; struct apm_bios_arg args; tmr = time(NULL) + delta; if (cmos_wall) tm = localtime(&tmr); else tm = gmtime(&tmr); bzero(&args, sizeof(args)); args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER; args.ebx = PMDV_APMBIOS; if (delta > 0) { args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02; args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min); args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday); args.edi = int2bcd(tm->tm_year + 1900); } else { args.ecx = 0x0000; } if (ioctl(fd, APMIO_BIOS, &args)) { err(1,"set resume timer"); } } int main(int argc, char *argv[]) { int c, fd; int dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0; int display = -1, batt_life = 0, ac_status = 0, standby = 0; int batt_time = 0, delta = 0, enable = -1, haltcpu = -1; char *cmdname; int bioscall_available = 0; size_t cmos_wall_len = sizeof(cmos_wall); if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len, NULL, 0) == -1) err(1, "sysctlbyname(machdep.wall_cmos_clock)"); if ((cmdname = strrchr(argv[0], '/')) != NULL) cmdname++; else cmdname = argv[0]; if (strcmp(cmdname, "zzz") == 0) { dosleep = 1; all_info = 0; goto finish_option; } while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) { switch (c) { case 'a': ac_status = 1; all_info = 0; break; case 'b': batt_status = 1; all_info = 0; break; case 'd': display = is_true(optarg); all_info = 0; break; case 'l': batt_life = 1; all_info = 0; break; case 'R': delta = -1; break; case 'r': delta = atoi(optarg); break; case 's': apm_status = 1; all_info = 0; break; case 'e': enable = is_true(optarg); all_info = 0; break; case 'h': haltcpu = is_true(optarg); all_info = 0; break; case 't': batt_time = 1; all_info = 0; break; case 'z': dosleep = 1; all_info = 0; break; case 'Z': standby = 1; all_info = 0; break; case '?': default: usage(); } argc -= optind; argv += optind; } finish_option: if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep || standby) { fd = open(APMDEV, O_RDWR); bioscall_available = 1; } else if ((fd = open(APMDEV, O_RDWR)) >= 0) bioscall_available = 1; else fd = open(APMDEV, O_RDONLY); if (fd == -1) err(1, "can't open %s", APMDEV); if (enable != -1) apm_enable(fd, enable); if (haltcpu != -1) apm_haltcpu(fd, haltcpu); if (delta) apm_set_timer(fd, delta); if (dosleep) apm_suspend(fd); else if (standby) apm_standby(fd); else if (delta == 0) { struct apm_info info; apm_getinfo(fd, &info); if (all_info) print_all_info(fd, &info, bioscall_available); if (ac_status) printf("%d\n", info.ai_acline); if (batt_status) printf("%d\n", info.ai_batt_stat); if (batt_life) printf("%d\n", info.ai_batt_life); if (apm_status) printf("%d\n", info.ai_status); if (batt_time) printf("%d\n", info.ai_batt_time); if (display != -1) apm_display(fd, display); } close(fd); exit(0); }