From d387d08c74dee9a4fd402c23228dc4072b6483c0 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Wed, 6 May 2020 16:12:34 +0300 Subject: [PATCH] [battery-udev] Enablers for configurable charger types. JB#49693 For example pinephone exposes charger devices in a manner where the name is prefixed with chiptype (e.g. "axp813-ac" / "axp20x-usb"). As mce does not recognize these, both wall charger and pc connection gets treated as "other" type charger - which works to some extent but triggers incorrect UI actions. If direct match for power supply device type / name is not found, try to eliminate chipname prefix so that for example names such as "axp20x-usb" get treated as plain "usb". To future proof things, make it possible to override build-in defaults and heuristics via mce configuration files. Signed-off-by: Simo Piiroinen --- datapipe.c | 34 +++++++- inifiles/battery-udev-chargertypes.ini | 21 +++++ mce.h | 7 ++ modules/battery-udev.c | 116 ++++++++++++++++++++++--- 4 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 inifiles/battery-udev-chargertypes.ini diff --git a/datapipe.c b/datapipe.c index 918c444a..98055bfe 100644 --- a/datapipe.c +++ b/datapipe.c @@ -1655,6 +1655,37 @@ const char *usb_cable_state_to_dbus(usb_cable_state_t state) return res; } +/** Convert human readable name to charger_type_t enum + * + * @param name human readable charger type name + * + * @return charger_type_t enumeration value, or CHARGER_TYPE_INVALID + */ +charger_type_t +charger_type_parse(const char *name) +{ + charger_type_t type = CHARGER_TYPE_INVALID; + if( !name ) + mce_log(LL_WARN, "invalid charger type: %s", "null"); + else if( !strcmp(name, "none") ) + type = CHARGER_TYPE_NONE; + else if( !strcmp(name, "usb") ) + type = CHARGER_TYPE_USB; + else if( !strcmp(name, "dcp") ) + type = CHARGER_TYPE_DCP; + else if( !strcmp(name, "hwdcp") ) + type = CHARGER_TYPE_HVDCP; + else if( !strcmp(name, "cdp") ) + type = CHARGER_TYPE_CDP; + else if( !strcmp(name, "wireless") ) + type = CHARGER_TYPE_WIRELESS; + else if( !strcmp(name, "other") ) + type = CHARGER_TYPE_OTHER; + else + mce_log(LL_WARN, "invalid charger type: %s", name); + return type; +} + /** Convert charger_type_t enum to human readable string * * @param type charger_type_t enumeration value @@ -1664,7 +1695,7 @@ const char *usb_cable_state_to_dbus(usb_cable_state_t state) const char * charger_type_repr(charger_type_t type) { - const char *repr = "unknown"; + const char *repr = "invalid"; switch( type ) { case CHARGER_TYPE_NONE: repr = "none"; break; case CHARGER_TYPE_USB: repr = "usb"; break; @@ -1689,6 +1720,7 @@ charger_type_to_dbus(charger_type_t type) { const char *repr = MCE_CHARGER_TYPE_OTHER; switch( type ) { + case CHARGER_TYPE_INVALID: case CHARGER_TYPE_NONE: repr = MCE_CHARGER_TYPE_NONE; break; case CHARGER_TYPE_USB: repr = MCE_CHARGER_TYPE_USB; break; case CHARGER_TYPE_DCP: repr = MCE_CHARGER_TYPE_DCP; break; diff --git a/inifiles/battery-udev-chargertypes.ini b/inifiles/battery-udev-chargertypes.ini new file mode 100644 index 00000000..511af060 --- /dev/null +++ b/inifiles/battery-udev-chargertypes.ini @@ -0,0 +1,21 @@ +# Configuration file for MCE - udev battery plugin + +[BatteryUDevChargerTypes] + +# Charger type is derived primarily from POWER_SUPPLY_TYPE +# udev attribute, or if such property is not present from +# device name. + +# The built-in defaults are as follows: + +#ac=dcp +#unknown=none +#mains=dcp +#usb_hvdcp=hwdcp +#wireless=wireless +#cdp=cdp +#usb_aca=usb +#usb=usb +#usb_dcp=dcp +#usb_hvdcp_3=hwdcp +#usb_cdp=cdp diff --git a/mce.h b/mce.h index cb63467c..e4beb63c 100644 --- a/mce.h +++ b/mce.h @@ -358,6 +358,12 @@ const char *charger_state_to_dbus(charger_state_t state); */ typedef enum { + /* Placeholder value for lookup failures etc + */ + CHARGER_TYPE_INVALID, + + /* Value that signifies that no charger is connected + */ CHARGER_TYPE_NONE, /* Charger types that do not carry special meaning from @@ -377,6 +383,7 @@ typedef enum CHARGER_TYPE_USB, // Standard Downstream Port } charger_type_t; +charger_type_t charger_type_parse(const char *name); const char *charger_type_repr(charger_type_t type); const char *charger_type_to_dbus(charger_type_t type); diff --git a/modules/battery-udev.c b/modules/battery-udev.c index f255f489..c1e1ffb5 100644 --- a/modules/battery-udev.c +++ b/modules/battery-udev.c @@ -72,6 +72,9 @@ /** INI-file group for blacklisting devices */ #define MCE_CONF_BATTERY_UDEV_DEVICE_BLACKLIST_GROUP "BatteryUDevDeviceBlacklist" +/** INI-file group for configuring charger types */ +#define MCE_CONF_BATTERY_UDEV_DEVICE_CHARGERTYPE_GROUP "BatteryUDevChargerTypes" + /** Delay between udev notifications and battery state evaluation * * The purpose is to increase chances of getting battery and @@ -343,6 +346,9 @@ static GHashTable *udevproperty_type_lut = 0; /** Lookup table for device blacklisting */ static GHashTable *udevdevice_blacklist_lut = 0; +/** Lookup table for determining charger types */ +static GHashTable *udevdevice_chargertype_lut = 0; + /** How to treat unknown properties; default to ignoring them */ static property_type_t udevproperty_type_def = PROPERTY_TYPE_IGNORE; @@ -1101,10 +1107,44 @@ udevdevice_lookup_battery_state(const char *status) */ static charger_type_t udevdevice_lookup_charger_type(const char *name) +{ + charger_type_t type = CHARGER_TYPE_INVALID; + gchar *key = 0; + gpointer val; + + if( !name || !udevdevice_chargertype_lut ) + goto EXIT; + + key = g_ascii_strdown(name, -1); + + /* Try exact match 1st, then relaxed one which equates + * "chipname-ac" with plain "ac". + */ + val = g_hash_table_lookup(udevdevice_chargertype_lut, key); + if( !val ) { + const char *end = strrchr(key, '-'); + if( end ) + val = g_hash_table_lookup(udevdevice_chargertype_lut, end + 1); + } + type = GPOINTER_TO_INT(val); + +EXIT: + if( type == CHARGER_TYPE_INVALID ) { + mce_log(LL_WARN, "unknown charger type: %s", name ?: "null"); + type = CHARGER_TYPE_OTHER; + } + g_free(key); + + mce_log(LL_DEBUG, "charger type: %s -> %s", name ?: "null", charger_type_repr(type)); + return type; +} + +static void +udevdevice_init_chargertype(void) { static const struct { - const char *name; - int value; + const char *name; + charger_type_t type; } lut[] = { /* Type map - adapted from statefs sources */ @@ -1131,20 +1171,68 @@ udevdevice_lookup_charger_type(const char *name) { 0, 0 } }; - charger_type_t value = CHARGER_TYPE_OTHER; - if( name ) { - for( size_t i = 0; ; ++i ) { - if( lut[i].name == 0 ) { - mce_log(LL_WARN, "unknown charger type: %s", name); - break; - } - if( !g_ascii_strcasecmp(lut[i].name, name) ) { - value = lut[i].value; - break; + static const char grp[] = MCE_CONF_BATTERY_UDEV_DEVICE_CHARGERTYPE_GROUP; + + if( udevdevice_chargertype_lut ) + goto EXIT; + + udevdevice_chargertype_lut = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, 0); + + /* Seed with built-in values */ + for( size_t i = 0; lut[i].name; ++i ) { + g_hash_table_insert(udevdevice_chargertype_lut, + g_ascii_strdown(lut[i].name, -1), + GINT_TO_POINTER(lut[i].type)); + } + +#if 0 + /* Dump as ini-file for use as an example */ + { + GHashTableIter iter; + gpointer key, val; + g_hash_table_iter_init(&iter, udevdevice_chargertype_lut); + printf("[%s]\n", grp); + while ( g_hash_table_iter_next (&iter, &key, &val) ) + printf("#%s = %s\n", key, charger_type_repr(GPOINTER_TO_INT(val))); + } +#endif + + /* Override with configuration */ + if( mce_conf_has_group(grp) ) { + mce_log(LL_DEBUG, "using configured chargertypes"); + gsize count = 0; + gchar **keys = mce_conf_get_keys(grp, &count); + + for( gsize i = 0; i < count; ++i ) { + const gchar *name = keys[i]; + gchar *value = mce_conf_get_string(grp, name, 0); + charger_type_t type = charger_type_parse(value); + + if( type != CHARGER_TYPE_INVALID ) { + g_hash_table_insert(udevdevice_chargertype_lut, + g_ascii_strdown(name, -1), + GINT_TO_POINTER(type)); } + + g_free(value); } + g_strfreev(keys); + } + +EXIT: + return; +} + +/** Release device chargertype lookup table + */ +static void +udevdevice_quit_chargertype(void) +{ + if( udevdevice_chargertype_lut ) { + g_hash_table_unref(udevdevice_chargertype_lut), + udevdevice_chargertype_lut = 0; } - return value; } /** Initialize device blacklist lookup table @@ -1955,6 +2043,7 @@ G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module) (void)module; udevdevice_init_blacklist(); + udevdevice_init_chargertype(); udevproperty_init_types(); mcebat_dbus_init(); @@ -1985,6 +2074,7 @@ G_MODULE_EXPORT void g_module_unload(GModule *module) udevtracker_delete(udevtracker_object), udevtracker_object = 0; udevproperty_quit_types(); + udevdevice_quit_chargertype(); udevdevice_quit_blacklist(); mce_log(LL_DEBUG, "%s: unloaded", MODULE_NAME);