summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Berg <bberg@redhat.com>2022-06-07 16:24:03 +0200
committerBenjamin Berg <bberg@redhat.com>2022-06-21 11:16:01 +0200
commited2d5a25b3f11e2773c47470c4c4f432b04c9984 (patch)
treed401619e5744faa3f045f3edcf0c6365b2627225
parentd672eb1d908bcd7534ac325421f7841bd1dfec15 (diff)
downloadupower-ed2d5a25b3f11e2773c47470c4c4f432b04c9984.tar.gz
linux: Add new UpDeviceSupplyBattery class
This split the functionality found in UpDeviceSupply to handle batteries out and is based on the previously added UpDeviceBattery class.
-rw-r--r--src/linux/meson.build2
-rw-r--r--src/linux/up-device-supply-battery.c343
-rw-r--r--src/linux/up-device-supply-battery.h33
-rw-r--r--src/linux/up-device-supply.c2
-rw-r--r--src/linux/up-device-supply.h2
5 files changed, 381 insertions, 1 deletions
diff --git a/src/linux/meson.build b/src/linux/meson.build
index ad49f0e..1134e7c 100644
--- a/src/linux/meson.build
+++ b/src/linux/meson.build
@@ -10,6 +10,8 @@ upshared += { 'linux': static_library('upshared',
sources: [
'up-device-supply.c',
'up-device-supply.h',
+ 'up-device-supply-battery.c',
+ 'up-device-supply-battery.h',
'up-device-hid.c',
'up-device-hid.h',
'up-device-wup.c',
diff --git a/src/linux/up-device-supply-battery.c b/src/linux/up-device-supply-battery.c
new file mode 100644
index 0000000..195ae53
--- /dev/null
+++ b/src/linux/up-device-supply-battery.c
@@ -0,0 +1,343 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 David Zeuthen <davidz@redhat.com>
+ * Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gprintf.h>
+#include <glib/gi18n-lib.h>
+#include <glib-object.h>
+#include <gudev/gudev.h>
+
+#include "up-config.h"
+#include "up-common.h"
+#include "up-types.h"
+#include "up-constants.h"
+#include "up-device-supply-battery.h"
+
+/* For up_device_supply_get_state */
+#include "up-device-supply.h"
+
+enum {
+ PROP_0,
+ PROP_IGNORE_SYSTEM_PERCENTAGE
+};
+
+struct _UpDeviceSupplyBattery
+{
+ gboolean has_coldplug_values;
+ gboolean coldplug_units;
+ gdouble *energy_old;
+ GTimeVal *energy_old_timespec;
+ guint energy_old_first;
+ gdouble rate_old;
+ gboolean shown_invalid_voltage_warning;
+ gboolean ignore_system_percentage;
+};
+
+G_DEFINE_TYPE (UpDeviceSupplyBattery, up_device_supply_battery, UP_TYPE_DEVICE_BATTERY)
+
+static gdouble
+up_device_supply_battery_get_design_voltage (UpDeviceSupplyBattery *self,
+ GUdevDevice *native)
+{
+ gdouble voltage;
+ const gchar *device_type = NULL;
+
+ /* design maximum */
+ voltage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "voltage_max_design") / 1000000.0;
+ if (voltage > 1.00f) {
+ g_debug ("using max design voltage");
+ return voltage;
+ }
+
+ /* design minimum */
+ voltage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "voltage_min_design") / 1000000.0;
+ if (voltage > 1.00f) {
+ g_debug ("using min design voltage");
+ return voltage;
+ }
+
+ /* current voltage */
+ voltage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "voltage_present") / 1000000.0;
+ if (voltage > 1.00f) {
+ g_debug ("using present voltage");
+ return voltage;
+ }
+
+ /* current voltage, alternate form */
+ voltage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "voltage_now") / 1000000.0;
+ if (voltage > 1.00f) {
+ g_debug ("using present voltage (alternate)");
+ return voltage;
+ }
+
+ /* is this a USB device? */
+ device_type = g_udev_device_get_sysfs_attr (native, "type");
+ if (device_type != NULL && g_ascii_strcasecmp (device_type, "USB") == 0) {
+ g_debug ("USB device, so assuming 5v");
+ voltage = 5.0f;
+ return voltage;
+ }
+
+ /* no valid value found; display a warning the first time for each
+ * device */
+ if (!self->shown_invalid_voltage_warning) {
+ self->shown_invalid_voltage_warning = TRUE;
+ g_warning ("no valid voltage value found for device %s, assuming 10V",
+ g_udev_device_get_sysfs_path (native));
+ }
+ /* completely guess, to avoid getting zero values */
+ g_debug ("no voltage values for device %s, using 10V as approximation",
+ g_udev_device_get_sysfs_path (native));
+ voltage = 10.0f;
+
+ return voltage;
+}
+
+static char*
+get_sysfs_attr_uncached (GUdevDevice *native, const gchar *key)
+{
+ g_autofree char *value = NULL;
+
+ /* get value, and strip to remove spaces */
+ value = g_strdup (g_udev_device_get_sysfs_attr_uncached (native, key));
+ if (!value)
+ return NULL;
+
+ g_strstrip (value);
+ if (value[0] == '\0')
+ return NULL;
+
+ return g_steal_pointer (&value);
+}
+
+static gboolean
+up_device_supply_battery_refresh (UpDevice *device,
+ UpRefreshReason reason)
+{
+ UpDeviceSupplyBattery *self = UP_DEVICE_SUPPLY_BATTERY (device);
+ UpDeviceBattery *battery = UP_DEVICE_BATTERY (device);
+ GUdevDevice *native;
+ UpBatteryInfo info = { 0 };
+ UpBatteryValues values = { 0 };
+
+ native = G_UDEV_DEVICE (up_device_get_native (device));
+
+ /*
+ * Reload battery information.
+ * NOTE: If we assume that a udev event is guaranteed to happen, then
+ * we can restrict this to updates other than UP_REFRESH_POLL.
+ * NOTE: Only energy.full and cycle_count can change for a battery.
+ */
+ info.present = g_udev_device_get_sysfs_attr_as_boolean_uncached (native, "present");
+ if (!info.present) {
+ up_device_battery_update_info (battery, &info);
+ return TRUE;
+ }
+
+ info.vendor = up_make_safe_string (get_sysfs_attr_uncached (native, "manufacturer"));
+ info.model = up_make_safe_string (get_sysfs_attr_uncached (native, "model_name"));
+ info.serial = up_make_safe_string (get_sysfs_attr_uncached (native, "serial_number"));
+
+ info.voltage_design = up_device_supply_battery_get_design_voltage (self, native);
+ info.charge_cycles = g_udev_device_get_sysfs_attr_as_int_uncached (native, "cycle_count");
+
+ info.units = UP_BATTERY_UNIT_ENERGY;
+ info.energy.full = g_udev_device_get_sysfs_attr_as_double_uncached (native, "energy_full") / 1000000.0;
+ info.energy.design = g_udev_device_get_sysfs_attr_as_double_uncached (native, "energy_full_design") / 1000000.0;
+
+ /* Assume we couldn't read anything if energy.full is extremely small */
+ if (info.energy.full < 0.01) {
+ info.units = UP_BATTERY_UNIT_CHARGE;
+ info.energy.full = g_udev_device_get_sysfs_attr_as_double_uncached (native, "charge_full") / 1000000.0;
+ info.energy.design = g_udev_device_get_sysfs_attr_as_double_uncached (native, "charge_full_design") / 1000000.0;
+ }
+ info.technology = up_convert_device_technology (get_sysfs_attr_uncached (native, "technology"));
+
+ /* NOTE: We used to warn about full > design, but really that is prefectly fine to happen. */
+
+ /* Update the battery information (will only fire events for actual changes) */
+ up_device_battery_update_info (battery, &info);
+
+ /*
+ * Load runtime information.
+ * NOTE: If we assume that a udev event is guaranteed to happen, then
+ * we can restrict this to updates other than UP_REFRESH_POLL.
+ */
+ values.units = info.units;
+
+ values.voltage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "voltage_now") / 1000000.0;
+ if (values.voltage < 0.01)
+ values.voltage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "voltage_avg") / 1000000.0;
+
+
+ switch (values.units) {
+ case UP_BATTERY_UNIT_CHARGE:
+ /* QUIRK:
+ * Some batteries (Nexus 7?) may report a separate energy_now.
+ * This appears to be more accurate (as it takes into account
+ * the dropping voltage). However, we ignore this here for
+ * consistency (but we used to read it in the past).
+ *
+ * See https://bugs.freedesktop.org/show_bug.cgi?id=60104#c2
+ * whichs reports energy_now of 15.05 Wh while our calculation
+ * will be ~16.4Wh by multiplying charge with voltage).
+ */
+ values.energy.rate = fabs (g_udev_device_get_sysfs_attr_as_double_uncached (native, "current_now") / 1000000.0);
+ values.energy.cur = fabs (g_udev_device_get_sysfs_attr_as_double_uncached (native, "charge_now") / 1000000.0);
+ break;
+ case UP_BATTERY_UNIT_ENERGY:
+ values.energy.rate = fabs (g_udev_device_get_sysfs_attr_as_double_uncached (native, "power_now") / 1000000.0);
+ values.energy.cur = fabs (g_udev_device_get_sysfs_attr_as_double_uncached (native, "energy_now") / 1000000.0);
+ if (values.energy.cur < 0.01)
+ values.energy.cur = g_udev_device_get_sysfs_attr_as_double_uncached (native, "energy_avg") / 1000000.0;
+
+ /* Legacy case: If we have energy units but no power_now, then current_now is in uW. */
+ if (values.energy.rate < 0)
+ values.energy.rate = fabs (g_udev_device_get_sysfs_attr_as_double_uncached (native, "current_now") / 1000000.0);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* NOTE:
+ * The old code tried to special case the 0xffff ACPI value of the energy rate.
+ * That doesn't really make any sense after doing the floating point math.
+ */
+
+ if (!self->ignore_system_percentage) {
+ values.percentage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "capacity");
+ values.percentage = CLAMP(values.percentage, 0.0f, 100.0f);
+ }
+
+ values.state = up_device_supply_get_state (native);
+
+ values.temperature = g_udev_device_get_sysfs_attr_as_double_uncached (native, "temp") / 10.0;
+
+ up_device_battery_report (battery, &values, reason);
+
+ return TRUE;
+}
+
+/**
+ * up_device_supply_coldplug:
+ *
+ * Return %TRUE on success, %FALSE if we failed to get data and should be removed
+ **/
+static gboolean
+up_device_supply_coldplug (UpDevice *device)
+{
+ GUdevDevice *native;
+ const gchar *native_path;
+ const gchar *scope;
+ const gchar *type;
+
+ /* detect what kind of device we are */
+ native = G_UDEV_DEVICE (up_device_get_native (device));
+ native_path = g_udev_device_get_sysfs_path (native);
+ if (native_path == NULL) {
+ g_warning ("could not get native path for %p", device);
+ return FALSE;
+ }
+
+ /* try to work out if the device is powering the system */
+ scope = g_udev_device_get_sysfs_attr (native, "scope");
+ if (scope != NULL && g_ascii_strcasecmp (scope, "device") == 0)
+ return FALSE;
+
+ /* Complain about a non-system scope, while accepting no scope information */
+ if (scope != NULL && g_ascii_strcasecmp (scope, "system") != 0)
+ g_warning ("Assuming system scope even though scope is %s", scope);
+
+ /* type should be a battery, but also accept unknown if "online" does not exist */
+ type = g_udev_device_get_sysfs_attr (native, "type");
+ if (!type || g_ascii_strcasecmp (type, "battery") != 0) {
+ if (g_udev_device_has_sysfs_attr (native, "online"))
+ return FALSE;
+
+ /* this is a good guess as UPS and CSR are not in the kernel */
+ g_warning ("Assuming battery as sysfs attribute 'type' is %s", type);
+ }
+
+ return TRUE;
+}
+
+static void
+up_device_supply_battery_init (UpDeviceSupplyBattery *self)
+{
+}
+
+static void
+up_device_supply_battery_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ UpDeviceSupplyBattery *self = UP_DEVICE_SUPPLY_BATTERY (object);
+
+ switch (property_id) {
+ case PROP_IGNORE_SYSTEM_PERCENTAGE:
+ self->ignore_system_percentage = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+up_device_supply_battery_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ UpDeviceSupplyBattery *self = UP_DEVICE_SUPPLY_BATTERY (object);
+
+ switch (property_id) {
+ case PROP_IGNORE_SYSTEM_PERCENTAGE:
+ g_value_set_flags (value, self->ignore_system_percentage);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
+ }
+}
+
+static void
+up_device_supply_battery_class_init (UpDeviceSupplyBatteryClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
+
+ object_class->set_property = up_device_supply_battery_set_property;
+ object_class->get_property = up_device_supply_battery_get_property;
+ device_class->coldplug = up_device_supply_coldplug;
+ device_class->refresh = up_device_supply_battery_refresh;
+
+ g_object_class_install_property (object_class, PROP_IGNORE_SYSTEM_PERCENTAGE,
+ g_param_spec_boolean ("ignore-system-percentage",
+ "Ignore system percentage",
+ "Ignore system provided battery percentage",
+ FALSE, G_PARAM_READWRITE));
+}
diff --git a/src/linux/up-device-supply-battery.h b/src/linux/up-device-supply-battery.h
new file mode 100644
index 0000000..1b6c531
--- /dev/null
+++ b/src/linux/up-device-supply-battery.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2022 Benjamin Berg <bberg@redhat.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#pragma once
+
+#include "up-device-battery.h"
+
+G_BEGIN_DECLS
+
+#define MAX_DISCHARGE_RATE 300
+
+#define UP_TYPE_DEVICE_SUPPLY_BATTERY (up_device_supply_battery_get_type ())
+
+G_DECLARE_FINAL_TYPE (UpDeviceSupplyBattery, up_device_supply_battery, UP, DEVICE_SUPPLY_BATTERY, UpDeviceBattery)
+
+G_END_DECLS
diff --git a/src/linux/up-device-supply.c b/src/linux/up-device-supply.c
index ffa4ad6..0f2b835 100644
--- a/src/linux/up-device-supply.c
+++ b/src/linux/up-device-supply.c
@@ -395,7 +395,7 @@ up_device_supply_units_changed (UpDeviceSupply *supply,
return TRUE;
}
-static UpDeviceState
+UpDeviceState
up_device_supply_get_state (GUdevDevice *native)
{
UpDeviceState state;
diff --git a/src/linux/up-device-supply.h b/src/linux/up-device-supply.h
index 811c355..e95a636 100644
--- a/src/linux/up-device-supply.h
+++ b/src/linux/up-device-supply.h
@@ -49,6 +49,8 @@ typedef struct
GType up_device_supply_get_type (void);
+UpDeviceState up_device_supply_get_state (GUdevDevice *native);
+
G_END_DECLS
#endif /* __UP_DEVICE_SUPPLY_H__ */