summaryrefslogtreecommitdiffstats
path: root/src/measurement
diff options
context:
space:
mode:
Diffstat (limited to 'src/measurement')
-rw-r--r--src/measurement/acpi.cpp213
-rw-r--r--src/measurement/acpi.h46
-rw-r--r--src/measurement/extech.cpp354
-rw-r--r--src/measurement/extech.h51
-rw-r--r--src/measurement/measurement.cpp188
-rw-r--r--src/measurement/measurement.h71
-rw-r--r--src/measurement/opal-sensors.cpp48
-rw-r--r--src/measurement/opal-sensors.h42
-rw-r--r--src/measurement/sysfs.cpp160
-rw-r--r--src/measurement/sysfs.h55
10 files changed, 1228 insertions, 0 deletions
diff --git a/src/measurement/acpi.cpp b/src/measurement/acpi.cpp
new file mode 100644
index 0000000..572852c
--- /dev/null
+++ b/src/measurement/acpi.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2010, Intel Corporation
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+#include "measurement.h"
+#include "acpi.h"
+#include <iostream>
+#include <fstream>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "../lib.h"
+
+using namespace std;
+
+acpi_power_meter::acpi_power_meter(const char *acpi_name)
+{
+ rate = 0.0;
+ capacity = 0.0;
+ voltage = 0.0;
+ pt_strcpy(battery_name, acpi_name);
+}
+
+/*
+present: yes
+capacity state: ok
+charging state: discharging
+present rate: 8580 mW
+remaining capacity: 34110 mWh
+present voltage: 12001 mV
+*/
+
+void acpi_power_meter::measure(void)
+{
+ char filename[PATH_MAX];
+ char line[4096];
+ ifstream file;
+
+ double _rate = 0;
+ double _capacity = 0;
+ double _voltage = 0;
+
+ char rate_units[16];
+ char capacity_units[16];
+ char voltage_units[16];
+
+ rate_units[0] = 0;
+ capacity_units[0] = 0;
+ voltage_units[0] = 0;
+
+ rate = 0;
+ voltage = 0;
+ capacity = 0;
+
+ snprintf(filename, sizeof(filename), "/proc/acpi/battery/%s/state", battery_name);
+
+ file.open(filename, ios::in);
+ if (!file)
+ return;
+
+ while (file) {
+ char *c;
+ file.getline(line, sizeof(line));
+
+ if (strstr(line, "present:") && (strstr(line, "yes") == NULL)) {
+ return;
+ }
+ if (strstr(line, "charging state:") && (strstr(line, "discharging") == NULL)) {
+ return; /* not discharging */
+ }
+ if (strstr(line, "present rate:")) {
+ c = strchr(line, ':');
+ c++;
+ while (*c == ' ') c++;
+ _rate = strtoull(c, NULL, 10);
+ c = strchr(c, ' ');
+ if (c) {
+ c++;
+ pt_strcpy(rate_units, c);
+ } else {
+ _rate = 0;
+ strcpy(rate_units, "Unknown");
+ }
+
+ }
+ if (strstr(line, "remaining capacity:")) {
+ c = strchr(line, ':');
+ c++;
+ while (*c == ' ') c++;
+ _capacity = strtoull(c, NULL, 10);
+ c = strchr(c, ' ');
+ if (c) {
+ c++;
+ pt_strcpy(capacity_units, c);
+ } else {
+ _capacity = 0;
+ strcpy(capacity_units, "Unknown");
+ }
+ }
+ if (strstr(line, "present voltage:")) {
+ c = strchr(line, ':');
+ c++;
+ while (*c == ' ') c++;
+ _voltage = strtoull(c, NULL, 10);
+ c = strchr(c, ' ');
+ if (c) {
+ c++;
+ pt_strcpy(voltage_units, c);
+ } else {
+ _voltage = 0;
+ strcpy(voltage_units, "Unknown");
+ }
+ }
+ }
+ file.close();
+
+ /* BIOS report random crack-inspired units. Lets try to get to the Si-system units */
+
+ if (strcmp(voltage_units, "mV") == 0) {
+ _voltage = _voltage / 1000.0;
+ strcpy(voltage_units, "V");
+ }
+
+ if (strcmp(rate_units, "mW") == 0) {
+ _rate = _rate / 1000.0;
+ strcpy(rate_units, "W");
+ }
+
+ if (strcmp(rate_units, "mA") == 0) {
+ _rate = _rate / 1000.0;
+ strcpy(rate_units, "A");
+ }
+
+ if (strcmp(capacity_units, "mAh") == 0) {
+ _capacity = _capacity / 1000.0;
+ strcpy(capacity_units, "Ah");
+ }
+ if (strcmp(capacity_units, "mWh") == 0) {
+ _capacity = _capacity / 1000.0;
+ strcpy(capacity_units, "Wh");
+ }
+ if (strcmp(capacity_units, "Wh") == 0) {
+ _capacity = _capacity * 3600.0;
+ strcpy(capacity_units, "J");
+ }
+
+
+ if (strcmp(capacity_units, "Ah") == 0 && strcmp(voltage_units, "V") == 0) {
+ _capacity = _capacity * 3600.0 * _voltage;
+ strcpy(capacity_units, "J");
+ }
+
+ if (strcmp(rate_units, "A") == 0 && strcmp(voltage_units, "V")==0 ) {
+ _rate = _rate * _voltage;
+ strcpy(rate_units, "W");
+ }
+
+
+
+
+ if (strcmp(capacity_units, "J") == 0)
+ capacity = _capacity;
+ else
+ capacity = 0.0;
+
+ if (strcmp(rate_units, "W")==0)
+ rate = _rate;
+ else
+ rate = 0.0;
+
+ if (strcmp(voltage_units, "V")==0)
+ voltage = _voltage;
+ else
+ voltage = 0.0;
+}
+
+
+void acpi_power_meter::end_measurement(void)
+{
+ measure();
+}
+
+void acpi_power_meter::start_measurement(void)
+{
+ /* ACPI battery state is a lagging indication, lets only measure at the end */
+}
+
+
+double acpi_power_meter::power(void)
+{
+ return rate;
+}
diff --git a/src/measurement/acpi.h b/src/measurement/acpi.h
new file mode 100644
index 0000000..25bbe04
--- /dev/null
+++ b/src/measurement/acpi.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010, Intel Corporation
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+#ifndef __INCLUDE_GUARD_ACPI_H
+#define __INCLUDE_GUARD_ACPI_H
+
+#include "measurement.h"
+
+class acpi_power_meter: public power_meter {
+ char battery_name[256];
+
+ double capacity;
+ double rate;
+ double voltage;
+ void measure(void);
+public:
+ acpi_power_meter(const char *_battery_name);
+ virtual void start_measurement(void);
+ virtual void end_measurement(void);
+
+ virtual double power(void);
+ virtual double dev_capacity(void) { return capacity; };
+};
+
+#endif
diff --git a/src/measurement/extech.cpp b/src/measurement/extech.cpp
new file mode 100644
index 0000000..867aca7
--- /dev/null
+++ b/src/measurement/extech.cpp
@@ -0,0 +1,354 @@
+/*
+ * extech - Program for controlling the extech Device
+ * This file is part of PowerTOP
+ *
+ * Based on earlier client by Patrick Mochel for Wattsup Pro device
+ * Copyright (c) 2005 Patrick Mochel
+ * Copyright (c) 2006 Intel Corporation
+ * Copyright (c) 2011 Intel Corporation
+ *
+ * Authors:
+ * Patrick Mochel
+ * Venkatesh Pallipadi
+ * Arjan van de Ven
+ *
+ * Thanks to Rajiv Kapoor for finding out the DTR, RTS line bits issue below
+ * Without that this program would never work.
+ *
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <time.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <sys/stat.h>
+
+#include "measurement.h"
+#include "extech.h"
+#include "../lib.h"
+#include <iostream>
+#include <fstream>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace std;
+
+struct packet {
+ char buf[256];
+ char op[32];
+ double watts;
+ int len;
+};
+
+
+static int open_device(const char *device_name)
+{
+ struct stat s;
+ int ret;
+
+ ret = stat(device_name, &s);
+ if (ret < 0)
+ return -1;
+
+ if (!S_ISCHR(s.st_mode))
+ return -1;
+
+ ret = access(device_name, R_OK | W_OK);
+ if (ret)
+ return -1;
+
+ ret = open(device_name, O_RDWR | O_NONBLOCK | O_NOCTTY);
+ if (ret < 0)
+ return -1;
+
+ return ret;
+}
+
+
+static int setup_serial_device(int dev_fd)
+{
+ struct termios t;
+ int ret;
+ int flgs;
+
+ ret = tcgetattr(dev_fd, &t);
+ if (ret)
+ return ret;
+
+ cfmakeraw(&t);
+ cfsetispeed(&t, B9600);
+ cfsetospeed(&t, B9600);
+ tcflush(dev_fd, TCIFLUSH);
+
+ t.c_iflag = IGNPAR;
+ t.c_cflag = B9600 | CS8 | CREAD | CLOCAL;
+ t.c_oflag = 0;
+ t.c_lflag = 0;
+ t.c_cc[VMIN] = 2;
+ t.c_cc[VTIME] = 0;
+
+ t.c_iflag &= ~(IXON | IXOFF | IXANY);
+ t.c_oflag &= ~(IXON | IXOFF | IXANY);
+
+ ret = tcsetattr(dev_fd, TCSANOW, &t);
+
+ if (ret)
+ return ret;
+
+ /*
+ * Root caused by Rajiv Kapoor. Without this extech reads
+ * will fail
+ */
+
+ /* get DTR and RTS line bits settings */
+ ioctl(dev_fd, TIOCMGET, &flgs);
+
+ /* set DTR to 1 and RTS to 0 */
+ flgs |= TIOCM_DTR;
+ flgs &= ~TIOCM_RTS;
+ ioctl(dev_fd, TIOCMSET, &flgs);
+
+ return 0;
+}
+
+
+static unsigned int decode_extech_value(unsigned char byt3, unsigned char byt4, char *a)
+{
+ unsigned int input = ((unsigned int)byt4 << 8) + byt3;
+ unsigned int i;
+ unsigned int idx;
+ unsigned char revnum[] = { 0x0, 0x8, 0x4, 0xc,
+ 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd,
+ 0x3, 0xb, 0x7, 0xf};
+ unsigned char revdec[] = { 0x0, 0x2, 0x1, 0x3};
+
+ unsigned int digit_map[] = {0x2, 0x3c, 0x3c0, 0x3c00};
+ unsigned int digit_shift[] = {1, 2, 6, 10};
+
+ unsigned int sign;
+ unsigned int decimal;
+
+ /* this is basically BCD encoded floating point... but kinda weird */
+
+ decimal = (input & 0xc000) >> 14;
+ decimal = revdec[decimal];
+
+ sign = input & 0x1;
+
+ idx = 0;
+ if (sign)
+ a[idx++] = '+';
+ else
+ a[idx++] = '-';
+
+ /* first digit is only one or zero */
+ a[idx] = '0';
+ if ((input & digit_map[0]) >> digit_shift[0])
+ a[idx] += 1;
+
+ idx++;
+ /* Reverse the remaining three digits and store in the array */
+ for (i = 1; i < 4; i++) {
+ int dig = ((input & digit_map[i]) >> digit_shift[i]);
+ dig = revnum[dig];
+ if (dig > 0xa)
+ goto error_exit;
+
+ a[idx++] = '0' + dig;
+ }
+
+ /* Fit the decimal point where appropriate */
+ for (i = 0; i < decimal; i++)
+ a[idx - i] = a[idx - i - 1];
+
+ a[idx - decimal] = '.';
+ a[++idx] = '0';
+ a[++idx] = '\0';
+
+ return 0;
+error_exit:
+ return -1;
+}
+
+static int parse_packet(struct packet * p)
+{
+ int i;
+ int ret;
+
+ p->buf[p->len] = '\0';
+
+ /*
+ * First character in 5 character block should be '02'
+ * Fifth character in 5 character block should be '03'
+ */
+ for (i = 0; i < 4; i++) {
+ if (p->buf[i * 0] != 2 || p->buf[i * 0 + 4] != 3) {
+ printf("Invalid packet\n");
+ return -1;
+ }
+ }
+
+ for (i = 0; i < 1; i++) {
+ ret = decode_extech_value(p->buf[5 * i + 2],
+ p->buf[5 * i + 3],
+ &(p->op[8 * i]));
+ if (ret) {
+ printf("Invalid packet, conversion failed\n");
+ return -1;
+ }
+ p->watts = strtod( &(p->op[8 * i]), NULL);
+ }
+ return 0;
+}
+
+
+static double extech_read(int fd)
+{
+ struct packet p;
+ fd_set read_fd;
+ struct timeval tv;
+ int ret;
+
+ if (fd < 0)
+ return 0.0;
+
+ FD_ZERO(&read_fd);
+ FD_SET(fd, &read_fd);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 500000;
+
+ memset(&p, 0, sizeof(p));
+
+ ret = select(fd + 1, &read_fd, NULL, NULL, &tv);
+ if (ret <= 0)
+ return -1;
+
+ ret = read(fd, &p.buf, 250);
+ if (ret < 0)
+ return ret;
+ p.len = ret;
+
+ if (!parse_packet(&p))
+ return p.watts;
+
+ return -1000.0;
+}
+
+extech_power_meter::extech_power_meter(const char *extech_name)
+{
+ rate = 0.0;
+ pt_strcpy(dev_name, extech_name);
+ int ret;
+
+ fd = open_device(dev_name);
+ if (fd < 0)
+ return;
+
+ ret = setup_serial_device(fd);
+ if (ret) {
+ close(fd);
+ fd = -1;
+ return;
+ }
+}
+
+
+void extech_power_meter::measure(void)
+{
+ /* trigger the extech to send data */
+ if(write(fd, " ", 1) == -1)
+ printf("Error: %s\n", strerror(errno));
+
+ rate = extech_read(fd);
+
+}
+
+void extech_power_meter::sample(void)
+{
+ ssize_t ret;
+ struct timespec tv;
+
+ tv.tv_sec = 0;
+ tv.tv_nsec = 200000000;
+ while (!end_thread) {
+ nanosleep(&tv, NULL);
+ /* trigger the extech to send data */
+ ret = write(fd, " ", 1);
+ if (ret < 0)
+ continue;
+
+ sum += extech_read(fd);
+ samples++;
+
+ }
+}
+
+extern "C"
+{
+ void* thread_proc(void *arg)
+ {
+ class extech_power_meter *parent;
+ parent = (class extech_power_meter*)arg;
+ parent->sample();
+ return 0;
+ }
+}
+
+void extech_power_meter::end_measurement(void)
+{
+ end_thread = 1;
+ pthread_join( thread, NULL);
+ if (samples){
+ rate = sum / samples;
+ }
+ else
+ measure();
+}
+
+void extech_power_meter::start_measurement(void)
+{
+ end_thread = 0;
+ sum = samples = 0;
+
+ if (pthread_create(&thread, NULL, thread_proc, this))
+ fprintf(stderr, "ERROR: extech measurement thread creation failed\n");
+
+}
+
+
+double extech_power_meter::power(void)
+{
+ return rate;
+}
diff --git a/src/measurement/extech.h b/src/measurement/extech.h
new file mode 100644
index 0000000..b7b330a
--- /dev/null
+++ b/src/measurement/extech.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2010, Intel Corporation
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+#ifndef __INCLUDE_GUARD_EXTECH_H
+#define __INCLUDE_GUARD_EXTECH_H
+
+#include <pthread.h>
+#include "measurement.h"
+
+class extech_power_meter: public power_meter {
+ char dev_name[256];
+ int fd;
+
+ double rate;
+ void measure(void);
+ double sum;
+ int samples;
+ int end_thread;
+ pthread_t thread;
+public:
+ extech_power_meter(const char *_dev_name);
+ virtual void start_measurement(void);
+ virtual void end_measurement(void);
+ virtual void sample(void);
+
+ virtual double power(void);
+ virtual double dev_capacity(void) { return 0.0; };
+};
+
+#endif
diff --git a/src/measurement/measurement.cpp b/src/measurement/measurement.cpp
new file mode 100644
index 0000000..caee24e
--- /dev/null
+++ b/src/measurement/measurement.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2010, Intel Corporation
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+#include "measurement.h"
+#include "acpi.h"
+#include "extech.h"
+#include "sysfs.h"
+#include "opal-sensors.h"
+#include "../parameters/parameters.h"
+#include "../lib.h"
+
+#include <string>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <fstream>
+#include <unistd.h>
+#include <time.h>
+
+double min_power = 50000.0;
+
+void power_meter::start_measurement(void)
+{
+}
+
+
+void power_meter::end_measurement(void)
+{
+}
+
+
+double power_meter::power(void)
+{
+ return 0.0;
+}
+
+vector<class power_meter *> power_meters;
+
+static struct timespec tlast;
+
+void start_power_measurement(void)
+{
+ unsigned int i;
+ clock_gettime(CLOCK_REALTIME, &tlast);
+ for (i = 0; i < power_meters.size(); i++)
+ power_meters[i]->start_measurement();
+ all_results.joules = 0.0;
+}
+void end_power_measurement(void)
+{
+ unsigned int i;
+ for (i = 0; i < power_meters.size(); i++)
+ power_meters[i]->end_measurement();
+}
+
+double global_power(void)
+{
+ bool global_discharging = false;
+ double total = 0.0;
+ unsigned int i;
+
+ for (i = 0; i < power_meters.size(); i++) {
+ global_discharging |= power_meters[i]->is_discharging();
+ total += power_meters[i]->power();
+ }
+
+ /* report global time left if at least one battery is discharging */
+ if (!global_discharging)
+ return 0.0;
+
+ all_results.power = total;
+ if (total < min_power && total > 0.01)
+ min_power = total;
+ return total;
+}
+
+void global_sample_power(void)
+{
+ struct timespec tnow;
+
+ clock_gettime(CLOCK_REALTIME, &tnow);
+ /* power * time = joules */
+ all_results.joules += global_power() * \
+ ( ((double)tnow.tv_sec + 1.0e-9*tnow.tv_nsec) - \
+ ((double)tlast.tv_sec + 1.0e-9*tlast.tv_nsec));
+ tlast = tnow;
+}
+
+double global_joules(void)
+{
+ return all_results.joules;
+}
+
+double global_time_left(void)
+{
+ bool global_discharging = false;
+ double total_capacity = 0.0;
+ double total_rate = 0.0;
+ unsigned int i;
+ for (i = 0; i < power_meters.size(); i++) {
+ global_discharging |= power_meters[i]->is_discharging();
+ total_capacity += power_meters[i]->dev_capacity();
+ total_rate += power_meters[i]->power();
+ }
+ /* report global time left if at least one battery is discharging */
+ if (!global_discharging)
+ return 0.0;
+
+ /* return 0.0 instead of INF+ */
+ if (total_rate < 0.001)
+ return 0.0;
+ return total_capacity / total_rate;
+}
+
+void sysfs_power_meters_callback(const char *d_name)
+{
+ std::string type = read_sysfs_string("/sys/class/power_supply/%s/type", d_name);
+
+ if (type != "Battery" && type != "UPS")
+ return;
+
+ class sysfs_power_meter *meter;
+ meter = new(std::nothrow) class sysfs_power_meter(d_name);
+ if (meter)
+ power_meters.push_back(meter);
+}
+
+void acpi_power_meters_callback(const char *d_name)
+{
+ class acpi_power_meter *meter;
+ meter = new(std::nothrow) class acpi_power_meter(d_name);
+ if (meter)
+ power_meters.push_back(meter);
+}
+
+void sysfs_opal_sensors_callback(const char *d_name)
+{
+ class opal_sensors_power_meter *meter;
+ const char *c;
+
+ /* Those that end in / are directories and we don't want them */
+ c = strrchr(d_name, '/');
+ if (c && *(c+1) == '\0')
+ return;
+
+ meter = new(std::nothrow) class opal_sensors_power_meter(d_name);
+ if (meter)
+ power_meters.push_back(meter);
+}
+
+void detect_power_meters(void)
+{
+ process_directory("/sys/class/power_supply", sysfs_power_meters_callback);
+ process_glob("/sys/devices/platform/opal-sensor/hwmon/hwmon*/power*", sysfs_opal_sensors_callback);
+ if (power_meters.size() == 0) {
+ process_directory("/proc/acpi/battery", acpi_power_meters_callback);
+ }
+}
+
+void extech_power_meter(const char *devnode)
+{
+ class extech_power_meter *meter;
+
+ meter = new class extech_power_meter(devnode);
+
+ power_meters.push_back(meter);
+}
diff --git a/src/measurement/measurement.h b/src/measurement/measurement.h
new file mode 100644
index 0000000..9a84bc2
--- /dev/null
+++ b/src/measurement/measurement.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2010, Intel Corporation
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Arjan van de Ven <arjan@linux.intel.com>
+ */
+#ifndef __INCLUDE_GUARD_MEASUREMENT_H
+#define __INCLUDE_GUARD_MEASUREMENT_H
+
+#include <vector>
+
+using namespace std;
+
+class power_meter {
+ bool discharging = false;
+public:
+ virtual ~power_meter() {};
+
+ virtual void start_measurement(void);
+ virtual void end_measurement(void);
+ virtual double power(void);
+
+ virtual double dev_capacity(void)
+ {
+ return 0.0; /* in Joules */
+ }
+
+ virtual void set_discharging(bool d)
+ {
+ discharging = d;
+ }
+
+ virtual bool is_discharging()
+ {
+ return discharging;
+ }
+};
+
+extern vector<class power_meter *> power_meters;
+
+extern void start_power_measurement(void);
+extern void end_power_measurement(void);
+extern double global_power(void);
+extern void global_sample_power(void);
+extern double global_joules(void);
+extern double global_time_left(void);
+
+extern void detect_power_meters(void);
+extern void extech_power_meter(const char *devnode);
+
+extern double min_power;
+
+#endif
diff --git a/src/measurement/opal-sensors.cpp b/src/measurement/opal-sensors.cpp
new file mode 100644
index 0000000..a70ec56
--- /dev/null
+++ b/src/measurement/opal-sensors.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015 IBM Corp.
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Stewart Smith <stewart@linux.vnet.ibm.com>
+ */
+#include "measurement.h"
+#include "opal-sensors.h"
+#include "../lib.h"
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+opal_sensors_power_meter::opal_sensors_power_meter(const char *power_supply_name)
+{
+ strncpy(name, power_supply_name, sizeof(name));
+}
+
+double opal_sensors_power_meter::power(void)
+{
+ bool ok;
+ int value;
+ double r = 0;
+
+ value = read_sysfs(name, &ok);
+
+ if(ok)
+ r = value / 1000000.0;
+ return r;
+}
diff --git a/src/measurement/opal-sensors.h b/src/measurement/opal-sensors.h
new file mode 100644
index 0000000..7962091
--- /dev/null
+++ b/src/measurement/opal-sensors.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2015 IBM Corp.
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Stewart Smith <stewart@linux.vnet.ibm.com>
+ */
+#ifndef INCLUDE_GUARD_OPAL_SENSORS_H
+#define INCLUDE_GUARD_OPAL_SENSORS_H
+
+#include "measurement.h"
+#include <limits.h>
+
+class opal_sensors_power_meter: public power_meter {
+ char name[PATH_MAX];
+public:
+ opal_sensors_power_meter(const char *power_supply_name);
+ virtual void start_measurement(void) {};
+ virtual void end_measurement(void) {};
+
+ virtual double power(void);
+ virtual double dev_capacity(void) { return 0.0; }
+};
+
+#endif
diff --git a/src/measurement/sysfs.cpp b/src/measurement/sysfs.cpp
new file mode 100644
index 0000000..c5a7a12
--- /dev/null
+++ b/src/measurement/sysfs.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2011 Anssi Hannula
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Anssi Hannula <anssi.hannula@iki.fi>
+ */
+#include "measurement.h"
+#include "sysfs.h"
+#include "../lib.h"
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+
+sysfs_power_meter::sysfs_power_meter(const char *power_supply_name)
+{
+ rate = 0.0;
+ capacity = 0.0;
+ pt_strcpy(name, power_supply_name);
+}
+
+bool sysfs_power_meter::get_sysfs_attr(const char *attribute, int *value)
+{
+ char filename[PATH_MAX];
+ bool ok;
+
+ snprintf(filename, sizeof(filename), "/sys/class/power_supply/%s/%s", name, attribute);
+ *value = read_sysfs(filename, &ok);
+
+ return ok;
+}
+
+bool sysfs_power_meter::is_present()
+{
+ int present = 0;
+
+ if (!get_sysfs_attr("present", &present))
+ return true; /* assume always present */
+
+ return present;
+}
+
+double sysfs_power_meter::get_voltage()
+{
+ int voltage;
+
+ if (!get_sysfs_attr("voltage_now", &voltage))
+ return -1.0;
+
+ /* µV to V */
+ return voltage / 1000000.0;
+}
+
+bool sysfs_power_meter::set_rate_from_power()
+{
+ int power;
+
+ if (!get_sysfs_attr("power_now", &power))
+ return false;
+
+ /* µW to W */
+ rate = power / 1000000.0;
+ return true;
+}
+
+bool sysfs_power_meter::set_rate_from_current(double voltage)
+{
+ int current;
+
+ if (!get_sysfs_attr("current_now", &current))
+ return false;
+
+ /* current: µA
+ * voltage: V
+ * rate: W */
+ rate = (current / 1000000.0) * voltage;
+ return true;
+}
+
+bool sysfs_power_meter::set_capacity_from_energy()
+{
+ int energy;
+
+ if (!get_sysfs_attr("energy_now", &energy))
+ return false;
+
+ /* µWh to J */
+ capacity = energy / 1000000.0 * 3600.0;
+ return true;
+}
+
+bool sysfs_power_meter::set_capacity_from_charge(double voltage)
+{
+ int charge;
+
+ if (!get_sysfs_attr("charge_now", &charge))
+ return false;
+
+ /* charge: µAh
+ * voltage: V
+ * capacity: J */
+ capacity = (charge / 1000000.0) * voltage * 3600.0;
+ return true;
+}
+
+void sysfs_power_meter::measure()
+{
+ bool got_rate = false;
+ bool got_capacity = false;
+
+ rate = 0.0;
+ capacity = 0.0;
+ this->set_discharging(false);
+
+ if (!is_present())
+ return;
+ /** do not jump over. we may have discharging battery */
+ if (read_sysfs_string("/sys/class/power_supply/%s/status", name) == "Discharging")
+ this->set_discharging(true);
+
+ got_rate = set_rate_from_power();
+ got_capacity = set_capacity_from_energy();
+
+ if (!got_rate || !got_capacity) {
+ double voltage = get_voltage();
+ if (voltage < 0.0)
+ return;
+ if (!got_rate)
+ set_rate_from_current(voltage);
+ if (!got_capacity)
+ set_capacity_from_charge(voltage);
+ }
+}
+
+void sysfs_power_meter::end_measurement(void)
+{
+ measure();
+}
+
+void sysfs_power_meter::start_measurement(void)
+{
+ /* Battery state is generally a lagging indication, lets only measure at the end */
+}
diff --git a/src/measurement/sysfs.h b/src/measurement/sysfs.h
new file mode 100644
index 0000000..5b440f5
--- /dev/null
+++ b/src/measurement/sysfs.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2011 Anssi Hannula
+ *
+ * This file is part of PowerTOP
+ *
+ * This program file 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; version 2 of the License.
+ *
+ * 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 in a file named COPYING; if not, write to the
+ * Free Software Foundation, Inc,
+ * 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ * or just google for it.
+ *
+ * Authors:
+ * Anssi Hannula <anssi.hannula@iki.fi>
+ */
+#ifndef INCLUDE_GUARD_SYSFS_H
+#define INCLUDE_GUARD_SYSFS_H
+
+#include "measurement.h"
+
+class sysfs_power_meter: public power_meter {
+ char name[256];
+
+ double capacity;
+ double rate;
+
+ bool get_sysfs_attr(const char *attribute, int *value);
+ bool is_present();
+ double get_voltage();
+
+ bool set_rate_from_power();
+ bool set_rate_from_current(double voltage);
+ bool set_capacity_from_energy();
+ bool set_capacity_from_charge(double voltage);
+
+ void measure();
+public:
+ sysfs_power_meter(const char *power_supply_name);
+ virtual void start_measurement(void);
+ virtual void end_measurement(void);
+
+ virtual double power(void) { return rate; }
+ virtual double dev_capacity(void) { return capacity; }
+};
+
+#endif