up-device-supply: read charge_type from the supplying charger
On a discrete-PMIC platform the fuel gauge and the charger are separate devices and only the charger knows the charge regime. On SDM845 qcom-battery is fuel-gauge@4000 and exposes no charge_type at all, while pmi8998-charger is charger@1000 and does, so reading only the battery's own attribute returned UNKNOWN on every refresh. Follow the device links the kernel already publishes rather than guessing which sibling supply is the charger: <native>/device/supplier:* -> /sys/devices/virtual/devlink/<s>--<c> <devlink>/supplier -> the supplying device <supplier>/power_supply/<name>/charge_type The battery's own attribute still wins when present, so this is inert on hardware where the gauge reports charge_type itself.
This commit is contained in:
parent
628283fb20
commit
718e9210d7
1 changed files with 101 additions and 2 deletions
|
|
@ -198,12 +198,109 @@ up_device_supply_get_state (GUdevDevice *native)
|
|||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_supply_get_supplier_charge_type_str:
|
||||
*
|
||||
* Reads `charge_type` from the power supply that *supplies* @native.
|
||||
*
|
||||
* On a discrete-PMIC platform the fuel gauge and the charger are separate
|
||||
* devices, and only the charger knows the charge regime. Qualcomm SDM845 is
|
||||
* the case in hand: `qcom-battery` is `fuel-gauge@4000` and exposes no
|
||||
* `charge_type` at all, while `pmi8998-charger` is `charger@1000` and does.
|
||||
* Reading only @native's own attribute yields UNKNOWN forever on such a
|
||||
* device.
|
||||
*
|
||||
* The kernel already publishes the relation as device links, so follow it
|
||||
* instead of guessing which sibling supply is the charger:
|
||||
*
|
||||
* <native>/device/supplier:<...> -> /sys/devices/virtual/devlink/<s>--<c>
|
||||
* <devlink>/supplier -> the supplying device
|
||||
* <supplier>/power_supply/<name>/charge_type
|
||||
*
|
||||
* Returns the raw attribute string (caller frees), or %NULL when there is no
|
||||
* supplier exposing one.
|
||||
**/
|
||||
static gchar *
|
||||
up_device_supply_get_supplier_charge_type_str (GUdevDevice *native)
|
||||
{
|
||||
g_autoptr(GUdevDevice) parent = NULL;
|
||||
g_autoptr(GDir) dir = NULL;
|
||||
const gchar *parent_path;
|
||||
const gchar *entry;
|
||||
|
||||
parent = g_udev_device_get_parent (native);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
|
||||
parent_path = g_udev_device_get_sysfs_path (parent);
|
||||
if (parent_path == NULL)
|
||||
return NULL;
|
||||
|
||||
dir = g_dir_open (parent_path, 0, NULL);
|
||||
if (dir == NULL)
|
||||
return NULL;
|
||||
|
||||
while ((entry = g_dir_read_name (dir)) != NULL) {
|
||||
g_autofree gchar *link_path = NULL;
|
||||
g_autofree gchar *link_target = NULL;
|
||||
g_autofree gchar *devlink_path = NULL;
|
||||
g_autofree gchar *supplier_link = NULL;
|
||||
g_autofree gchar *supplier_target = NULL;
|
||||
g_autofree gchar *supplier_path = NULL;
|
||||
g_autofree gchar *ps_path = NULL;
|
||||
g_autoptr(GDir) ps_dir = NULL;
|
||||
const gchar *ps_entry;
|
||||
|
||||
if (!g_str_has_prefix (entry, "supplier:"))
|
||||
continue;
|
||||
|
||||
/* <native>/device/supplier:* -> the devlink node */
|
||||
link_path = g_build_filename (parent_path, entry, NULL);
|
||||
link_target = g_file_read_link (link_path, NULL);
|
||||
if (link_target == NULL)
|
||||
continue;
|
||||
devlink_path = g_canonicalize_filename (link_target, parent_path);
|
||||
|
||||
/* <devlink>/supplier -> the supplying device */
|
||||
supplier_link = g_build_filename (devlink_path, "supplier", NULL);
|
||||
supplier_target = g_file_read_link (supplier_link, NULL);
|
||||
if (supplier_target == NULL)
|
||||
continue;
|
||||
supplier_path = g_canonicalize_filename (supplier_target, devlink_path);
|
||||
|
||||
/* Not every supplier is a power supply — the SPMI bus controller
|
||||
* is a supplier here too, and has no power_supply child. */
|
||||
ps_path = g_build_filename (supplier_path, "power_supply", NULL);
|
||||
ps_dir = g_dir_open (ps_path, 0, NULL);
|
||||
if (ps_dir == NULL)
|
||||
continue;
|
||||
|
||||
while ((ps_entry = g_dir_read_name (ps_dir)) != NULL) {
|
||||
g_autofree gchar *attr_path = NULL;
|
||||
gchar *contents = NULL;
|
||||
|
||||
attr_path = g_build_filename (ps_path, ps_entry, "charge_type", NULL);
|
||||
if (!g_file_get_contents (attr_path, &contents, NULL, NULL))
|
||||
continue;
|
||||
|
||||
g_strstrip (contents);
|
||||
if (contents[0] != '\0')
|
||||
return contents;
|
||||
g_free (contents);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* up_device_supply_get_charge_type:
|
||||
*
|
||||
* Reads the kernel `charge_type` sysfs attribute and maps it to a
|
||||
* #UpDeviceChargeType. Returns %UP_DEVICE_CHARGE_TYPE_UNKNOWN if the
|
||||
* driver doesn't expose the attribute, and
|
||||
* #UpDeviceChargeType. When @native carries no such attribute the value is
|
||||
* resolved from the supplying charger instead (see
|
||||
* up_device_supply_get_supplier_charge_type_str()). Returns
|
||||
* %UP_DEVICE_CHARGE_TYPE_UNKNOWN only when neither exposes it, and
|
||||
* %UP_DEVICE_CHARGE_TYPE_NONE for an empty/unset value (no charger
|
||||
* active).
|
||||
**/
|
||||
|
|
@ -214,6 +311,8 @@ up_device_supply_get_charge_type (GUdevDevice *native)
|
|||
UpDeviceChargeType charge_type;
|
||||
|
||||
charge_type_str = up_device_supply_get_string (native, "charge_type");
|
||||
if (charge_type_str == NULL)
|
||||
charge_type_str = up_device_supply_get_supplier_charge_type_str (native);
|
||||
if (charge_type_str == NULL)
|
||||
return UP_DEVICE_CHARGE_TYPE_UNKNOWN;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue