• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdeutils
  • Sitemap
  • Contact Us
 

klaptopdaemon

portable.cpp

Go to the documentation of this file.
00001  /*
00002  * portable.cpp
00003  *
00004  * $Id: portable.cpp 701121 2007-08-17 11:24:48Z amth $
00005  *
00006  * Copyright (c) 1999 Paul Campbell <paul@taniwha.com>
00007  *
00008  * Requires the Qt widget libraries, available at no cost at
00009  * http://www.troll.no/
00010  *
00011  *  This program is free software; you can redistribute it and/or modify
00012  *  it under the terms of the GNU General Public License as published by
00013  *  the Free Software Foundation; either version 2 of the License, or
00014  *  (at your option) any later version.
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU General Public License for more details.
00020  *
00021  *  You should have received a copy of the GNU General Public License
00022  *  along with this program; if not, write to the Free Software
00023  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024  */
00025 
00026 //
00027 //  this file contains the machine specific laptop power management stuff
00028 //  to add support for your own OS this is should be the only place you need
00029 //  to change - to add your own stuff insert above the line marked
00030 //  'INSERT HERE' :
00031 //
00032 //      #ifdef MY_OS"
00033 //          .. copy of linux code or whatever you want to use as a base
00034 //      # else
00035 //
00036 //  then tag an extra '#endif' at the end
00037 //
00038 // There is support for the following OSsen right now:
00039 //
00040 //  Linux       (#if __linux__)
00041 //  FreeBSD     (#elif __FreeBSD__)
00042 //  NetBSD      (#elif __NetBSD_APM__)
00043 //  generic nothing (#else)
00044 //
00045 //
00046 // The code here is written in a C rather than C++ like to encourage
00047 // people more used to kernel types to do stuff here
00048 //
00049 //  If you have any problems, questions, whatever please get in touch
00050 //
00051 //      Paul Campbell
00052 //      paul@taniwha.com
00053 //
00054 //
00055 //  Thanks to Cajus Pollmeier <C.Pollmeier@gmx.net>
00056 //  and Robert Ellis Parrott <parrott@fas.harvard.edu>
00057 //  who both provided ACPI support
00058 //  and Volker Krause <volker.krause@rwth-aachen.de> who provided ACPI bug fixes
00059 //
00060 
00061 #include <config-klaptopdaemon.h>
00062 #include <klocale.h>
00063 #include <kdebug.h>
00064 #include <stdio.h>
00065 #include "portable.h"
00066 
00067 #ifdef __linux__
00068 
00069 /*
00070 ** This is the Linux-specific laptop code.
00071 */
00072 #include <sys/types.h>
00073 #include <unistd.h>
00074 #include <sys/stat.h>
00075 #include <time.h>
00076 #include <stdlib.h>
00077 #include <math.h>
00078 #include <fcntl.h>
00079 #include <sys/ioctl.h>
00080 #include <dirent.h>
00081 #include <QPushButton>
00082 #include <QDir>
00083 #include <QFile>
00084 #include <qfileinfo.h>
00085 #include <qstringlist.h>
00086 
00087 #include <QObject>
00088 #include <QRegExp>
00089 #include <qiodevice.h>
00090 #include <QLayout>
00091 #include <q3valuevector.h>
00092 //Added by qt3to4:
00093 #include <QLabel>
00094 #include <QVBoxLayout>
00095 
00096 #include <k3activelabel.h>
00097 #include <kconfig.h>
00098 #include <kstandarddirs.h>
00099 #include <kprocess.h>
00100 #include <krichtextlabel.h>
00101 
00102 // ibm specific stuff
00103 extern "C"{
00104 #include "thinkpad_common.h"
00105 #include "smapi.h"
00106 #include "smapidev.h"
00107 }
00108 
00109 //
00110 //  here's the Linux specific laptop control panel stuff
00111 //
00112 
00113 typedef struct apm_info {
00114    unsigned int apm_flags;
00115    unsigned int ac_line_status;
00116    int          battery_percentage;
00117    int          battery_time;
00118 } apm_info;
00119 
00120 static int
00121 apm_read(apm_info *ap)
00122 {
00123     FILE    *f = 0;
00124     char    tmp2[10];
00125     int tmp, s;
00126     unsigned int utmp;
00127     char    version[256];
00128 
00129     f = fopen("/proc/apm", "r");
00130     if (f == NULL)
00131         return(1);
00132     s = fscanf(f, "%255s %d.%d %x %x %x %x %d%% %d %s\n",
00133         version,
00134         &tmp,
00135         &tmp,
00136         &ap->apm_flags,
00137         &ap->ac_line_status,
00138         &utmp,
00139         &utmp,
00140         &ap->battery_percentage,
00141         &ap->battery_time,
00142         tmp2);
00143     if (s < 9) {
00144         fclose(f);
00145         return(1);
00146         }
00147     if (version[0] == 'B') {
00148         fclose(f);
00149         return(2);
00150     }
00151     if (ap->battery_percentage > 100)
00152         ap->battery_percentage = -1;
00153     if (strcmp(tmp2, "sec") == 0)
00154         ap->battery_time /= 60;
00155     fclose(f);
00156     return(0);
00157 }
00158 
00159 
00160 // 0 => unknown
00161 // 1 => have it
00162 // -1 => don't have it
00163 static int pmustate = 0;
00164 
00165 static bool
00166 have_pmu(void)
00167 {
00168     if (pmustate != 0)
00169         return (pmustate == 1);
00170     if (!access("/proc/pmu", R_OK|X_OK)) {
00171         kDebug() << "Found powermac PMU.  Using that." ;
00172         pmustate = 1;
00173         return true;
00174     }
00175     pmustate = -1;
00176     return false;
00177 }
00178 
00179 /* Only supports 1 battery right now - all batteries are merged into one stat */
00180 static int
00181 pmu_read(apm_info *ap)
00182 {
00183     int bcnt = 0;
00184     memset(ap, 0, sizeof(apm_info));
00185     QFile f("/proc/pmu/info");
00186     if (!f.open(QIODevice::ReadOnly))
00187         return 1;
00188 
00189     while (!f.atEnd()) {
00190         QString l;
00191             l = f.readLine();
00192         QStringList ll = QStringList::split(':', l, false);
00193         if (ll[0].trimmed() == "AC Power") {
00194             ap->ac_line_status = ll[1].trimmed().toInt();
00195             //kDebug() << "line status " << ap->ac_line_status ;
00196         } else if (ll[0].trimmed() == "Battery count") {
00197             bcnt = ll[1].trimmed().toInt();
00198             //kDebug() << "batteries: " << bcnt ;
00199         }
00200     }
00201 
00202     f.close();
00203 
00204     int charge = 0;
00205     int timerem = 0;
00206     int maxcharge = 0;
00207     for (int i = 0; i < bcnt; i++) {
00208         QFile bf(QString("/proc/pmu/battery_%1").arg(i));
00209         if (!bf.open(QIODevice::ReadOnly))
00210             continue;
00211 
00212         while(!bf.atEnd()) {
00213             QString l;
00214             l = bf.readLine();
00215             QStringList ll = QStringList::split(':', l, false);
00216             if (ll[0].trimmed() == "charge") {
00217                 charge += ll[1].trimmed().toInt();
00218                 //kDebug() << "charge: " << charge ;
00219             } else if (ll[0].trimmed() == "max_charge") {
00220                 maxcharge += ll[1].trimmed().toInt();
00221                 //kDebug() << "max charge: " << maxcharge ;
00222             } else if (ll[0].trimmed() == "time rem.") {
00223                 timerem += ll[1].trimmed().toInt();
00224                 //kDebug() << "time: " << timerem ;
00225             }
00226         }
00227         bf.close();
00228     }
00229     ap->battery_percentage = int(rint(100.0*float(float(charge)/float(maxcharge))));
00230     ap->battery_time = timerem;
00231 
00232     if (ap->ac_line_status > 0 || timerem == 0 ||
00233         (ap->ac_line_status == 0 && charge > 100 && timerem == 0))
00234         ap->battery_time = -1;
00235 
00236     return 0;
00237 }
00238 
00239 
00240 struct acpi_battery_info {
00241     int percentage;
00242     bool present;
00243     int cap;
00244     int cap_low;
00245     int remaining;
00246     int rate;
00247     QString name;
00248     QString state_file;
00249     QString info_file;
00250 };
00251 
00252 static Q3ValueVector<acpi_battery_info> acpi_batteries;
00253 static int acpi_last_known=0;
00254 static int last_seed=1; // increment this to force revaluation
00255 
00256 static unsigned char acpi_ac_ok;
00257 
00258 //
00259 //  linux APCI doesn't return useful stuff like how much TIME is left yet
00260 //      the 'rate' is not smoothed over time so it's faked out here
00261 //      it's not pretty and it's a quick estimate
00262 //
00263 //      for the moment we prefer APM
00264 //
00265 
00266 static int
00267 acpi_ac_status()
00268 {
00269     DIR *dfd;
00270     struct dirent *dp;
00271     FILE *f = NULL;
00272     static char buff[NAME_MAX+50];
00273     static bool inited=0;
00274     static bool bad=0;
00275 
00276     if (inited) {
00277         if (bad)
00278             return(-1);
00279         f = fopen(buff, "r");
00280         goto readit;
00281     }
00282     inited = 1;
00283 
00284     dfd = opendir("/proc/acpi/ac_adapter/");
00285     if (dfd) {
00286         for (;;) {
00287             dp = readdir(dfd);
00288             if (dp == 0)
00289                 break;
00290             if (strcmp(dp->d_name, ".") == 0 ||
00291                     strcmp(dp->d_name, "..") == 0)
00292                 continue;
00293             strcpy(buff, "/proc/acpi/ac_adapter/");
00294             strcat(buff, dp->d_name);
00295             strcat(buff, "/status");
00296             f = fopen(buff, "r");
00297             if (!f) {
00298                 strcpy(buff, "/proc/acpi/ac_adapter/");
00299                 strcat(buff, dp->d_name);
00300                 strcat(buff, "/state");
00301                 f = fopen(buff, "r");
00302             }
00303             if (f)
00304                 break;
00305         }
00306         closedir(dfd);
00307 readit:
00308         if (f) {
00309             for (;;) {
00310                 char buff2[1024];
00311                 if (fgets(buff2, sizeof(buff), f) == NULL)
00312                     break;
00313                 if (strstr(buff2, "Status:") != NULL ||
00314                         strstr(buff2, "state:") != NULL) {
00315                     if (strstr(buff2, "on-line") != NULL) {
00316                         fclose(f);
00317                         return(1);
00318                     }
00319                 }
00320             }
00321             fclose(f);
00322             return(0);
00323         }
00324     }
00325     bad=1;
00326     return(-1);
00327 }
00328 
00329 static void acpi_read_batteries() {
00330     QString buff;
00331     QFile *f;
00332     static int test_count = 0;
00333     bool skip = false;
00334 
00335     for(int i = 0; i < acpi_batteries.count(); ++i) {
00336         acpi_battery_info& bat = acpi_batteries[i];
00337         bool present = false;
00338         if ((test_count==0 || acpi_last_known != last_seed) && !bat.info_file.isNull()) {
00339             f = new QFile(bat.info_file);
00340             if (f && f->open(QIODevice::ReadOnly)) {
00341                 while( !( buff=f->readLine() ).isNull() ) {
00342                     if (buff.contains("design capacity low:", Qt::CaseInsensitive)) {
00343                         QRegExp rx("(\\d*)\\D*$");
00344                         rx.search(buff);
00345                         bat.cap_low = rx.cap(1).toInt();
00346                         if (bat.cap_low < 0)
00347                             bat.cap_low = 0;
00348                         continue;
00349                     }
00350                     if (buff.contains("last full capacity:", Qt::CaseInsensitive)) {
00351                         QRegExp rx("(\\d*)\\D*$");
00352                         rx.search(buff);
00353                         bat.cap = rx.cap(1).toInt();
00354                         continue;
00355                     }
00356                 }
00357                 f->close();
00358                 bat.cap -= bat.cap_low;
00359             }
00360             delete f;
00361         }
00362         if (bat.cap <= 0) {
00363             KConfig* config = new KConfig("kcmlaptoprc", true /*readonly*/, false /*useKDEGlobals*/);
00364             config->setGroup("AcpiBattery");
00365             bat.cap = config->readEntry(bat.name,0);
00366             delete config;
00367         }
00368         if (!bat.state_file.isNull()) {
00369             f = new QFile(bat.state_file);
00370             if (f && f->open(QIODevice::ReadOnly)) {
00371                 while( !( buff= f->readLine() ).isNull()) {
00372                     if (buff.contains("present rate:", Qt::CaseInsensitive)) {
00373                         QRegExp rx("(\\d*)\\D*$");
00374                         rx.search(buff);
00375                         bat.rate = rx.cap(1).toInt();
00376                         if (bat.rate < 0)
00377                             bat.rate = 0;
00378                         present = true;
00379                         continue;
00380                     }
00381                     if (buff.contains("remaining capacity:", Qt::CaseInsensitive)) {
00382                         QRegExp rx("(\\d*)\\D*$");
00383                         rx.search(buff);
00384                         bat.remaining = rx.cap(1).toInt();
00385                         bat.remaining -= bat.cap_low;
00386                         if (bat.remaining < 0)
00387                             bat.remaining = 0;
00388                         present = true;
00389                         continue;
00390                     }
00391                 }
00392                 f->close();
00393             }
00394             delete f;
00395         }
00396         if(present && !bat.present) // recheck capacity if a battery was put in
00397             skip = true;
00398         bat.present = present;
00399         if (bat.present) {
00400             if (bat.remaining > bat.cap) {  // happens e.g. if the system doesn't provide a capacity value
00401                 bat.cap = bat.remaining;
00402                 KConfig* config = new KConfig("kcmlaptoprc", false /*readonly*/, false /*useKDEGlobals*/);
00403                 config->setGroup("AcpiBattery");
00404                 config->writeEntry(bat.name, bat.cap);
00405                 config->sync();
00406                 delete config;
00407                 skip = true;
00408             }
00409             if (bat.cap == 0)
00410                 bat.percentage = 0;
00411             else
00412                 bat.percentage = bat.remaining*100/bat.cap;
00413         }
00414         else
00415             bat.percentage = 0;
00416     }
00417 
00418     if (!skip) {
00419         acpi_last_known = last_seed;
00420         test_count++;
00421     }
00422     else
00423         test_count = 0;
00424     if (test_count > 1000)      // every 1000 or so times recheck the battery capacity
00425         test_count = 0;
00426 
00427 }
00428 
00429 static int
00430 acpi_read(apm_info *ap)
00431 {
00432     int rate, part, total;
00433     int ret = 1;
00434     bool present = 0;
00435 
00436     part = 0;
00437     total = 0;
00438     rate = 0;
00439 
00440     acpi_read_batteries();
00441 
00442     for(int i = 0; i < acpi_batteries.count(); ++i) {
00443         acpi_battery_info& bat = acpi_batteries[i];
00444         present |= bat.present;
00445         if(bat.present) {
00446             total += bat.cap;
00447             part += bat.remaining;
00448             rate += bat.rate;
00449         }
00450         ret = 0;
00451     }
00452 
00453     // some broken ACPI implementations don't return a rate
00454     // compute a 'fake' rate by diffing remaining values
00455     if (rate == 0)
00456     {
00457         static int last_remaining = 0;
00458         static time_t last_time = 0;
00459         if (last_remaining != 0
00460         && last_time != 0)
00461         {
00462         int diff_time = time(0) - last_time;
00463         if (diff_time > 0)
00464             rate = (last_remaining - part) * 3600 / diff_time;
00465         }
00466         last_remaining = part;
00467         last_time = time(0);
00468         if (rate < 0)
00469         rate = 0;
00470     }
00471 
00472     static int nrates = 0;
00473     static int saved_rate[8];
00474     static unsigned char ignore_next = 2;   // ignore the first couple
00475 
00476     ap->ac_line_status = 0;
00477     //
00478     //  ACPI (unlike nice systems like some APM implementations) doesn't
00479     //  tell us how much battery TIME we have left - here we
00480     //  do a weighted average of the discharge rate (in mW) and
00481     //  figure out how long we have left by dividing it into the
00482     //  remaining capacity
00483     //
00484     //  because some ACPI implementations return bogus
00485     //  rates when charging we can't estimate the battery life
00486     //  so we only collect discharge rate data when we're actually
00487     //  discharging
00488     //
00489     if (acpi_ac_status() == 1) {
00490         ap->ac_line_status |= 1;
00491         ignore_next = 2;
00492     } else {
00493         // after switching from power to unpowered we often get
00494         // a bad reading from ACPI resulting in bizarre
00495         // readings
00496         if (ignore_next == 0) {
00497             if (nrates < 8)             // smooth the power flow
00498                 nrates++;           // simple box filter
00499             for (int i = 8-1; i > 0; i--)
00500                 saved_rate[i] = saved_rate[i-1];
00501             saved_rate[0] = rate;
00502         } else {
00503             ignore_next--;
00504         }
00505     }
00506     //
00507     //  if we haven't got any discharge rate data yet don't return a
00508     //  battery time - probably happens when you start up with the
00509     //  ac adaptor plugged in
00510     //
00511     if (nrates == 0) {
00512         ap->battery_time = -1;
00513     } else {
00514         rate = 0;
00515         for (int i = 0; i < nrates; i++)
00516             rate += saved_rate[i];
00517         rate = (rate+2*saved_rate[0])/(nrates+2);       // weight it slighly
00518         ap->battery_time = (rate==0 ? -1 : 60*part/rate);
00519     }
00520     ap->battery_percentage = (total==0?0:100*part/total);
00521     if (!present) {
00522         ap->battery_percentage = -1;
00523         ap->battery_time = -1;
00524     }
00525     ap->apm_flags = 0;
00526     return(ret);
00527 }
00528 
00529 static int apm_no_time;
00530 static apm_info apmx = {0,0,0,0};
00531 static int
00532 has_apm()
00533 {
00534         static int init = 0;
00535         static int val;
00536         if (init)
00537                 return(val);
00538         init = 1;
00539         val = 1;
00540         apm_no_time=0;
00541         if (apm_read(&apmx) || (apmx.apm_flags&0x20)) {
00542                 val = 0;
00543                 apm_no_time = 1;
00544         } else {
00545                 apm_no_time = apmx.battery_time < 0;
00546         }
00547 
00548     if (val == 0) {
00549         val = have_pmu();
00550         if (val && pmu_read(&apmx)) {
00551             val = 0;
00552         }
00553     }
00554         return(val);
00555 }
00556 
00557 // Power to the powermacs!!
00558 static int
00559 has_pmu()
00560 {
00561     static int init = 0;
00562     static int val;
00563     if (init)
00564         return val;
00565     init = 1;
00566     val = 1;
00567     if (!QDir("/proc/pmu").exists()) {
00568         val = 0;
00569     }
00570     return val;
00571 }
00572 
00573 static bool software_suspend_is_preferred = false;  // user prefers to use SS for hibernate
00574 static bool acpi_helper_ok(bool type);
00575 static bool
00576 has_software_suspend(int type)
00577 {
00578     static int known=0;
00579     static bool present=0;      // functionality seems to be here
00580     static bool available=0;    // helper can work
00581     if (known != last_seed) {
00582         known = last_seed;
00583         available = 0;
00584         present = (((::access("/proc/sys/kernel/swsusp", F_OK) == 0) ||
00585                             (::access("/proc/software_suspend", F_OK) == 0)) ||
00586                             (::access("/proc/suspend2", F_OK) == 0)) &&
00587                             (::access("/usr/sbin/hibernate", F_OK) == 0);
00588         if (present) {
00589             if (::getuid() == 0) {  // running as root
00590                 available = ::access("/usr/sbin/hibernate", X_OK) == 0 && acpi_helper_ok(1);
00591             } else {
00592                 available = acpi_helper_ok(0);
00593             }
00594         }
00595     }
00596     switch (type) {
00597     case 0: return(present);
00598     case 1: return(present&available&software_suspend_is_preferred);
00599     case 2: return(present&available);
00600     default:return(0);
00601     }
00602 }
00603 
00604 bool
00605 laptop_portable::has_software_suspend(int type)
00606 {
00607     return(::has_software_suspend(type));
00608 }
00609 
00610 void
00611 laptop_portable::software_suspend_set_mask(bool hibernate)
00612 {
00613     software_suspend_is_preferred = hibernate;
00614 }
00615 
00616 
00617 static int x_acpi_init = 0;
00618 static bool
00619 has_acpi()
00620 {
00621     static bool val;
00622     static bool checked = 0;
00623 
00624     if (!checked) {
00625         val = ::access("/proc/acpi", F_OK) == 0;
00626         checked = 1;
00627     }
00628     return(val);
00629 }
00630 
00631 static int
00632 has_acpi_power()
00633 {
00634     static int val;
00635 
00636     if (x_acpi_init)
00637         return(val);
00638     x_acpi_init = 1;
00639     val = 0;
00640 
00641     acpi_batteries.clear();
00642 
00643     if (acpi_ac_status() >= 0)
00644         acpi_ac_ok = 1;
00645 
00646     QDir battdir("/proc/acpi/battery");
00647     battdir.setFilter(QDir::Dirs);
00648     if(!battdir.isReadable())
00649         return(val = 0);
00650     for(uint i = 0; !battdir[i].isNull(); ++i) {
00651         if(battdir[i] == "." || battdir[i] == "..")
00652             continue;
00653         bool ok = 0;
00654         acpi_battery_info bat = {0,0,0,0,0,0,0,0,0};
00655         QString base = battdir.path() + "/" + battdir[i] + "/";
00656         QString name(base + "state");
00657         QFileInfo f(name);
00658         if(f.isReadable()) {
00659             bat.state_file = name;
00660             ok = true;
00661         } else {
00662             name = base + "status";
00663             f.setFile(name);
00664             if(f.isReadable()) {
00665                 bat.state_file = name;
00666                 ok = true;
00667             }
00668         }
00669         name = base + "info";
00670         f.setFile(name);
00671         if(f.isReadable()) {
00672             bat.info_file = name;
00673             ok = true;
00674         }
00675         if (ok) {
00676             bat.name = battdir[i];
00677             acpi_batteries.append(bat);
00678             val = 1;
00679         }
00680     }
00681     if (!acpi_ac_ok)
00682         val = 0;
00683     return(val);
00684 }
00685 
00686 static unsigned long acpi_sleep_enabled = 0x00; // acpi sleep functions enabled mask
00687 
00688 
00689 static bool
00690 has_acpi_sleep(int state)
00691 {
00692     static int known=0;
00693     static unsigned long mask=0;
00694     if (known != last_seed) {
00695         known = last_seed;
00696         mask = 0;
00697         QFile f("/proc/acpi/sleep");
00698         if (f.open(QIODevice::ReadOnly)) {
00699             QString l;
00700                 l= f.readLine();
00701             QStringList ll = QStringList::split(' ',l,false);
00702             for (QStringList::const_iterator i = ll.constBegin(); i!=ll.constEnd(); ++i) {
00703                 QString s = *i;
00704                 if (s[0] == 'S') {
00705                     int c = s[1].digitValue();
00706                     if (c >= 0 && c <= 9)
00707                         mask |= 1<<c;
00708                 }
00709             }
00710             f.close();
00711         }
00712     }
00713     return((mask&acpi_sleep_enabled&(1<<state)) != 0);
00714 }
00715 
00716 static bool acpi_performance_enabled = 0;
00717 static bool acpi_throttle_enabled = 0;
00718 void
00719 laptop_portable::acpi_set_mask(bool standby, bool suspend, bool hibernate, bool perf, bool throttle)
00720 {
00721     acpi_sleep_enabled =
00722             (standby ? (1<<1)|(1<<2) : 0) |
00723             (suspend ? 1<<3 : 0) |
00724             (hibernate ? 1<<4 : 0);
00725     acpi_performance_enabled = perf;
00726     acpi_throttle_enabled = throttle;
00727     last_seed++;
00728 }
00729 
00730 static void
00731 invoke_acpi_helper(const char *param, const char *param2, const char *param3)
00732 {
00733     KProcess proc;
00734     proc << KStandardDirs::findExe("klaptop_acpi_helper");
00735     proc << param;
00736     if (param2)
00737         proc << param2;
00738     if (param3)
00739         proc << param3;
00740     proc.execute(); // helper runs fast and we want to see the result
00741 }
00742 
00743 static unsigned long apm_sleep_enabled = 0x0c;  // apm sleep functions enabled mask
00744 static bool
00745 has_apm_sleep(int state)
00746 {
00747     return((apm_sleep_enabled&(1<<state)) != 0);
00748 }
00749 
00750 void
00751 laptop_portable::apm_set_mask(bool standby, bool suspend)
00752 {
00753     apm_sleep_enabled =
00754             (standby ? 1<<2 : 0) |
00755             (suspend ? 1<<3 : 0);
00756     last_seed++;
00757 }
00758 
00759 static bool apm_sleep_access_ok();
00760 static bool acpi_sleep_access_ok();
00761 
00762 static int
00763 apm_has_time()
00764 {
00765         return(!apm_no_time);
00766 }
00767 
00768 //
00769 //  something changed maybe we need to check out environment again
00770 //
00771 void
00772 laptop_portable::power_management_restart()
00773 {
00774     last_seed++;
00775     x_acpi_init = 0;
00776 }
00777 
00778 int laptop_portable::has_apm(int type)
00779 {
00780     switch (type) {
00781     case 0:
00782         return(::has_apm());
00783     case 1:
00784         return(::has_apm() && ::apm_sleep_access_ok());
00785     default:
00786         return(0);
00787     }
00788 }
00789 
00790 //
00791 //  returns 1 if we support acpi
00792 //
00793 int laptop_portable::has_acpi(int type)
00794 {
00795     switch (type) {
00796     case 0:
00797         return(::has_acpi_power());
00798     case 1:
00799         return(::has_acpi() && ::acpi_sleep_access_ok());
00800     case 3:
00801         return(::has_acpi() &&::acpi_sleep_access_ok() && (::has_acpi_sleep(1)||::has_acpi_sleep(2)));
00802     case 4:
00803         return(::has_acpi() &&::acpi_sleep_access_ok() && ::has_acpi_sleep(3));
00804     case 5:
00805         return(::has_acpi() &&::acpi_sleep_access_ok() && ::has_acpi_sleep(4));
00806     default:
00807         return(0);
00808     }
00809 }
00810 
00811 //
00812 // The following code was derived from tpctl for IBM thinkpad support
00813 //      (Thomas Hood, Bill Mair - tpctl.sourceforge.net)
00814 //
00815 //  we check for the existence of /dev/thinkpad/thinkpad or /dev/thinkpad
00816 //
00817 static const char *ibm_device;
00818 static int ibm_fd = -1;
00819 
00820 static bool has_ibm()
00821 {
00822     static int known=0;
00823     static bool result;
00824     if (known == last_seed)
00825         return(result);
00826     known = last_seed;
00827     result = 0;
00828     if (ibm_fd >= 0) {
00829         result = 1;
00830         return(1);
00831     }
00832     ibm_device = "/dev/thinkpad/thinkpad";
00833     if ((ibm_fd = open(ibm_device, O_RDWR)) < 0) {
00834         ibm_device = "/dev/thinkpad";
00835         if ((ibm_fd = open(ibm_device, O_RDWR)) < 0)
00836             return(0);
00837     }
00838     result = 1;
00839     return(1);
00840 }
00841 //
00842 //  returns 1 if we support power management
00843 //
00844 int laptop_portable::has_power_management()
00845 {
00846     if (::has_apm())
00847         return 1;
00848     if (::has_pmu())
00849         return 1;
00850     if (::has_acpi_power())
00851         return 1;
00852     if (::has_ibm())
00853         return 1;
00854     // INSERT HERE
00855     return 0;
00856 }
00857 //
00858 //  returns 1 if the BIOS returns the time left in the battery rather than a % of full
00859 //
00860 int laptop_portable::has_battery_time()
00861 {
00862     if (::has_acpi_power())
00863         return 1;
00864     if (::apm_has_time())
00865         return 1;
00866     // INSERT HERE
00867     return 0;
00868 }
00869 //
00870 //  returns 1 if we can perform a change-to-suspend-mode operation for the user
00871 //  (has_power_management() has already returned 1)
00872 //
00873 int laptop_portable::has_suspend()
00874 {
00875     if (::has_acpi())
00876         return ::acpi_sleep_access_ok() && ::has_acpi_sleep(3);
00877     if (::has_pmu())
00878         return 1;
00879     if (::has_ibm())
00880         return 1;
00881     if (::has_apm())
00882         return ::apm_sleep_access_ok() && ::has_apm_sleep(3);
00883     // INSERT HERE
00884     return 0;
00885 }
00886 //
00887 //  returns 1 if we can perform a change-to-standby-mode operation for the user
00888 //  (has_power_management() has already returned 1)
00889 //
00890 int laptop_portable::has_standby()
00891 {
00892     if (::has_pmu())
00893         return 0;
00894     if (::has_acpi())
00895         return ::acpi_sleep_access_ok() && (::has_acpi_sleep(1)||::has_acpi_sleep(2));
00896     if (::has_ibm())
00897         return 1;
00898     if (::has_apm())
00899         return ::apm_sleep_access_ok() && ::has_apm_sleep(2);
00900     // INSERT HERE
00901     return 0;
00902 }
00903 //
00904 //  returns 1 if we can perform a change-to-hibernate-mode for a user
00905 //      (has_power_management() has already returned 1)  [hibernate is the save-to-disk mode
00906 //  not supported by linux APM]
00907 //
00908 int laptop_portable::has_hibernation()
00909 {
00910     if (::has_pmu())
00911         return 0;
00912     if (::has_software_suspend(1))  // must be before acpi
00913         return 1;
00914     if (::has_acpi())
00915         return ::acpi_sleep_access_ok() && ::has_acpi_sleep(4);
00916     if (::has_ibm())
00917         return 1;
00918     // INSERT HERE
00919     return 0;
00920 }
00921 
00922 //
00923 //  explain to the user what they need to do if has_power_management() returned 0
00924 //  to get any software they lack
00925 //
00926 
00927 K3ActiveLabel *laptop_portable::no_power_management_explanation(QWidget *parent)
00928 {
00929     if (access("/proc/acpi", F_OK) == 0) {  // probably has default kernel ACPI installed
00930         K3ActiveLabel* explain = new K3ActiveLabel(i18n("Your computer seems to have a partial ACPI installation. ACPI was probably enabled, but some of the sub-options were not - you need to enable at least 'AC Adaptor' and 'Control Method Battery' and then rebuild your kernel."), parent);
00931         return(explain);
00932     }
00933 
00934     K3ActiveLabel* explain = new K3ActiveLabel(i18n("Your computer doesn't have the Linux APM (Advanced Power Management) or ACPI software installed, or doesn't have the APM kernel drivers installed - check out the <a href=\"http://www.linuxdoc.org/HOWTO/Laptop-HOWTO.html\">Linux Laptop-HOWTO</a> document for information on how to install APM."), parent);
00935 
00936     return(explain);
00937 }
00938 
00939 //
00940 //  explain to the user what they need to do to get suspend/resume to work from user mode
00941 //
00942 QLabel *laptop_portable::how_to_do_suspend_resume(QWidget *parent)
00943 {
00944     if (::has_apm()) {
00945                 // TODO: remove linefeed from string, can't do it right now coz we have a string freeze
00946         QLabel* note = new KRichTextLabel(i18n("\nIf you make /usr/bin/apm setuid then you will also "
00947                         "be able to choose 'suspend' and 'standby' in the above "
00948                         "dialog - check out the help button below to find out "
00949                         "how to do this").replace("\n", QString()), parent);
00950         return(note);
00951     }
00952     if (::has_acpi()) {
00953                 // TODO: remove linefeed from string, can't do it right now coz we have a string freeze
00954         QLabel* note = new KRichTextLabel(i18n("\nYou may need to enable ACPI suspend/resume in the ACPI panel").replace("\n", QString()), parent);
00955         return(note);
00956     }
00957         // TODO: remove linefeed from string, can't do it right now coz we have a string freeze
00958     QLabel* note = new KRichTextLabel(i18n("\nYour system does not support suspend/standby").replace("\n", QString()), parent);
00959     return(note);
00960 }
00961 
00962 static char tmp0[256], tmp1[256];
00963 static int present=0;
00964 static
00965 void get_pcmcia_info()
00966 {
00967       FILE *f = fopen("/var/lib/pcmcia/stab", "r");
00968       if (!f) f = fopen("/var/run/stab", "r");
00969       if (f) {
00970     int c;
00971     char *cp;
00972 
00973     present = 1;
00974     cp = tmp0;
00975     for (;;) {
00976         c = fgetc(f);
00977         if (c == EOF || c == '\n')
00978             break;
00979         if (c == ':') {
00980             while ((c = fgetc(f)) == ' ')
00981                 ;
00982             if (c == EOF)
00983                 break;
00984             for (;;) {
00985                 *cp++ = c;
00986                 c = fgetc(f);
00987                 if (c == EOF || c == '\n')
00988                     break;
00989             }
00990             break;
00991         }
00992     }
00993     *cp = 0;
00994 
00995     if (c != EOF) {
00996         cp = tmp1;
00997         for (;;) {
00998             c = fgetc(f);
00999             if (c == EOF)
01000                 break;
01001             if (c == ':') {
01002                 while ((c = fgetc(f)) == ' ')
01003                     ;
01004                 if (c == EOF)
01005                     break;
01006                 for (;;) {
01007                     *cp++ = c;
01008                     c = fgetc(f);
01009                     if (c == EOF || c == '\n')
01010                         break;
01011                 }
01012                 break;
01013             }
01014         }
01015     *cp = 0;
01016     }
01017 
01018     fclose(f);
01019       } else {
01020     present = 0;
01021       }
01022 }
01023 
01024 //
01025 //  pcmcia support - this will be replaced by better - pcmcia support being worked on by
01026 //  others
01027 //
01028 QLabel *laptop_portable::pcmcia_info(int x, QWidget *parent)
01029 {
01030     if (x == 0)
01031         get_pcmcia_info();
01032     if (!present) {
01033         if (x == 1)
01034                 return(new QLabel(i18n("No PCMCIA controller detected"), parent));
01035             return(new QLabel(parent));
01036     } else {
01037         switch (x) {
01038             case 0: return(new QLabel(i18n("Card 0:"), parent));
01039             case 1: return(new QLabel(tmp0, parent));
01040         case 2: return(new QLabel(i18n("Card 1:"), parent));
01041             default:return(new QLabel(tmp1, parent));
01042         }
01043     }
01044 }
01045 
01046 //
01047 //  puts us into standby mode
01048 //
01049 void laptop_portable::invoke_standby()
01050 {
01051     last_seed++;    // make it look for battery removal/return
01052     if (::has_acpi()) {
01053         if (::has_acpi_sleep(1)) {  // people seem to argue as to which is 'standby' the S1 people are winning
01054             ::invoke_acpi_helper("--standby", 0, 0);
01055         } else {
01056             ::invoke_acpi_helper("--standby2", 0, 0);
01057         }
01058         return;
01059     }
01060     if (::has_ibm()) {
01061         smapi_ioparm_t ioparmMy;
01062         sync();
01063         ioparmMy.in.bFunc = (byte) 0x70;
01064         ioparmMy.in.bSubFunc = (byte) 0;
01065         ioparmMy.in.wParm1 = (word) 0;
01066         ioparmMy.in.wParm2 = (word) 0;
01067         ioparmMy.in.wParm3 = (word) 0;
01068         ioparmMy.in.dwParm4 = (dword) 0;
01069         ioparmMy.in.dwParm5 = (dword) 0;
01070         (void)ioctl_smapi( ibm_fd, &ioparmMy );
01071         return;
01072     }
01073     // add other machine specific standbys here
01074     KProcess proc;
01075     proc << "/usr/bin/apm";
01076     proc << "--standby";
01077     proc.execute(); // helper runs fast and we want to see the result
01078 }
01079 
01080 //
01081 //  puts us into suspend mode
01082 //
01083 void laptop_portable::invoke_suspend()
01084 {
01085     last_seed++;    // make it look for battery removal/return
01086 
01087     if (::has_pmu()) {
01088         KProcess proc;
01089         proc << "/usr/bin/apm";
01090         proc << "-f";
01091         proc.execute(); // helper runs fast and we want to see the result
01092         return;
01093     }
01094 
01095     if (::has_acpi()) {
01096         ::invoke_acpi_helper("--suspend", 0, 0);
01097         return;
01098     }
01099     if (::has_ibm()) {
01100         smapi_ioparm_t ioparmMy;
01101         sync();
01102         ioparmMy.in.bFunc = (byte) 0x70;
01103         ioparmMy.in.bSubFunc = (byte) 1;
01104         ioparmMy.in.wParm1 = (word) 0;
01105         ioparmMy.in.wParm2 = (word) 0;
01106         ioparmMy.in.wParm3 = (word) 0;
01107         ioparmMy.in.dwParm4 = (dword) 0;
01108         ioparmMy.in.dwParm5 = (dword) 0;
01109         (void)ioctl_smapi( ibm_fd, &ioparmMy );
01110         return;
01111     }
01112     // add other machine specific suspends here
01113     KProcess proc;
01114     proc << "/usr/bin/apm";
01115     proc << "--suspend";
01116     proc.execute(); // helper runs fast and we want to see the result
01117 }
01118 
01119 //
01120 //  puts us into hibernate mode
01121 //
01122 void laptop_portable::invoke_hibernation()
01123 {
01124     last_seed++;    // make it look for battery removal/return
01125     if (::has_software_suspend(1)) {    // must be before acpi
01126         ::invoke_acpi_helper("--software-suspend", 0, 0);
01127         return;
01128     }
01129     if (::has_acpi()) {
01130         ::invoke_acpi_helper("--hibernate", 0, 0);
01131         return;
01132     }
01133     if (::has_ibm()) {
01134         smapi_ioparm_t ioparmMy;
01135         sync();
01136         ioparmMy.in.bFunc = (byte) 0x70;
01137         ioparmMy.in.bSubFunc = (byte) 2;
01138         ioparmMy.in.wParm1 = (word) 0;
01139         ioparmMy.in.wParm2 = (word) 0;
01140         ioparmMy.in.wParm3 = (word) 0;
01141         ioparmMy.in.dwParm4 = (dword) 0;
01142         ioparmMy.in.dwParm5 = (dword) 0;
01143         (void)ioctl_smapi( ibm_fd, &ioparmMy );
01144         return;
01145     }
01146     // add other machine specific hibernates here
01147 }
01148 
01149 void
01150 laptop_portable::extra_config(QWidget *wp, KConfig *, QVBoxLayout *top_layout)
01151 {
01152     if (laptop_portable::has_apm(1) || laptop_portable::has_acpi(1))
01153         return;
01154     if (laptop_portable::has_apm(0)) {
01155             QLabel* explain = new KRichTextLabel( i18n("Your system has APM installed but may not be able to use all "
01156                                "of its features without further setup - look in the 'APM Config' "
01157                            "tab for information about setting up APM for suspend and resume"), wp);
01158         top_layout->addWidget(explain, 0);
01159     }
01160     if (laptop_portable::has_acpi(0)) {
01161             QLabel* explain = new KRichTextLabel( i18n("Your system has ACPI installed but may not be able to use all "
01162                                "of its features without further setup - look in the 'ACPI Config' "
01163                            "tab for information about setting up ACPI for suspend and resume"), wp);
01164             top_layout->addWidget(explain, 0);
01165     }
01166 }
01167 
01168 //
01169 //  return current battery state
01170 //
01171 struct power_result laptop_portable::poll_battery_state()
01172 {
01173     struct power_result p;
01174 
01175     apm_info x = {0,0,0,-1};
01176     if (have_pmu()) {
01177         pmu_read(&x);
01178     } else
01179     if ((::has_acpi_power() ? ::acpi_read(&x) : ::apm_read(&x)) || (x.apm_flags&0x20)) {
01180         // INSERT HERE
01181                 p.powered = 0;
01182                 p.percentage=0;
01183                 p.time = -1;
01184         return p;
01185         }
01186     p.powered = x.ac_line_status&1;
01187     p.percentage = x.battery_percentage;
01188     p.time = x.battery_time;
01189     return(p);
01190 }
01191 
01192 void
01193 laptop_portable::get_battery_status(int &num_batteries, QStringList &names, QStringList &state, QStringList &values)
01194 {
01195     struct power_result r;
01196 
01197     if (!has_power_management()) {
01198         num_batteries = 0;
01199         names.clear();
01200         state.clear();
01201         values.clear();
01202         return;
01203     }
01204 
01205     if (::has_acpi_power()) {
01206         names.clear();
01207         state.clear();
01208         values.clear();
01209         acpi_read_batteries();
01210         num_batteries = acpi_batteries.count();
01211         for(int i = 0; i < acpi_batteries.count(); ++i) {
01212             acpi_battery_info& bat = acpi_batteries[i];
01213             names.append(bat.name);
01214             values.append(QString("%1").arg(bat.percentage));
01215             state.append(bat.present ? "yes" : "no");
01216         }
01217         return;
01218     }
01219 
01220     // INSERT HERE
01221 
01222     num_batteries = 1;
01223     r = poll_battery_state();
01224     names.append("BAT1");
01225     state.append("yes");
01226     QString s;
01227     s.setNum(r.percentage);
01228     values.append(s);
01229 }
01230 
01231 
01232 //
01233 //  returns the current system load average, -1 if none
01234 //
01235 static QFile lav_file;
01236 static bool lav_inited=0;
01237 static bool lav_openok=0;
01238 
01239 static bool has_lav()
01240 {
01241     if (!lav_inited) {
01242         lav_inited =1;
01243         lav_file.setName("/proc/loadavg");
01244         lav_openok = lav_file.open( QIODevice::ReadOnly );
01245         if (lav_openok)
01246             lav_file.close();
01247     }
01248     return(lav_openok);
01249 }
01250 
01251 bool laptop_portable::has_lav()
01252 {
01253     return ::has_lav();
01254 }
01255 
01256 float laptop_portable::get_load_average()
01257 {
01258     if (!::has_lav())
01259         return(-1);
01260     lav_file.open( QIODevice::ReadOnly );
01261     QString l;
01262         l=lav_file.readLine();
01263     lav_file.close();
01264     QStringList ll = QStringList::split(' ', l, false);
01265     l = ll[0];
01266     bool ok;
01267     float f = l.toFloat(&ok);
01268     if (!ok)
01269         f = -1;
01270     return(f);
01271 }
01272 
01273 
01274 int laptop_portable::has_cpufreq() {
01275     struct stat sb;
01276     int rc;
01277     rc = stat("/proc/cpufreq", &sb);
01278     if (rc == 0) {
01279         rc = stat("/proc/cpuinfo", &sb);
01280         if (rc == 0)
01281             return 1;
01282     }
01283     // INSERT HERE
01284     return 0;
01285 }
01286 
01287 
01288 QString laptop_portable::cpu_frequency() {
01289     QString rc;
01290     QFile cf("/proc/cpufreq");
01291     bool haveProfile = false;
01292 
01293     if (cf.open(QIODevice::ReadOnly)) {
01294         while (!cf.atEnd()) {
01295             QString l;
01296                 l = cf.readLine();
01297             if (l.left(3) == "CPU") {
01298                 QStringList ll = QStringList::split(' ', l, false);
01299                 rc = ll.last();
01300                 haveProfile = true;
01301                 break;
01302             }
01303         }
01304 
01305     }
01306     if (haveProfile) {
01307         QFile ci("/proc/cpuinfo");
01308         if (ci.open(QIODevice::ReadOnly)) {
01309             while (!ci.atEnd()) {
01310                 QString l;
01311                     l=ci.readLine();
01312                 QStringList ll =
01313                     QStringList::split(':',l,false);
01314                 if (ll.count() != 2)
01315                     continue;
01316                 if (ll.first().trimmed()
01317                         == "cpu MHz") {
01318                     rc = i18n("%1 MHz (%2)", ll.last().trimmed(), rc);
01319                     break;
01320                 } else if (ll.first().trimmed()
01321                         == "clock") {
01322                     rc = QString("%1 (%2)").arg(ll.last().trimmed()).arg(rc);
01323                     break;
01324                 }
01325             }
01326         }
01327     }
01328 
01329     return rc;
01330 }
01331 
01332 static int sonyFd = -1;
01333 static int has_toshiba_brightness = 0;
01334 
01335 static bool
01336 acpi_helper_ok(bool type)
01337 {
01338     static int known[2]={0,0};
01339     static bool known_res[2];
01340 
01341     if (known[type] == last_seed)
01342         return(known_res[type]);
01343     known[type] = last_seed;
01344     known_res[type] = 0;
01345     struct stat sb;
01346