diff options
Diffstat (limited to 'battstat/apmlib/apmlib.c')
-rw-r--r-- | battstat/apmlib/apmlib.c | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/battstat/apmlib/apmlib.c b/battstat/apmlib/apmlib.c new file mode 100644 index 00000000..bf72e348 --- /dev/null +++ b/battstat/apmlib/apmlib.c @@ -0,0 +1,465 @@ +/* apmlib.c -- Sample APM interface routines + * Created: Mon Jan 8 10:28:16 1996 by [email protected] + * Revised: Fri Dec 26 21:38:29 1997 by [email protected] + * Copyright 1996, 1997 Rickard E. Faith ([email protected]) + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published + * by the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <ctype.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/sysmacros.h> +#include "apm.h" + +#define BACKWARD_COMPAT 1 + +/* If APM support of the right version exists in kernel, return zero. + * Otherwise, return 1 if no support exists, or 2 if it is the wrong + * version. *NOTE* The sense of the return value is not intuitive. + */ +int apm_exists(void) +{ + apm_info i; + + if (access(APM_PROC, R_OK)) + return 1; + return apm_read(&i); +} + + +/* Read information from /proc/apm. Return 0 on success, 1 if APM not + * installed, 2 if APM installed, but old version. + */ +int apm_read(apm_info * i) +{ + FILE *str; + char units[10]; + char buffer[100]; + int retcode = 0; + + if (!(str = fopen(APM_PROC, "r"))) + return 1; + fgets(buffer, sizeof(buffer) - 1, str); + buffer[sizeof(buffer) - 1] = '\0'; + + /* Should check for other driver versions; driver 1.9 (and some + * others) uses this format, which doesn't expose # batteries. + */ + sscanf(buffer, "%s %d.%d %x %x %x %x %d%% %d %s\n", + (char *) i->driver_version, + &i->apm_version_major, + &i->apm_version_minor, + &i->apm_flags, + &i->ac_line_status, + &i->battery_status, + &i->battery_flags, + &i->battery_percentage, + &i->battery_time, + units); + i->using_minutes = !strncmp(units, "min", 3) ? 1 : 0; + if (i->driver_version[0] == 'B') + { /* old style. argh. */ +#if !BACKWARD_COMPAT + retcode = 2; +#else + strcpy((char *) i->driver_version, "pre-0.7"); + i->apm_version_major = 0; + i->apm_version_minor = 0; + i->apm_flags = 0; + i->ac_line_status = 0xff; + i->battery_status = 0xff; + i->battery_flags = 0xff; + i->battery_percentage = -1; + i->battery_time = -1; + i->using_minutes = 1; + + sscanf(buffer, "BIOS version: %d.%d", + &i->apm_version_major, &i->apm_version_minor); + fgets(buffer, sizeof(buffer) - 1, str); + sscanf(buffer, "Flags: 0x%02x", &i->apm_flags); + if (i->apm_flags & APM_32_BIT_SUPPORT) + { + fgets(buffer, sizeof(buffer) - 1, str); + fgets(buffer, sizeof(buffer) - 1, str); + if (buffer[0] != 'P') + { + if (!strncmp(buffer + 4, "off line", 8)) + i->ac_line_status = 0; + else if (!strncmp(buffer + 4, "on line", 7)) + i->ac_line_status = 1; + else if (!strncmp(buffer + 4, "on back", 7)) + i->ac_line_status = 2; + + fgets(buffer, sizeof(buffer) - 1, str); + if (!strncmp(buffer + 16, "high", 4)) + i->battery_status = 0; + else if (!strncmp(buffer + 16, "low", 3)) + i->battery_status = 1; + else if (!strncmp(buffer + 16, "crit", 4)) + i->battery_status = 2; + else if (!strncmp(buffer + 16, "charg", 5)) + i->battery_status = 3; + + fgets(buffer, sizeof(buffer) - 1, str); + if (strncmp(buffer + 14, "unknown", 7)) + i->battery_percentage = atoi(buffer + 14); + if (i->apm_version_major >= 1 && i->apm_version_minor >= 1) + { + fgets(buffer, sizeof(buffer) - 1, str); + sscanf(buffer, "Battery flag: 0x%02x", &i->battery_flags); + + fgets(buffer, sizeof(buffer) - 1, str); + if (strncmp(buffer + 14, "unknown", 7)) + i->battery_time = atoi(buffer + 14); + } + } + } +#endif + } + + /* Fix possible kernel bug -- percentage + * set to 0xff (==255) instead of -1. + */ + if (i->battery_percentage > 100) + i->battery_percentage = -1; + + fclose(str); + return retcode; +} + + +/* Lookup the device number for the apm_bios device. */ +dev_t apm_dev(void) +{ + FILE *str; + static int cached = -1; + char buf[80]; + char *pt; + apm_info i; + + if (cached >= 0) + return cached; + + if (access(APM_PROC, R_OK) || apm_read(&i) == 1) + return cached = -1; + if (i.driver_version[0] == '1') + return cached = makedev(10, 134); + + if (!(str = fopen(APM_DEV, "r"))) + return -1; + while (fgets(buf, sizeof(buf) - 1, str)) + { + buf[sizeof(buf) - 1] = '\0'; + for (pt = buf; *pt && isspace(*pt); ++pt); /* skip leading spaces */ + for (; *pt && !isspace(*pt); ++pt); /* find next space */ + if (isspace(*pt)) + { + *pt++ = '\0'; + pt[strlen(pt) - 1] = '\0'; /* get rid of newline */ + if (!strcmp(pt, APM_NAME)) + { + fclose(str); + return cached = makedev(atoi(buf), 0); + } + } + } + fclose(str); + return cached = -1; +} + + +/* Return a file descriptor for the apm_bios device, or -1 if there is an + * error. Is this method secure? Should we make the device in /dev + * instead of /tmp? + * + * apenwarr 2001/05/11: just throw out the weird temporary device file stuff. + * It was only for ancient kernel versions anyway. + */ +int apm_open(void) +{ + int fd; + apm_info i; + + if (access(APM_PROC, R_OK) || apm_read(&i) == 1) + return -1; + if (i.driver_version[0] >= '1') + { + if ((fd = open(APM_DEVICE, O_RDWR)) < 0) + { + /* Try to create it. This is reasonable + * for backward compatibility. + */ + if (mknod(APM_DEVICE, S_IFCHR | S_IRUSR | S_IWUSR, apm_dev())) + { + unlink(APM_DEVICE); + return -1; + } + fd = open(APM_DEVICE, O_RDWR); + } + + return fd; + } + + return -1; +} + + +/* Given a file descriptor for the apm_bios device, close it. */ +int apm_close(int fd) +{ + return close(fd); +} + +/* Given a file descriptor for the apm_bios device, this routine will wait + * timeout seconds for APM events. Up to n events will be placed in the + * events queue. The return code will indicate the number of events + * stored. Since this routine uses select(2), it will return if an + * unblocked signal is caught. A timeout < 0 means to block indefinately. + * + * Note that if you read a request to standby or to suspend, the kernel + * will be waiting for you to respond to it with a call to apm_suspend() + * or to apm_standby() ! + */ +int apm_get_events(int fd, int timeout, apm_event_t * events, int n) +{ + int retcode; + fd_set fds; + struct timeval t; + + t.tv_sec = timeout; + t.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + retcode = select(fd + 1, &fds, NULL, NULL, timeout < 0 ? NULL : &t); + if (retcode <= 0) + return 0; + return read(fd, events, n * sizeof(apm_event_t)) / sizeof(apm_event_t); +} + + +/* Try to set the Power State to Suspend. */ +int apm_suspend(int fd) +{ + sync(); + return ioctl(fd, APM_IOC_SUSPEND, NULL); +} + + +/* Try to set the Power State to Standby. */ +int apm_standby(int fd) +{ + sync(); + return ioctl(fd, APM_IOC_STANDBY, NULL); +} + +/* Return the last error code generated by the kernel APM driver */ +unsigned int apm_last_error( int fd ) +{ + int err = 0; + +#ifdef APM_IOC_LAST_ERROR + int ierr = 0; + if ( (ierr = ioctl( fd, APM_IOC_LAST_ERROR, &err)) ) + return ierr; +#endif + return err; +} + +/* Define lookup table for error messages */ +typedef struct lookup_t { + int key; + char * msg; +} lookup_t; + +/* APM error messages, arranged by error code */ +static const lookup_t error_table[] = { +/* N/A { APM_SUCCESS, "Operation succeeded" }, */ + { APM_DISABLED, "Power management disabled" }, + { APM_CONNECTED, "Real mode interface already connected" }, + { APM_NOT_CONNECTED, "Interface not connected" }, + { APM_16_CONNECTED, "16 bit interface already connected" }, +/* N/A { APM_16_UNSUPPORTED, "16 bit interface not supported" }, */ + { APM_32_CONNECTED, "32 bit interface already connected" }, + { APM_32_UNSUPPORTED, "32 bit interface not supported" }, + { APM_BAD_DEVICE, "Unrecognized device ID" }, + { APM_BAD_PARAM, "Parameter out of range" }, + { APM_NOT_ENGAGED, "Interface not engaged" }, +#ifdef APM_BAD_FUNCTION + { APM_BAD_FUNCTION, "Function not supported" }, +#endif +#ifdef APM_RESUME_DISABLED + { APM_RESUME_DISABLED, "Resume timer disabled" }, +#endif + { APM_BAD_STATE, "Unable to enter requested state" }, +/* N/A { APM_NO_EVENTS, "No events pending" }, */ + { APM_NOT_PRESENT, "No APM present" } +}; +#define ERROR_COUNT (sizeof(error_table)/sizeof(lookup_t)) + +/* Return character string describing error messages from APM kernel */ +const char *apm_error_name( unsigned int err ) +{ + int i; + + for(i=0; i<ERROR_COUNT; i++) + if(err == error_table[i].key) return(error_table[i].msg); + + return "Unknown error"; +} + +int apm_reject( int fd ) +{ +#ifdef APM_IOC_REJECT + if ( ioctl( fd, APM_IOC_REJECT, NULL ) ) + return apm_last_error( fd ); + else +#endif + return 0; +} + +#ifdef APM_IOC_IGNORE /* detect kernel support of IGNORE/NOIGNORE functions */ +int apm_set_ignore(int fd, int mode) +/* Ignore Standby. */ +{ + if (mode == IGNORE) + { + printf("Telling kernel to ignore system standby/suspend mode\n"); + return ioctl(fd, APM_IOC_IGNORE, NULL); + } + else + { + printf("Telling kernel not to ignore system standby/suspend mode\n"); + return ioctl(fd, APM_IOC_NOIGNORE, NULL); + } + printf("NOTE: User-generated suspend/standby requests are not ignored\n"); +} +#endif + +/* Return a string describing the event. From p. 16 of the Intel/Microsoft + * Advanded Power Management (APM) BIOS Interface Specification, Revision + * 1.1 (September 1993). Intel Order Number: 241704-001. Microsoft Part + * Number: 781-110-X01. + * + * Updated to APM BIOS 1.2 spec (February 1996). Available on-line. + */ +const char *apm_event_name(apm_event_t event) +{ + switch (event) + { + case APM_SYS_STANDBY: + return "System Standby Request"; + case APM_SYS_SUSPEND: + return "System Suspend Request"; + case APM_NORMAL_RESUME: + return "Normal Resume System"; + case APM_CRITICAL_RESUME: + return "Critical Resume System"; + case APM_LOW_BATTERY: + return "Battery Low"; + case APM_POWER_STATUS_CHANGE: + return "Power Status Change"; + case APM_UPDATE_TIME: + return "Update Time"; + case APM_CRITICAL_SUSPEND: + return "Critical Suspend"; + case APM_USER_STANDBY: + return "User System Standby Request"; + case APM_USER_SUSPEND: + return "User System Suspend Request"; + case APM_STANDBY_RESUME: + return "System Standby Resume"; +#ifdef APM_CAPABILITY_CHANGE + case APM_CAPABILITY_CHANGE: + return "Capability Change"; +#endif + } + return "Unknown"; +} + + +/* This is a convenience function that has nothing to do with APM. It just + * formats a time nicely. If you don't like this format, then write your + * own. + */ +#define SEC_PER_DAY (60*60*24) +#define SEC_PER_HOUR (60*60) +#define SEC_PER_MIN (60) + +const char *apm_delta_time(time_t then, time_t now) +{ + return apm_time(now - then); +} + +const char *apm_time(time_t t) +{ + static char buffer[128]; + unsigned long s, m, h, d; + + d = t / SEC_PER_DAY; + t -= d * SEC_PER_DAY; + h = t / SEC_PER_HOUR; + t -= h * SEC_PER_HOUR; + m = t / SEC_PER_MIN; + t -= m * SEC_PER_MIN; + s = t; + + if (d) + sprintf(buffer, "%lu day%s, %02lu:%02lu:%02lu", + d, d > 1 ? "s" : "", h, m, s); + else + sprintf(buffer, "%02lu:%02lu:%02lu", h, m, s); + + if (t == -1) + sprintf(buffer, "unknown"); + + return buffer; +} + +const char *apm_time_nosec(time_t t) +{ + static char buffer[128]; + unsigned long s, m, h, d; + + d = t / SEC_PER_DAY; + t -= d * SEC_PER_DAY; + h = t / SEC_PER_HOUR; + t -= h * SEC_PER_HOUR; + m = t / SEC_PER_MIN; + t -= m * SEC_PER_MIN; + s = t; + + if (s > 30) + ++m; + + if (d) + sprintf(buffer, "%lu day%s, %lu:%02lu", + d, d > 1 ? "s" : "", h, m); + else + sprintf(buffer, "%lu:%02lu", h, m); + + if (t == -1) + sprintf(buffer, "unknown"); + + return buffer; +} |