mirror of
https://gitlab.freedesktop.org/upower/upower.git
synced 2025-12-20 05:30:03 +01:00
1. free the udev parent node automatically. 2. free new_model_name variable in the same scope.
743 lines
21 KiB
C
743 lines
21 KiB
C
/* -*- 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-types.h"
|
|
#include "up-constants.h"
|
|
#include "up-device-supply.h"
|
|
#include "up-common.h"
|
|
|
|
struct UpDeviceSupplyPrivate
|
|
{
|
|
gboolean has_coldplug_values;
|
|
gboolean shown_invalid_voltage_warning;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (UpDeviceSupply, up_device_supply, UP_TYPE_DEVICE)
|
|
|
|
static gboolean up_device_supply_refresh (UpDevice *device,
|
|
UpRefreshReason reason);
|
|
static UpDeviceKind up_device_supply_guess_type (GUdevDevice *native,
|
|
const char *native_path);
|
|
|
|
static gboolean
|
|
up_device_supply_refresh_line_power (UpDeviceSupply *supply,
|
|
UpRefreshReason reason)
|
|
{
|
|
UpDevice *device = UP_DEVICE (supply);
|
|
GUdevDevice *native;
|
|
gboolean online_old, online_new;
|
|
|
|
/* get new AC value */
|
|
native = G_UDEV_DEVICE (up_device_get_native (device));
|
|
|
|
g_object_get (device,
|
|
"online", &online_old,
|
|
NULL);
|
|
online_new = g_udev_device_get_sysfs_attr_as_int_uncached (native, "online");
|
|
/* Avoid notification if the value did not change. */
|
|
if (online_old != online_new)
|
|
g_object_set (device,
|
|
"online", online_new,
|
|
NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* up_device_supply_reset_values:
|
|
**/
|
|
static void
|
|
up_device_supply_reset_values (UpDeviceSupply *supply)
|
|
{
|
|
supply->priv->has_coldplug_values = FALSE;
|
|
|
|
/* reset to default */
|
|
g_object_set (supply,
|
|
"vendor", NULL,
|
|
"model", NULL,
|
|
"serial", NULL,
|
|
"update-time", (guint64) 0,
|
|
"online", FALSE,
|
|
"energy", (gdouble) 0.0,
|
|
"is-present", FALSE,
|
|
"is-rechargeable", FALSE,
|
|
"has-history", FALSE,
|
|
"has-statistics", FALSE,
|
|
"state", UP_DEVICE_STATE_UNKNOWN,
|
|
"capacity", (gdouble) 0.0,
|
|
"energy-empty", (gdouble) 0.0,
|
|
"energy-full", (gdouble) 0.0,
|
|
"energy-full-design", (gdouble) 0.0,
|
|
"energy-rate", (gdouble) 0.0,
|
|
"voltage", (gdouble) 0.0,
|
|
"time-to-empty", (gint64) 0,
|
|
"time-to-full", (gint64) 0,
|
|
"percentage", (gdouble) 0.0,
|
|
"temperature", (gdouble) 0.0,
|
|
"technology", UP_DEVICE_TECHNOLOGY_UNKNOWN,
|
|
"charge-cycles", -1,
|
|
"charge-start-threshold", 0,
|
|
"charge-end-threshold", 100,
|
|
"charge-threshold-enabled", FALSE,
|
|
"charge-threshold-supported", FALSE,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* up_device_supply_get_online:
|
|
**/
|
|
static gboolean
|
|
up_device_supply_get_online (UpDevice *device, gboolean *online)
|
|
{
|
|
UpDeviceSupply *supply = UP_DEVICE_SUPPLY (device);
|
|
UpDeviceKind type;
|
|
gboolean online_tmp;
|
|
|
|
g_return_val_if_fail (UP_IS_DEVICE_SUPPLY (supply), FALSE);
|
|
g_return_val_if_fail (online != NULL, FALSE);
|
|
|
|
g_object_get (device,
|
|
"type", &type,
|
|
"online", &online_tmp,
|
|
NULL);
|
|
|
|
if (type != UP_DEVICE_KIND_LINE_POWER)
|
|
return FALSE;
|
|
|
|
*online = online_tmp;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* up_device_supply_get_string:
|
|
**/
|
|
static gchar *
|
|
up_device_supply_get_string (GUdevDevice *native, const gchar *key)
|
|
{
|
|
gchar *value;
|
|
|
|
/* get value, and strip to remove spaces */
|
|
value = g_strdup (g_udev_device_get_sysfs_attr_uncached (native, key));
|
|
if (value)
|
|
g_strstrip (value);
|
|
|
|
/* no value */
|
|
if (value == NULL)
|
|
goto out;
|
|
|
|
/* empty value */
|
|
if (value[0] == '\0') {
|
|
g_free (value);
|
|
value = NULL;
|
|
goto out;
|
|
}
|
|
out:
|
|
return value;
|
|
}
|
|
|
|
UpDeviceState
|
|
up_device_supply_get_state (GUdevDevice *native)
|
|
{
|
|
UpDeviceState state;
|
|
gchar *status;
|
|
|
|
status = up_device_supply_get_string (native, "status");
|
|
if (status == NULL ||
|
|
g_ascii_strcasecmp (status, "unknown") == 0 ||
|
|
*status == '\0') {
|
|
state = UP_DEVICE_STATE_UNKNOWN;
|
|
} else if (g_ascii_strcasecmp (status, "charging") == 0)
|
|
state = UP_DEVICE_STATE_CHARGING;
|
|
else if (g_ascii_strcasecmp (status, "discharging") == 0)
|
|
state = UP_DEVICE_STATE_DISCHARGING;
|
|
else if (g_ascii_strcasecmp (status, "full") == 0)
|
|
state = UP_DEVICE_STATE_FULLY_CHARGED;
|
|
else if (g_ascii_strcasecmp (status, "empty") == 0)
|
|
state = UP_DEVICE_STATE_EMPTY;
|
|
else if (g_ascii_strcasecmp (status, "not charging") == 0)
|
|
state = UP_DEVICE_STATE_PENDING_CHARGE;
|
|
else {
|
|
g_warning ("unknown status string: %s", status);
|
|
state = UP_DEVICE_STATE_UNKNOWN;
|
|
}
|
|
|
|
g_free (status);
|
|
|
|
return state;
|
|
}
|
|
|
|
static gdouble
|
|
sysfs_get_capacity_level (GUdevDevice *native,
|
|
UpDeviceLevel *level)
|
|
{
|
|
char *str;
|
|
gdouble ret = -1.0;
|
|
guint i;
|
|
struct {
|
|
const char *str;
|
|
gdouble percentage;
|
|
UpDeviceLevel level;
|
|
} levels[] = {
|
|
/* In order of most likely to least likely,
|
|
* Keep in sync with up_daemon_compute_warning_level() */
|
|
{ "Normal", 55.0, UP_DEVICE_LEVEL_NORMAL },
|
|
{ "High", 70.0, UP_DEVICE_LEVEL_HIGH },
|
|
{ "Low", 10.0, UP_DEVICE_LEVEL_LOW },
|
|
{ "Critical", 5.0, UP_DEVICE_LEVEL_CRITICAL },
|
|
{ "Full", 100.0, UP_DEVICE_LEVEL_FULL },
|
|
{ "Unknown", 50.0, UP_DEVICE_LEVEL_UNKNOWN }
|
|
};
|
|
|
|
g_return_val_if_fail (level != NULL, -1.0);
|
|
|
|
if (!g_udev_device_has_sysfs_attr_uncached (native, "capacity_level")) {
|
|
g_debug ("capacity_level doesn't exist, skipping");
|
|
*level = UP_DEVICE_LEVEL_NONE;
|
|
return -1.0;
|
|
}
|
|
|
|
*level = UP_DEVICE_LEVEL_UNKNOWN;
|
|
str = g_strchomp (g_strdup (g_udev_device_get_sysfs_attr_uncached (native, "capacity_level")));
|
|
if (!str) {
|
|
g_debug ("Failed to read capacity_level!");
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS(levels); i++) {
|
|
if (strcmp (levels[i].str, str) == 0) {
|
|
ret = levels[i].percentage;
|
|
*level = levels[i].level;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret < 0.0)
|
|
g_debug ("Could not find a percentage for capacity level '%s'", str);
|
|
|
|
g_free (str);
|
|
return ret;
|
|
}
|
|
|
|
static gboolean
|
|
up_device_supply_refresh_device (UpDeviceSupply *supply,
|
|
UpRefreshReason reason)
|
|
{
|
|
UpDeviceState state;
|
|
UpDevice *device = UP_DEVICE (supply);
|
|
GUdevDevice *native;
|
|
gdouble percentage = 0.0f;
|
|
UpDeviceLevel level = UP_DEVICE_LEVEL_NONE;
|
|
gboolean is_present = TRUE;
|
|
|
|
native = G_UDEV_DEVICE (up_device_get_native (device));
|
|
|
|
/* initial values */
|
|
if (!supply->priv->has_coldplug_values) {
|
|
gchar *model_name;
|
|
gchar *serial_number;
|
|
|
|
/* get values which may be blank */
|
|
model_name = up_device_supply_get_string (native, "model_name");
|
|
serial_number = up_device_supply_get_string (native, "serial_number");
|
|
|
|
/* some vendors fill this with binary garbage */
|
|
up_make_safe_string (model_name);
|
|
up_make_safe_string (serial_number);
|
|
|
|
g_object_set (device,
|
|
"model", model_name,
|
|
"serial", serial_number,
|
|
"is-rechargeable", TRUE,
|
|
"has-history", TRUE,
|
|
"has-statistics", TRUE,
|
|
NULL);
|
|
|
|
/* we only coldplug once, as these values will never change */
|
|
supply->priv->has_coldplug_values = TRUE;
|
|
|
|
g_free (model_name);
|
|
g_free (serial_number);
|
|
}
|
|
|
|
/* Some devices change whether they're present or not */
|
|
if (g_udev_device_has_sysfs_attr_uncached (native, "present"))
|
|
is_present = g_udev_device_get_sysfs_attr_as_boolean_uncached (native, "present");
|
|
|
|
/* get a precise percentage */
|
|
percentage = g_udev_device_get_sysfs_attr_as_double_uncached (native, "capacity");
|
|
if (percentage == 0.0f)
|
|
percentage = sysfs_get_capacity_level (native, &level);
|
|
|
|
if (percentage < 0.0) {
|
|
/* Probably talking to the device over Bluetooth */
|
|
state = UP_DEVICE_STATE_UNKNOWN;
|
|
g_object_set (device,
|
|
"state", state,
|
|
"is-present", is_present,
|
|
NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
state = up_device_supply_get_state (native);
|
|
|
|
/* Override whatever the device might have told us
|
|
* because a number of them are always discharging */
|
|
if (percentage == 100.0)
|
|
state = UP_DEVICE_STATE_FULLY_CHARGED;
|
|
|
|
g_object_set (device,
|
|
"percentage", percentage,
|
|
"battery-level", level,
|
|
"state", state,
|
|
"is-present", is_present,
|
|
NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
up_device_supply_sibling_discovered_guess_type (UpDevice *device,
|
|
GObject *sibling)
|
|
{
|
|
GUdevDevice *input, *native_device;
|
|
g_autoptr (GUdevDevice) parent_device = NULL;
|
|
g_autoptr (GUdevDevice) parent_sibling = NULL;
|
|
UpDeviceKind cur_type, new_type;
|
|
gboolean is_same_parent = FALSE;
|
|
char *new_model_name;
|
|
char *model_name;
|
|
char *serial_number;
|
|
int i;
|
|
struct {
|
|
const char *prop;
|
|
UpDeviceKind type;
|
|
} types[] = {
|
|
/* In order of type priority (*within* one input node). */
|
|
{ "SOUND_INITIALIZED", UP_DEVICE_KIND_OTHER_AUDIO },
|
|
{ "ID_INPUT_TABLET", UP_DEVICE_KIND_TABLET },
|
|
{ "ID_INPUT_TOUCHPAD", UP_DEVICE_KIND_TOUCHPAD },
|
|
{ "ID_INPUT_MOUSE", UP_DEVICE_KIND_MOUSE },
|
|
{ "ID_INPUT_JOYSTICK", UP_DEVICE_KIND_GAMING_INPUT },
|
|
{ "ID_INPUT_KEYBOARD", UP_DEVICE_KIND_KEYBOARD },
|
|
};
|
|
/* The type priority if we have multiple siblings,
|
|
* i.e. we select the first of the current type of the found type.
|
|
* Give a new priority for device type since the GAMING_INPUT may include
|
|
* a keyboard, a touchpad, and... etc, for example Sony DualShock4 joystick.
|
|
* A mouse and a touchpad may include a mouse and a keyboard.
|
|
* Therefore, the priority is:
|
|
* 1. Gaming_input
|
|
* 2. Audio
|
|
* 3. Keyboard
|
|
* 4. Tablet
|
|
* 5. Touchpad
|
|
* 6. Mouse
|
|
*/
|
|
UpDeviceKind priority[] = {
|
|
UP_DEVICE_KIND_GAMING_INPUT,
|
|
UP_DEVICE_KIND_OTHER_AUDIO,
|
|
UP_DEVICE_KIND_KEYBOARD,
|
|
UP_DEVICE_KIND_TABLET,
|
|
UP_DEVICE_KIND_TOUCHPAD,
|
|
UP_DEVICE_KIND_MOUSE
|
|
};
|
|
/* Form-factors set in rules.d/78-sound-card.rules in systemd */
|
|
struct {
|
|
const char *form_factor;
|
|
UpDeviceKind kind;
|
|
} sound_types[] = {
|
|
{ "webcam", UP_DEVICE_KIND_VIDEO },
|
|
{ "speaker", UP_DEVICE_KIND_SPEAKERS },
|
|
{ "headphone", UP_DEVICE_KIND_HEADPHONES },
|
|
{ "headset", UP_DEVICE_KIND_HEADSET },
|
|
/* unhandled:
|
|
* - handset
|
|
* - microphone */
|
|
};
|
|
|
|
input = G_UDEV_DEVICE (sibling);
|
|
|
|
/* Do not process if we already have a "good" guess for the device type. */
|
|
g_object_get (device, "type", &cur_type, NULL);
|
|
if (cur_type == UP_DEVICE_KIND_LINE_POWER)
|
|
return;
|
|
|
|
if (g_strcmp0 (g_udev_device_get_subsystem (input), "input") != 0 &&
|
|
g_strcmp0 (g_udev_device_get_subsystem (input), "sound") != 0)
|
|
return;
|
|
|
|
/* Only process "card" devices, as those are tagged with form-factor */
|
|
if (g_str_equal (g_udev_device_get_subsystem (input), "sound") &&
|
|
!g_str_has_prefix (g_udev_device_get_name (input), "card"))
|
|
return;
|
|
|
|
g_object_get (device,
|
|
"model", &model_name,
|
|
"serial", &serial_number,
|
|
NULL);
|
|
|
|
if (model_name == NULL) {
|
|
model_name = up_device_supply_get_string (input, "name");
|
|
up_make_safe_string (model_name);
|
|
g_object_set (device,
|
|
"model", model_name,
|
|
NULL);
|
|
g_free (model_name);
|
|
}
|
|
|
|
if (serial_number == NULL) {
|
|
serial_number = up_device_supply_get_string (input, "uniq");
|
|
up_make_safe_string (serial_number);
|
|
g_object_set (device,
|
|
"serial", serial_number,
|
|
NULL);
|
|
g_free (serial_number);
|
|
}
|
|
|
|
new_type = UP_DEVICE_KIND_UNKNOWN;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (types); i++) {
|
|
if (g_udev_device_get_property_as_boolean (input, types[i].prop)) {
|
|
new_type = types[i].type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (priority); i++) {
|
|
if (priority[i] == cur_type || priority[i] == new_type) {
|
|
new_type = priority[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Match audio sub-type */
|
|
if (new_type == UP_DEVICE_KIND_OTHER_AUDIO) {
|
|
const char *form_factor = g_udev_device_get_property (input, "SOUND_FORM_FACTOR");
|
|
g_debug ("Guessing audio sub-type from SOUND_FORM_FACTOR='%s'", form_factor);
|
|
for (i = 0; form_factor != NULL && i < G_N_ELEMENTS (sound_types); i++) {
|
|
if (g_strcmp0 (form_factor, sound_types[i].form_factor) == 0) {
|
|
new_type = sound_types[i].kind;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TODO: Add a heuristic here (and during initial discovery) that uses
|
|
* the model name.
|
|
*/
|
|
|
|
/* Fall back to "keyboard" if we didn't find anything. */
|
|
if (new_type == UP_DEVICE_KIND_UNKNOWN) {
|
|
if (cur_type != UP_DEVICE_KIND_UNKNOWN) {
|
|
g_debug ("Not overwriting existing type '%s'",
|
|
up_device_kind_to_string(cur_type));
|
|
return;
|
|
}
|
|
new_type = UP_DEVICE_KIND_KEYBOARD;
|
|
}
|
|
|
|
if (cur_type != new_type) {
|
|
native_device = G_UDEV_DEVICE (up_device_get_native (device));
|
|
parent_device = g_udev_device_get_parent (native_device);
|
|
parent_sibling = g_udev_device_get_parent (input);
|
|
|
|
g_debug ("Type changed from %s to %s",
|
|
up_device_kind_to_string (cur_type),
|
|
up_device_kind_to_string (new_type));
|
|
|
|
/* Check if the device and the sibling have the same parent. */
|
|
if (!g_strcmp0 (g_udev_device_get_sysfs_path (parent_device),
|
|
g_udev_device_get_sysfs_path (parent_sibling)))
|
|
is_same_parent = TRUE;
|
|
|
|
new_model_name = up_device_supply_get_string (input, "name");
|
|
/* The model name of a device component may be different. For example, DualSense
|
|
* joystick owns "Sony Interactive Entertainment DualSense Wireless Controller"
|
|
* for the joystick and "Sony Interactive Entertainment DualSense Wireless Controller
|
|
* Motion Sensors" for the accelerometer. If the type is change, the corresponding
|
|
* model name have to be changed too. */
|
|
if (new_model_name != NULL && is_same_parent) {
|
|
up_make_safe_string (new_model_name);
|
|
g_object_set (device,
|
|
"type", new_type,
|
|
"model", new_model_name,
|
|
NULL);
|
|
} else {
|
|
g_object_set (device, "type", new_type, NULL);
|
|
}
|
|
g_free (new_model_name);
|
|
}
|
|
}
|
|
|
|
static void
|
|
up_device_supply_sibling_discovered_handle_wireless_status (UpDevice *device,
|
|
GObject *obj)
|
|
{
|
|
const char *status;
|
|
GUdevDevice *sibling = G_UDEV_DEVICE (obj);
|
|
|
|
status = g_udev_device_get_sysfs_attr_uncached (sibling, "wireless_status");
|
|
if (!status)
|
|
return;
|
|
|
|
if (!g_str_equal (status, "connected") &&
|
|
!g_str_equal (status, "disconnected")) {
|
|
g_warning ("Unhandled wireless_status value '%s' on %s",
|
|
status, g_udev_device_get_sysfs_path (sibling));
|
|
return;
|
|
}
|
|
|
|
g_debug ("Detected wireless_status '%s' on %s",
|
|
status, g_udev_device_get_sysfs_path (sibling));
|
|
|
|
g_object_set (G_OBJECT (device),
|
|
"disconnected", g_str_equal (status, "disconnected"),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
up_device_supply_sibling_discovered (UpDevice *device,
|
|
GObject *sibling)
|
|
{
|
|
GUdevDevice *native;
|
|
|
|
if (!G_UDEV_IS_DEVICE (sibling))
|
|
return;
|
|
|
|
native = G_UDEV_DEVICE (up_device_get_native (device));
|
|
g_debug ("up_device_supply_sibling_discovered (device: %s, sibling: %s)",
|
|
g_udev_device_get_sysfs_path (native),
|
|
g_udev_device_get_sysfs_path (G_UDEV_DEVICE (sibling)));
|
|
|
|
up_device_supply_sibling_discovered_guess_type (device, sibling);
|
|
up_device_supply_sibling_discovered_handle_wireless_status (device, sibling);
|
|
}
|
|
|
|
static UpDeviceKind
|
|
up_device_supply_guess_type (GUdevDevice *native,
|
|
const char *native_path)
|
|
{
|
|
gchar *device_type;
|
|
UpDeviceKind type = UP_DEVICE_KIND_UNKNOWN;
|
|
|
|
device_type = up_device_supply_get_string (native, "type");
|
|
if (device_type == NULL)
|
|
return type;
|
|
|
|
if (g_ascii_strcasecmp (device_type, "mains") == 0) {
|
|
type = UP_DEVICE_KIND_LINE_POWER;
|
|
goto out;
|
|
}
|
|
|
|
if (g_ascii_strcasecmp (device_type, "battery") == 0) {
|
|
type = UP_DEVICE_KIND_BATTERY;
|
|
|
|
} else if (g_ascii_strcasecmp (device_type, "USB") == 0) {
|
|
|
|
/* USB supplies should have a usb_type attribute which we would
|
|
* ideally decode further.
|
|
*
|
|
* For historic reasons, we have a heuristic for wacom tablets
|
|
* that can be dropped in the future.
|
|
* As of May 2022, it is expected to be fixed in kernel 5.19.
|
|
* https://patchwork.kernel.org/project/linux-input/patch/20220407115406.115112-1-hadess@hadess.net/
|
|
*/
|
|
if (g_udev_device_has_sysfs_attr (native, "usb_type") &&
|
|
g_udev_device_has_sysfs_attr (native, "online"))
|
|
type = UP_DEVICE_KIND_LINE_POWER;
|
|
else if (g_strstr_len (native_path, -1, "wacom_") != NULL)
|
|
type = UP_DEVICE_KIND_TABLET;
|
|
else
|
|
g_warning ("USB power supply %s without usb_type property, please report",
|
|
native_path);
|
|
} else {
|
|
g_warning ("did not recognize type %s, please report", device_type);
|
|
}
|
|
|
|
out:
|
|
g_free (device_type);
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
UpDeviceSupply *supply = UP_DEVICE_SUPPLY (device);
|
|
GUdevDevice *native;
|
|
const gchar *native_path;
|
|
const gchar *scope;
|
|
UpDeviceKind type;
|
|
gboolean is_power_supply;
|
|
|
|
up_device_supply_reset_values (supply);
|
|
|
|
/* 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) {
|
|
is_power_supply = FALSE;
|
|
} else if (scope != NULL && g_ascii_strcasecmp (scope, "system") == 0) {
|
|
is_power_supply = TRUE;
|
|
} else {
|
|
g_debug ("taking a guess for power supply scope");
|
|
is_power_supply = TRUE;
|
|
}
|
|
|
|
/* we don't use separate ACs for devices */
|
|
if (is_power_supply == FALSE &&
|
|
!g_udev_device_has_sysfs_attr_uncached (native, "capacity") &&
|
|
!g_udev_device_has_sysfs_attr_uncached (native, "capacity_level")) {
|
|
g_debug ("Ignoring device AC, we'll monitor the device battery");
|
|
return FALSE;
|
|
}
|
|
|
|
/* try to detect using the device type */
|
|
type = up_device_supply_guess_type (native, native_path);
|
|
|
|
/* if reading the device type did not work, use heuristic */
|
|
if (type == UP_DEVICE_KIND_UNKNOWN) {
|
|
if (g_udev_device_has_sysfs_attr_uncached (native, "online")) {
|
|
g_debug ("'online' attribute was found. "
|
|
"Assume it is a line power supply.");
|
|
type = UP_DEVICE_KIND_LINE_POWER;
|
|
} else {
|
|
/*
|
|
* This might be a battery or a UPS, but it might also
|
|
* be something else that we really don't know how to
|
|
* handle (e.g. BMS, exposed by Android-centric vendor
|
|
* kernels in parallel to actual battery).
|
|
*
|
|
* As such, we have no choice but to assume that we
|
|
* can't handle this device, and ignore it.
|
|
*/
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* set the value */
|
|
g_object_set (device,
|
|
"type", type,
|
|
"power-supply", is_power_supply,
|
|
NULL);
|
|
|
|
/* Handled by separate battery class */
|
|
if (is_power_supply)
|
|
g_assert (type == UP_DEVICE_KIND_LINE_POWER);
|
|
|
|
if (type != UP_DEVICE_KIND_LINE_POWER)
|
|
g_object_set (device, "poll-timeout", UP_DAEMON_SHORT_TIMEOUT, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
up_device_supply_refresh (UpDevice *device, UpRefreshReason reason)
|
|
{
|
|
gboolean updated;
|
|
UpDeviceSupply *supply = UP_DEVICE_SUPPLY (device);
|
|
UpDeviceKind type;
|
|
|
|
g_object_get (device,
|
|
"type", &type,
|
|
NULL);
|
|
if (type == UP_DEVICE_KIND_LINE_POWER) {
|
|
updated = up_device_supply_refresh_line_power (supply, reason);
|
|
} else {
|
|
updated = up_device_supply_refresh_device (supply, reason);
|
|
}
|
|
|
|
/* reset time if we got new data */
|
|
if (updated)
|
|
g_object_set (device, "update-time", (guint64) g_get_real_time () / G_USEC_PER_SEC, NULL);
|
|
|
|
return updated;
|
|
}
|
|
|
|
/**
|
|
* up_device_supply_init:
|
|
**/
|
|
static void
|
|
up_device_supply_init (UpDeviceSupply *supply)
|
|
{
|
|
supply->priv = up_device_supply_get_instance_private (supply);
|
|
|
|
supply->priv->shown_invalid_voltage_warning = FALSE;
|
|
}
|
|
|
|
/**
|
|
* up_device_supply_finalize:
|
|
**/
|
|
static void
|
|
up_device_supply_finalize (GObject *object)
|
|
{
|
|
UpDeviceSupply *supply;
|
|
|
|
g_return_if_fail (object != NULL);
|
|
g_return_if_fail (UP_IS_DEVICE_SUPPLY (object));
|
|
|
|
supply = UP_DEVICE_SUPPLY (object);
|
|
g_return_if_fail (supply->priv != NULL);
|
|
|
|
G_OBJECT_CLASS (up_device_supply_parent_class)->finalize (object);
|
|
}
|
|
|
|
/**
|
|
* up_device_supply_class_init:
|
|
**/
|
|
static void
|
|
up_device_supply_class_init (UpDeviceSupplyClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
UpDeviceClass *device_class = UP_DEVICE_CLASS (klass);
|
|
|
|
object_class->finalize = up_device_supply_finalize;
|
|
|
|
device_class->get_online = up_device_supply_get_online;
|
|
device_class->coldplug = up_device_supply_coldplug;
|
|
device_class->sibling_discovered = up_device_supply_sibling_discovered;
|
|
device_class->refresh = up_device_supply_refresh;
|
|
}
|