/* * portable.cpp * * Copyright (c) 1999 Paul Campbell * * Requires the Qt widget libraries, available at no cost at * http://www.troll.no/ * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ // // this file contains the machine specific laptop power management stuff // to add support for your own OS this is should be the only place you need // to change - to add your own stuff insert above the line marked // 'INSERT HERE' : // // #ifdef MY_OS" // .. copy of linux code or whatever you want to use as a base // # else // // then tag an extra '#endif' at the end // // If you have any problems, questions, whatever please get in touch // // Paul Campbell // paul@taniwha.com // // #include #include #include "portable.h" #ifdef __linux__ #include #include #include #include // // here's the Linux specific laptop control panel stuff // typedef struct apm_info { int apm_flags; int ac_line_status; int battery_percentage; int battery_time; } apm_info; static int apm_read(apm_info *ap) { FILE *f; char tmp2[10]; int tmp, s; char version[256]; f = fopen("/proc/apm", "r"); if (f == NULL) return(1); s = fscanf(f, "%255s %d.%d %x %x %x %x %d%% %d %s\n", version, &tmp, &tmp, &ap->apm_flags, &ap->ac_line_status, &tmp, &tmp, &ap->battery_percentage, &ap->battery_time, tmp2); if (s < 9) return(1); if (version[0] == 'B') { fclose(f); return(2); } if (ap->battery_percentage > 100) ap->battery_percentage = -1; if (strcmp(tmp2, "sec") == 0) ap->battery_time /= 60; fclose(f); return(0); } #define ACPI_BATT_LOC 8 static unsigned int num_acpi_batt_loc = ACPI_BATT_LOC; static const char * const batt_info[] = { "/proc/acpi/battery/0/info", "/proc/acpi/battery/1/info", "/proc/acpi/battery/2/info", "/proc/acpi/battery/3/info", "/proc/acpi/battery/BAT0/info", "/proc/acpi/battery/BAT1/info", "/proc/acpi/battery/BAT2/info", "/proc/acpi/battery/BAT3/info", }; static const char * const batt_status[] = { "/proc/acpi/battery/0/status", "/proc/acpi/battery/1/status", "/proc/acpi/battery/2/status", "/proc/acpi/battery/3/status", "/proc/acpi/battery/BAT0/state", "/proc/acpi/battery/BAT1/state", "/proc/acpi/battery/BAT2/state", "/proc/acpi/battery/BAT3/state", }; static unsigned char acpi_batt_ok[ACPI_BATT_LOC]; static unsigned char acpi_ac_ok; static int getint(char *s) { int v = 0, sgn=1; for (;;) { if (*s == 0) return(0); if (*s == ':') break; s++; } s++; for (;;) { if (*s == 0) return(0); if (*s != ' ' && *s != '\t') break; s++; } if (*s == '-') { s++; sgn = -1; } else if (*s == '+') s++; while (*s >= '0' && *s <= '9') { v = (v*10) + (*s-'0'); s++; } return(sgn*v); } // // linux APCI doesn't return usefull stuff like how much TIME is left yet // the 'rate' is not smoothed over time so it's faked out here // it's not pretty and it's a quick estimate // // for the moment we prefer APM // static int acpi_read(apm_info *ap) { FILE *f; int v, i; int rate, part, total; int ret = 1; char buff[1024]; static int ratecount=0; static int ratetotal=0; part = 0; total = 0; rate = 0; for (i = 0; i < num_acpi_batt_loc; i++) if (acpi_batt_ok[i]) { int remaining = 0; int caplow = 0; int cap = 0; int first=1; f = fopen(batt_info[i], "r"); again: if (f == NULL) { if (!first) continue; first = 0; f = fopen(batt_status[i], "r"); goto again; } for (;;) { /* REPADDED: include options in lower case for INTEL ACPI version. */ if (fgets(buff, sizeof(buff), f) == NULL) break; if (strstr(buff, "Design Capacity Low:") != NULL) { caplow = getint(buff); } else if (strstr(buff, "design capacity low:") != NULL) { caplow = getint(buff); } else if (strstr(buff, "present rate:") != NULL) { v = getint(buff); if (v < 0) v = 0; } else if (strstr(buff, "Present Rate:") != NULL) { v = getint(buff); if (v < 0) v = 0; } else if (strstr(buff, "Last Full Capacity:") != NULL) { cap = getint(buff); } else if (strstr(buff, "last full capacity:") != NULL) { cap = getint(buff); } else if (strstr(buff, "Remaining Capacity:") != NULL) { remaining = getint(buff); } else if (strstr(buff, "remaining capacity:") != NULL) { remaining = getint(buff); } } fclose(f); if (first) { first = 0; f = fopen(batt_status[i], "r"); goto again; } ret = 0; if (caplow < 0) caplow = 0; cap -= caplow; if (cap < 0) cap = 0; remaining -= caplow; if (remaining < 0) remaining = 0; total += cap; part += remaining; } if (ratecount == 16) { // about 5 mins with a 20 sec poll ratecount /= 2; ratetotal /= 2; } ratecount++; ratetotal += v; rate = ratetotal/ratecount; ap->battery_percentage = (total==0?0:100*part/total); //ap->battery_time = (rate==0||part==0?0 : part/rate); ap->battery_time = -1; ap->ac_line_status = 0; ap->apm_flags = 0; f = fopen("/proc/acpi/ac_adapter/0/status", "r"); if (!f) f = fopen("/proc/acpi/ac_adapter/ADP0/state", "r"); /* INTEL ACPI version. REPADDED */ if (f) { for (;;) { if (fgets(buff, sizeof(buff), f) == NULL) break; if (strstr(buff, "Status:") != NULL) { if (strstr(buff, "on-line") != NULL) ap->ac_line_status |= 1; break; } /*INTEL ACPI version. REPADDED */ if (strstr(buff, "state:") != NULL) { if (strstr(buff, "on-line") != NULL) ap->ac_line_status |= 1; break; } } fclose(f); } return(ret); } static int apm_no_time; static apm_info apmx = {0,0,0,0}; static int has_apm() { static int init = 0; static int val; if (init) return(val); init = 1; val = 1; apm_no_time=0; if (apm_read(&apmx) || (apmx.apm_flags&0x20)) { val = 0; apm_no_time = 1; } else { apm_no_time = apmx.battery_time < 0; } return(val); } static int has_acpi() { static int init = 0; static int val; int num_batt_found = 0; /* REPADDED: count # batteries */ int i; if (init) return(val); init = 1; val = 0; if (::access("/proc/acpi/ac_adapter/0/status", R_OK) == 0) acpi_ac_ok = 1; /* REPADDED : look for INTEL ACPI location too */ else if (::access("/proc/acpi/ac_adapter/ADP0/state", R_OK) == 0) acpi_ac_ok = 1; for (i = 0; i < num_acpi_batt_loc; i++) { if (::access(batt_status[i], R_OK) == 0 || ::access(batt_info[i], R_OK) == 0) { acpi_batt_ok[i] = 1; num_batt_found++; } } if (acpi_ac_ok && num_batt_found > 0 ) val = 1; return(val); } static int apm_has_time() { return(!apm_no_time); } // // returns 1 if we support power management // int laptop_portable::has_power_management() { if (::has_apm()) return(1); return (::has_acpi()); } // // returns 1 if the BIOS returns the time left in the battery rather than a % of full // int laptop_portable::has_battery_time() { if (::has_acpi()) return(0); return (::apm_has_time()); } // // returns 1 if we can perform a change-to-suspend-mode operation for the user // (has_power_management() has already returned 1) // int laptop_portable::has_suspend() { struct stat s; if (::has_acpi()) return(0); if (stat("/usr/bin/apm", &s) || !(getuid() == 0 || s.st_mode&S_ISUID)) return(0); return(1); } // // returns 1 if we can perform a change-to-standby-mode operation for the user // (has_power_management() has already returned 1) // int laptop_portable::has_standby() { struct stat s; if (::has_acpi()) return(0); if (stat("/usr/bin/apm", &s) || !(getuid() == 0 || s.st_mode&S_ISUID)) return(0); return(1); } // // returns 1 if we can perform a change-to-hibernate-mode for a user // (has_power_management() has already returned 1) [hibernate is the save-to-disk mode // not supported by linux APM] // static int hiber_type = 0; // remember how we hibernate int laptop_portable::has_hibernation() { struct stat s; hiber_type = 0; if (stat("/usr/local/bin/tpctl", &s) == 0 && (getuid() == 0 || s.st_mode&S_ISUID)) { hiber_type = 1; return(1); } return(0); } // // explain to the user what they need to do if has_power_management() returned 0 // to get any software they lack // QLabel *laptop_portable::no_power_management_explanation(QWidget *parent) { if (access("/proc/acpi", F_OK) == 0) { // probably has default kernel ACPI installed QLabel* explain = new QLabel(i18n("Your computer seems to have a partial ACPI installation\nprobably ACPI was enabled, but some of the sub-options\nwere not enabled - you need to enable at least 'AC Adaptor'\nand 'Control Method Battery' and rebuild your kernel"), parent); explain->setMinimumSize(explain->sizeHint()); return(explain); } QLabel* explain = new QLabel(i18n("Your computer doesn't have the Linux APM (Advanced\nPower Management) or ACPI software installed, or doesn't have\nthe APM kernel drivers installed - check out the Linux Laptop-HOWTO\ndocument for information how to install APM\nit is available at http://www.linuxdoc.org/HOWTO/Laptop-HOWTO.html"), parent); explain->setMinimumSize(explain->sizeHint()); return(explain); } // // explain to the user what they need to do to get suspend/resume to work from user mode // QLabel *laptop_portable::how_to_do_suspend_resume(QWidget *parent) { if (::has_apm()) { QLabel* note = new QLabel(i18n("\nIf you make /usr/bin/apm setuid then you will also\nbe able to choose 'suspend' and 'standby' in the\nabove dialog - check out the help button below to\nfind out how to do this"), parent); note->setMinimumSize(note->sizeHint()); return(note); } if (::has_acpi()) { QLabel* note = new QLabel(i18n("\nCurrently ACPI suspend/standby is not supported"), parent); note->setMinimumSize(note->sizeHint()); return(note); } QLabel* note = new QLabel(i18n("\nYour system does not support suspend/standby"), parent); note->setMinimumSize(note->sizeHint()); return(note); } static char tmp0[256], tmp1[256]; static int present=0; static void get_pcmcia_info() { FILE *f = fopen("/var/lib/pcmcia/stab", "r"); if (!f) f = fopen("/var/run/stab", "r"); if (f) { char c, *cp; present = 1; cp = tmp0; for (;;) { c = getc(f); if (c == EOF || c == '\n') break; if (c == ':') { while ((c = getc(f)) == ' ') ; for (;;) { *cp++ = c; c = getc(f); if (c == EOF || c == '\n') break; } break; } } *cp = 0; cp = tmp1; for (;;) { c = getc(f); if (c == EOF) break; if (c == ':') { while ((c = getc(f)) == ' ') ; for (;;) { *cp++ = c; c = getc(f); if (c == EOF || c == '\n') break; } break; } } *cp = 0; fclose(f); } else { present = 0; } } // // pcmcia support - this will be replaced by better - pcmcia support being worked on by // others // QLabel *laptop_portable::pcmcia_info(int x, QWidget *parent) { if (x == 0) get_pcmcia_info(); if (!present) { if (x == 1) return(new QLabel(i18n("No PCMCIA controller detected"), parent)); return(new QLabel(i18n(""), parent)); } else { switch (x) { case 0: return(new QLabel(i18n("Card 0:"), parent)); case 1: return(new QLabel(tmp0, parent)); case 2: return(new QLabel(i18n("Card 1:"), parent)); default:return(new QLabel(tmp1, parent)); } } } // // puts us into standby mode // void laptop_portable::invoke_standby() { ::system("/usr/bin/apm --standby"); } // // puts us into suspend mode // void laptop_portable::invoke_suspend() { ::system("/usr/bin/apm --suspend"); } // // puts us into hibernate mode // void laptop_portable::invoke_hibernation() { switch (hiber_type) { case 1: ::system("/usr/local/bin/tpctl --hibernate"); break; // add other machine specific hibernates here } } // // return current battery state // struct power_result laptop_portable::poll_battery_state() { struct power_result p; apm_info x = {0,0,0,0}; if ((::has_acpi() ? ::acpi_read(&x) : ::apm_read(&x)) || (x.apm_flags&0x20)) { p.powered = 0; p.percentage=0; p.time = 0; } else { p.powered = x.ac_line_status&1; p.percentage = x.battery_percentage; p.time = x.battery_time; } return(p); } // // returns true if any mouse or kdb activity has been detected // int laptop_portable::poll_activity() { static int mouse_count = 0, key_count = 0; int m=0, k = 0; int v; char name[256]; char *cp, *cp2; int *vp; static FILE *procint = 0; if (procint == 0) { procint = fopen("/proc/interrupts", "r"); if (procint != 0) return(0); poll_activity(); // initialise statics return(1); } ::rewind(procint); for (;;) { if (::fgets(name, sizeof(name), procint) == 0) break; vp = 0; if (strstr(name, "Mouse") || strstr(name, "mouse")) { vp = &m; } else if (strstr(name, "Keyboard") || strstr(name, "keyboard")) vp = &k; if (vp == 0) continue; v = 0; for (cp = name;*cp;cp++) { if (*cp != ':') continue; cp++; for (;;) { for(;;cp++) { if (*cp != ' ' && *cp != '\t') break; } if (*cp < '0' || *cp > '9') break; cp2 = cp; while (*cp >= '0' && *cp <= '9') cp++; *cp++ = 0; v += atoi(cp2); } break; } if (v > *vp) *vp = v; } v = k != key_count || m != mouse_count; key_count = k; mouse_count = m; return(v); } #elif __FreeBSD__ #include #include #include #include #include #include #define APMDEV "/dev/apm" // FreeBSD support by yours truely. Yay. // Actually this code was "adapted" from apm(8) from // FreeBSD's collection of tools. The orignal apm program // was pieced together by Tatsumi Hosokawa in 1994 // // returns 1 if we support power management // #include int laptop_portable::has_power_management() { int ret, fd = ::open(APMDEV, O_RDWR); if (fd == -1) { return 0; } struct apm_info info; ret=ioctl(fd, APMIO_GETINFO, &info); ::close(fd); if (ret == -1) { return 0; } return info.ai_status; } // // returns 1 if the BIOS returns the time left in the battery rather than a % of full // int laptop_portable::has_battery_time() { int ret, fd = ::open(APMDEV, O_RDWR); if (fd == -1) return 0; struct apm_info info; ret=ioctl(fd, APMIO_GETINFO, &info); ::close(fd); if (ret == -1) return 0; return (info.ai_batt_time != 0xffff); } // // returns 1 if we can perform a change-to-suspend-mode operation for the user // (has_power_management() has already returned 1) // int laptop_portable::has_suspend() { int ret, fd = ::open(APMDEV, O_RDWR); if (fd == -1) return 0; struct apm_info info; ret=ioctl(fd, APMIO_GETINFO, &info); ::close(fd); if (ret == -1) return 0; return (info.ai_capabilities & 0x02); } // // returns 1 if we can perform a change-to-standby-mode operation for the user // (has_power_management() has already returned 1) // int laptop_portable::has_standby() { int ret, fd = ::open(APMDEV, O_RDWR); if (fd == -1) return 0; struct apm_info info; ret=ioctl(fd, APMIO_GETINFO, &info); ::close(fd); if (ret == -1) return 0; return (info.ai_capabilities & 0x01); } // // returns 1 if we can perform a change-to-hibernate-mode for a user // (has_power_management() has already returned 1) [hibernate is the save-to-disk mode // not supported by linux - different laptops have their own - the first here is for // a ThinkPad] // static int hiber_type = 0; // remember how we hibernate int laptop_portable::has_hibernation() { struct stat s; hiber_type = 0; if (stat("/usr/local/bin/tpctl", &s) == 0 && (getuid() == 0 || s.st_mode&S_ISUID)) { hiber_type = 1; // ThinkPad return(1); } return(0); } // // explain to the user what they need to do if has_power_management() returned 0 // to get any software they lack // QLabel *laptop_portable::no_power_management_explanation(QWidget *parent) { int fd; QLabel *explain; fd = ::open(APMDEV, O_RDWR); if (fd == -1) { switch (errno) { case ENOENT: explain = new QLabel("There is no /dev/apm file on this system. Pleae review the FreeBSD handbook on how to create a device node for the apm device driver (man 4 apm)", parent); break; case EACCES: explain = new QLabel("Your system has the proper device node for apm support, however you can't access it. If you're root right now, you've got a problem, otherwise contact your local sysadmin and beg for read/write access to /dev/apm.", parent); break; case ENXIO: explain = new QLabel("Your kernel lacks support for Advanced Power Managment.", parent); break; break; default: explain = new QLabel("There was some generic error while opening /dev/apm. Contact your local supermarket, there's a blue light special on FreeBSD, really.", parent); break; } } else { close(fd); explain = new QLabel("APM has most likely been disabled. Oops", parent); } explain->setMinimumSize(explain->sizeHint()); return(explain); } // // explain to the user what they need to do to get suspend/resume to work from user mode // QLabel *laptop_portable::how_to_do_suspend_resume(QWidget *parent) { QLabel* note = new QLabel(i18n(" "), parent); note->setMinimumSize(note->sizeHint()); return(note); } // // pcmcia support - this will be replaced by better - pcmcia support being worked on by // others // QLabel *laptop_portable::pcmcia_info(int x, QWidget *parent) { if (x == 0) return(new QLabel(i18n("No PCMCIA controller detected"), parent)); return(new QLabel(i18n(""), parent)); } // // puts us into standby mode // void laptop_portable::invoke_standby() { int fd = ::open(APMDEV, O_RDWR); if (fd == -1) return; ioctl(fd, APMIO_STANDBY, NULL); ::close(fd); return; } // // puts us into suspend mode // void laptop_portable::invoke_suspend() { int fd = ::open(APMDEV, O_RDWR); if (fd == -1) return; ioctl(fd, APMIO_SUSPEND, NULL); ::close(fd); return; } // // puts us into hibernate mode // void laptop_portable::invoke_hibernation() { switch (hiber_type) { case 1: ::system("/usr/local/bin/tpctl --hibernate"); break; // add other macheine specific hibernates here } } // // return current battery state // struct power_result laptop_portable::poll_battery_state() { struct power_result p; int ret; int fd = ::open(APMDEV, O_RDWR); if (fd == -1) goto bad; struct apm_info info; ret=ioctl(fd, APMIO_GETINFO, &info); ::close(fd); if (ret == -1) goto bad; p.powered = info.ai_acline; p.percentage = (info.ai_batt_life==255 ? 100 : info.ai_batt_life); p.time = (info.ai_batt_time != 0xffff ? info.ai_batt_time/60 : -1); return(p); bad: p.powered = 1; p.percentage = 100; p.time = 0; return(p); } // // // returns true if any mouse or kdb activity has been detected // int laptop_portable::poll_activity() { return(1); } #else // INSERT HERE // // returns 1 if we support power management // int laptop_portable::has_power_management() { return(0); } // // returns 1 if the BIOS returns the time left in the battery rather than a % of full // int laptop_portable::has_battery_time() { return (0); } // // returns 1 if we can perform a change-to-suspend-mode operation for the user // (has_power_management() has already returned 1) // int laptop_portable::has_suspend() { return(0); } // // returns 1 if we can perform a change-to-standby-mode operation for the user // (has_power_management() has already returned 1) // int laptop_portable::has_standby() { return(0); } // // returns 1 if we can perform a change-to-hibernate-mode for a user // (has_power_management() has already returned 1) [hibernate is the save-to-disk mode // not supported by linux] // int laptop_portable::has_hibernation() { return(0); } // // explain to the user what they need to do if has_power_management() returned 0 // to get any software they lack // QLabel *laptop_portable::no_power_management_explanation(QWidget *parent) { QLabel* explain = new QLabel(i18n("Your computer or operating system is not supported by the current version of the\nKDE laptop control panels. If you want help porting these panels to work with it\nplease contact paul@taniwha.com."), parent); explain->setMinimumSize(explain->sizeHint()); return(explain); } // // explain to the user what they need to do to get suspend/resume to work from user mode // QLabel *laptop_portable::how_to_do_suspend_resume(QWidget *parent) { QLabel* note = new QLabel(i18n(" "), parent); note->setMinimumSize(note->sizeHint()); return(note); } // // pcmcia support - this will be replaced by better - pcmcia support being worked on by // others // QLabel *laptop_portable::pcmcia_info(int x, QWidget *parent) { if (x == 0) return(new QLabel(i18n("No PCMCIA controller detected"), parent)); return(new QLabel(i18n(""), parent)); } // // puts us into standby mode // void laptop_portable::invoke_standby() { } // // puts us into suspend mode // void laptop_portable::invoke_suspend() { } // // puts us into hibernate mode // void laptop_portable::invoke_hibernation() { } // // return current battery state // struct power_result laptop_portable::poll_battery_state() { struct power_result p; p.powered = 0; p.percentage = 0; p.time = 0; return(p); } // // returns true if any mouse or kdb activity has been detected // int laptop_portable::poll_activity() { return(1); } #endif