/* apmlib.c -- Sample APM interface routines * Created: Mon Jan 8 10:28:16 1996 by faith@acm.org * Revised: Fri Dec 26 21:38:29 1997 by faith@acm.org * Copyright 1996, 1997 Rickard E. Faith (faith@acm.org) * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #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; if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); 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); if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); sscanf(buffer, "Flags: 0x%02x", &i->apm_flags); if (i->apm_flags & APM_32_BIT_SUPPORT) { if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); 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; if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); 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; if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); if (strncmp(buffer + 14, "unknown", 7)) i->battery_percentage = atoi(buffer + 14); if (i->apm_version_major >= 1 && i->apm_version_minor >= 1) { if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); sscanf(buffer, "Battery flag: 0x%02x", &i->battery_flags); if (fgets(buffer, sizeof(buffer) - 1, str) == NULL) printf("fgets error\n"); 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 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; }