aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/apm/apm.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/apm/apm.c')
-rw-r--r--usr.sbin/apm/apm.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/usr.sbin/apm/apm.c b/usr.sbin/apm/apm.c
new file mode 100644
index 000000000000..01e75eb1f8b1
--- /dev/null
+++ b/usr.sbin/apm/apm.c
@@ -0,0 +1,488 @@
+/*
+ * 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>
+#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)
+
+static int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
+ "[ -h enable ] [-r delta]\n");
+ 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;
+ 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)");
+
+ 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;
+ }
+ 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);
+}