diff -rN -u old-acpid-1.0.4/acpid.c new-acpid-1.0.4/acpid.c --- old-acpid-1.0.4/acpid.c 2004-10-17 15:33:51.000000000 -0700 +++ new-acpid-1.0.4/acpid.c 2005-09-30 12:16:28.000000000 -0700 @@ -3,6 +3,7 @@ * * Portions Copyright (C) 2000 Andrew Henroid * Portions Copyright (C) 2001 Sun Microsystems (thockin@sun.com) + * Portions Copyright (C) 2004 Wez Furlong * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,20 +53,238 @@ static const char *logfile = ACPI_LOGFILE; static const char *eventfile = ACPI_EVENTFILE; static const char *socketfile = ACPI_SOCKETFILE; +static const char *toshiba_keys = ACPI_TOSHIBA_KEYS; + static int nosocket; static const char *socketgroup; static mode_t socketmode = ACPI_SOCKETMODE; static int foreground; +struct powstate { + /* battery life remaining */ + int remain; + int hours, mins; + int pct; + /* on ac power? */ + int on_ac; + /* name of the battery */ + char batname[8]; + /* design capacity */ + int dcap; + /* last full capacity */ + int fullcap; + /* cap level at which to warn */ + int capwarn; + /* time at which we last warned the user */ + time_t last_warn; +} pows = { 0, }; + +int tosh_get_key(void) +{ + char keybuf[256]; + int n; + int ready, keyval; + int tosh_fd; + + tosh_fd = open(toshiba_keys, O_RDONLY); + if (tosh_fd < 0) + return 0; + + n = read(tosh_fd, keybuf, sizeof(keybuf)); + close(tosh_fd); + + /* remove that button from the queue */ + tosh_fd = open(toshiba_keys, O_WRONLY); + if (tosh_fd >= 0) { + write(tosh_fd, "hotkey_ready:0", sizeof("hotkey_ready:0")-1); + close(tosh_fd); + } + + /* did we get a key ? */ + if (n == 0) + return 0; + + ready = keyval = 0; + if (sscanf(keybuf, " hotkey_ready : %i hotkey : %i", &ready, &keyval) && ready) { + return keyval; + } + + return 0; +} + +static int get_value(const char *filename, const char *thing) +{ + int fd = open(filename, O_RDONLY); + char buf[256]; + int n; + + if (fd < 0) + return -1; + + n = read(fd, buf, sizeof(buf)); + close(fd); + + if (n < 0) + return -1; + + buf[n] = '\0'; + + if (sscanf(buf, thing, &n)) { + return n; + } + + return -1; +} + +static void set_value(const char *filename, const char *thing, int val) +{ + int fd = open(filename, O_WRONLY); + char buf[256]; + int n; + + if (fd < 0) + return; + + n = sprintf(buf, thing, val); + write(fd, buf, n); + close(fd); +} + + +/* probe for, and flush, toshiba key queue */ +int flush_tosh(void) +{ + int t; + + t = open(toshiba_keys, O_RDONLY); + if (t < 0) + return 0; + + fprintf(stderr, "%s: opened %s: will handle hotkey events\n", progname, + toshiba_keys); + + /* now, we need to flush that queue */ + close(t); + + while (tosh_get_key()) + ; + + return 1; +} + +static void check_power_status(void) +{ + char buf[1024]; + int fd, n; + int rate = 0; + int voltage = 0; + double time_left; + char present[16]; + char capstate[16], chgstate[16]; + + sprintf(buf, ACPI_BAT_STATE, pows.batname); + fd = open(buf, O_RDONLY); + if (fd < 0) + return; + + n = read(fd, buf, sizeof(buf)); + buf[n] = 0; + close(fd); + + sscanf(buf, "present: %s capacity state: %s charging state: %s " + "present rate: %i mW remaining capacity: %i mWh " + "present voltage: %i mV", + present, capstate, chgstate, &rate, &pows.remain, &voltage); + + if (rate == 0) { + pows.hours = pows.mins = -1; + pows.pct = -1; + } else { + time_left = (double)pows.remain / (double)rate; + pows.hours = (int)time_left; + pows.mins = (time_left - pows.hours) * 60; + pows.pct = -2; + } + + sprintf(buf, ACPI_BAT_INFO, pows.batname); + fd = open(buf, O_RDONLY); + if (fd >= 0) { + n = read(fd, buf, sizeof(buf)); + buf[n] = 0; + close(fd); + + sscanf(buf, "present: %s design capacity: %i mWh " + "last full capacity: %i mWh battery technology: %s " + "design voltage: %i mV design capacity warning: %i mWh ", + present, &pows.dcap, &pows.fullcap, capstate /* ignored */, + &voltage, &pows.capwarn); + + pows.pct = (pows.remain * 100) / pows.fullcap; + } + + /* if we are at or below the capwarn level, we are going to + * initiate a shutdown */ + if (rate && pows.remain < pows.capwarn && time(NULL) >= pows.last_warn + 60) { + time(&pows.last_warn); + acpid_handle_event("critical low power"); + } else if (rate && pows.hours == 0 && pows.mins <= 15) { + /* if there is less than 15 minutes power, send a warning + * event. Repeat this warning every 5 minutes until we + * hit the capwarn level */ + if (pows.mins == 15 || time(NULL) >= pows.last_warn + (5 * 60)) { + + /* turn down the brightness one notch */ + if (access(ACPI_TOSHIBA_LCD, R_OK|W_OK) == 0) + set_value(ACPI_TOSHIBA_LCD, "brightness:%i", get_value(ACPI_TOSHIBA_LCD, " brightness : %i") - 1); + + sprintf(buf, "warning power %d %d", pows.mins, pows.pct); + acpid_handle_event(buf); + } + time(&pows.last_warn); + } +} + +static void acpid_check_power_event(const char *event) +{ + if (!sscanf(event, "battery %s", pows.batname)) + return; + check_power_status(); +} + +static void handle_key(int keycode) +{ + char keybuf[64]; + + switch (keycode) { + case 0x0140: /* bright down */ + if (access(ACPI_TOSHIBA_LCD, R_OK|W_OK) == 0) + set_value(ACPI_TOSHIBA_LCD, "brightness:%i", get_value(ACPI_TOSHIBA_LCD, " brightness : %i") - 1); + break; + + case 0x0141: /* bright up */ + if (access(ACPI_TOSHIBA_LCD, R_OK|W_OK) == 0) + set_value(ACPI_TOSHIBA_LCD, "brightness:%i", get_value(ACPI_TOSHIBA_LCD, " brightness : %i") + 1); + break; + + default: + /* format it into a button event */ + sprintf(keybuf, "button/toshiba 0x%04x", keycode); + acpid_log("received event \"%s\"\n", keybuf); + acpid_handle_event(keybuf); + //acpid_log("completed event \"%s\"\n", keybuf); + } +} + int main(int argc, char **argv) { int event_fd; int sock_fd; + int have_tosh; /* learn who we really are */ progname = (const char *)strrchr(argv[0], '/'); - progname = progname ? (progname + 1) : argv[0]; + progname = strdup(progname ? (progname + 1) : argv[0]); /* handle the commandline */ handle_cmdline(&argc, &argv); @@ -115,6 +334,9 @@ } #endif + /* for toshiba keys */ + have_tosh = flush_tosh(); + /* open our socket */ if (!nosocket) { sock_fd = ud_create_socket(socketfile); @@ -181,7 +403,7 @@ if (!nosocket) { ar[1].fd = sock_fd; ar[1].events = POLLIN; fds++; } - r = poll(ar, fds, -1); + r = poll(ar, fds, have_tosh >= 0 ? 200 : -1); if (r < 0 && errno == EINTR) { continue; @@ -205,6 +427,7 @@ event = read_line(event_fd); if (event) { acpid_log("received event \"%s\"\n", event); + acpid_check_power_event(event); acpid_handle_event(event); acpid_log("completed event \"%s\"\n", event); } else if (errno == EPIPE) { @@ -244,6 +467,15 @@ creds.pid, creds.uid, creds.gid); acpid_add_client(cli_fd, buf); } + + /* was it a toshiba key ? */ + if (have_tosh) { + int keyval; + + while ((keyval = tosh_get_key())) { + handle_key(keyval); + } + } } clean_exit(EXIT_SUCCESS); diff -rN -u old-acpid-1.0.4/acpid.h new-acpid-1.0.4/acpid.h --- old-acpid-1.0.4/acpid.h 2001-08-17 11:30:31.000000000 -0700 +++ new-acpid-1.0.4/acpid.h 2005-09-30 12:09:00.000000000 -0700 @@ -30,9 +30,15 @@ #define ACPI_PROCDIR "/proc/acpi" #define ACPI_EVENTFILE ACPI_PROCDIR "/event" +#define ACPI_TOSHIBA_KEYS ACPI_PROCDIR "/toshiba/keys" #define ACPI_CONFDIR "/etc/acpi/events" #define ACPI_LOGFILE "/var/log/acpid" #define ACPI_SOCKETFILE "/var/run/acpid.socket" + +#define ACPI_BAT_STATE "/proc/acpi/battery/%s/state" +#define ACPI_BAT_INFO "/proc/acpi/battery/%s/info" +#define ACPI_TOSHIBA_LCD "/proc/acpi/toshiba/lcd" + #define ACPI_SOCKETMODE 0666 #define ACPI_MAX_ERRS 5