From 6f1cd0776b40ad2067595219a53d8f2531862d46 Mon Sep 17 00:00:00 2001 From: Anssi Hannula Date: Sat, 26 Nov 2011 20:27:45 +0200 Subject: [PATCH 2/5] measurement: add sysfs power_supply class support ACPI battery status in /proc/acpi/battery is deprecated in recent Linux kernels, and missing when CONFIG_ACPI_PROCFS_POWER is disabled. Add support for getting the battery readout from /sys/class/power_supply and use it instead of /proc/acpi when available. Signed-off-by: Anssi Hannula --- Makefile | 2 +- measurement/measurement.cpp | 22 ++++++- measurement/sysfs.cpp | 158 +++++++++++++++++++++++++++++++++++++++++++ measurement/sysfs.h | 55 +++++++++++++++ 4 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 measurement/sysfs.cpp create mode 100644 measurement/sysfs.h diff --git a/Makefile b/Makefile index b0b6cb7..36b39e8 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ OBJS += perf/perf.o perf/perf_bundle.o OBJS += process/process.o process/do_process.o process/interrupt.o process/timer.o process/work.o process/powerconsumer.o process/device.o DEVS += devices/device.o devices/backlight.o devices/usb.o devices/ahci.o devices/alsa.o devices/rfkill.o devices/i915-gpu.o devices/thinkpad-fan.o devices/network.o devices/thinkpad-light.o DEVS += devices/runtime_pm.o -DEVS += measurement/measurement.o measurement/acpi.o measurement/extech.o +DEVS += measurement/measurement.o measurement/acpi.o measurement/extech.o measurement/sysfs.o OBJS += $(DEVS) OBJS += parameters/parameters.o parameters/learn.o parameters/persistent.o OBJS += calibrate/calibrate.o diff --git a/measurement/measurement.cpp b/measurement/measurement.cpp index 3dbaab9..f16cd32 100644 --- a/measurement/measurement.cpp +++ b/measurement/measurement.cpp @@ -25,9 +25,11 @@ #include "measurement.h" #include "acpi.h" #include "extech.h" +#include "sysfs.h" #include "../parameters/parameters.h" #include "../lib.h" +#include #include #include @@ -103,7 +105,20 @@ double global_time_left(void) return total; } -void power_meters_callback(const char *d_name) +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); @@ -113,7 +128,10 @@ void power_meters_callback(const char *d_name) void detect_power_meters(void) { - process_directory("/proc/acpi/battery", power_meters_callback); + process_directory("/sys/class/power_supply", sysfs_power_meters_callback); + if (power_meters.size() == 0) { + process_directory("/proc/acpi/battery", acpi_power_meters_callback); + } } void extech_power_meter(const char *devnode) diff --git a/measurement/sysfs.cpp b/measurement/sysfs.cpp new file mode 100644 index 0000000..58565ee --- /dev/null +++ b/measurement/sysfs.cpp @@ -0,0 +1,158 @@ +/* + * 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 + */ +#include "measurement.h" +#include "sysfs.h" +#include "../lib.h" +#include +#include + +sysfs_power_meter::sysfs_power_meter(const char *power_supply_name) +{ + rate = 0.0; + capacity = 0.0; + strncpy(name, power_supply_name, sizeof(name)); +} + +bool sysfs_power_meter::get_sysfs_attr(const char *attribute, int *value) +{ + char filename[4096]; + 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", ¤t)) + 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; + + if (!is_present()) + return; + if (read_sysfs_string("/sys/class/power_supply/%s/status", name) != "Discharging") + return; + + 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/measurement/sysfs.h b/measurement/sysfs.h new file mode 100644 index 0000000..36d259a --- /dev/null +++ b/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 + */ +#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 joules_consumed(void) { return rate; } + virtual double dev_capacity(void) { return capacity; } +}; + +#endif -- 1.7.7.2