From 513dbb5409fd0605da0e941d1a0c79f5630b2e08 Mon Sep 17 00:00:00 2001 From: Simo Piiroinen Date: Tue, 1 Oct 2019 13:00:53 +0300 Subject: [PATCH] [battery-udev] Expose battery charging state. JB#44852 For example CSD tests need more detailed information about battery charging state. Track udev battery status, expose it as mce battery state on D-Bus. Required D-Bus constants are included in mce-dev >= 1.28.0. Signed-off-by: Simo Piiroinen --- datapipe.c | 74 ++++++++++++++++++++++++++ datapipe.h | 1 + mce-common.c | 115 +++++++++++++++++++++++++++++++++++++++++ mce.conf | 3 ++ mce.h | 13 +++++ modules/battery-udev.c | 104 ++++++++++++++++++++++++++++--------- rpm/mce.spec | 2 +- tools/mcetool.c | 10 ++++ 8 files changed, 296 insertions(+), 26 deletions(-) diff --git a/datapipe.c b/datapipe.c index 97f59695..bf23df4c 100644 --- a/datapipe.c +++ b/datapipe.c @@ -116,6 +116,7 @@ static const char *datapipe_hook_tklock_request_value (gconstpointer data); static const char *datapipe_hook_charger_state_value (gconstpointer data); static const char *datapipe_hook_charger_type_value (gconstpointer data); static const char *datapipe_hook_battery_status_value (gconstpointer data); +static const char *datapipe_hook_battery_state_value (gconstpointer data); static const char *datapipe_hook_camera_button_state_value(gconstpointer data); static const char *datapipe_hook_audio_route_value (gconstpointer data); static const char *datapipe_hook_usb_cable_state_value (gconstpointer data); @@ -249,6 +250,13 @@ const char *tklock_status_repr(int status); const char *battery_status_repr (battery_status_t state); const char *battery_status_to_dbus(battery_status_t state); +/* ------------------------------------------------------------------------- * + * BATTERY_STATE + * ------------------------------------------------------------------------- */ + +const char *battery_state_repr (battery_state_t state); +const char *battery_state_to_dbus(battery_state_t state); + /* ------------------------------------------------------------------------- * * ALARM_STATE * ------------------------------------------------------------------------- */ @@ -506,6 +514,14 @@ datapipe_hook_battery_status_value(gconstpointer data) } #define datapipe_hook_battery_status_change 0 +static const char * +datapipe_hook_battery_state_value(gconstpointer data) +{ + battery_state_t value = GPOINTER_TO_INT(data); + return battery_state_repr(value); +} +#define datapipe_hook_battery_state_change 0 + static const char * datapipe_hook_camera_button_state_value(gconstpointer data) { @@ -724,6 +740,9 @@ datapipe_t charger_state_pipe = DATAPIPE_INIT(charger_state, c /** Battery status; read only */ datapipe_t battery_status_pipe = DATAPIPE_INIT(battery_status, battery_status, BATTERY_STATUS_UNDEF, 0, DATAPIPE_FILTERING_DENIED, DATAPIPE_CACHE_DEFAULT); +/** Battery state; read only */ +datapipe_t battery_state_pipe = DATAPIPE_INIT(battery_state, battery_state, BATTERY_STATE_UNKNOWN, 0, DATAPIPE_FILTERING_DENIED, DATAPIPE_CACHE_DEFAULT); + /** Battery charge level; read only */ datapipe_t battery_level_pipe = DATAPIPE_INIT(battery_level, int, BATTERY_LEVEL_INITIAL, 0, DATAPIPE_FILTERING_DENIED, DATAPIPE_CACHE_DEFAULT); @@ -1303,6 +1322,7 @@ void mce_datapipe_quit(void) datapipe_free(&topmost_window_pid_pipe); datapipe_free(&camera_button_state_pipe); datapipe_free(&battery_status_pipe); + datapipe_free(&battery_state_pipe); datapipe_free(&charger_type_pipe); datapipe_free(&charger_state_pipe); datapipe_free(&interaction_expected_pipe); @@ -1810,6 +1830,60 @@ const char *tklock_status_repr(int status) return repr; } +/** Convert battery_state_t enum to human readable string + * + * @param state battery_state_t enumeration value + * + * @return human readable representation of state + */ +const char * +battery_state_repr(battery_state_t state) +{ + const char *repr = "invalid"; + + switch( state ) { + case BATTERY_STATE_UNKNOWN: repr = "unknown"; break; + case BATTERY_STATE_CHARGING: repr = "charging"; break; + case BATTERY_STATE_DISCHARGING: repr = "discharging"; break; + case BATTERY_STATE_NOT_CHARGING: repr = "not_charging"; break; + case BATTERY_STATE_FULL: repr = "full"; break; + default: break; + } + + return repr; +} + +/** Convert battery_state_t enum to dbus argument string + * + * @param state battery_state_t enumeration value + * + * @return representation of state for use over dbus + */ +const char * +battery_state_to_dbus(battery_state_t state) +{ + const char *res = MCE_BATTERY_STATE_UNKNOWN; + + switch( state ) { + case BATTERY_STATE_CHARGING: + res = MCE_BATTERY_STATE_CHARGING; + break; + case BATTERY_STATE_DISCHARGING: + res = MCE_BATTERY_STATE_DISCHARGING; + break; + case BATTERY_STATE_NOT_CHARGING: + res = MCE_BATTERY_STATE_NOT_CHARGING; + break; + case BATTERY_STATE_FULL: + res = MCE_BATTERY_STATE_FULL; + break; + default: + break; + } + + return res; +} + /** Convert battery_status_t enum to human readable string * * @param state battery_status_t enumeration value diff --git a/datapipe.h b/datapipe.h index a06b2581..54e5ffa9 100644 --- a/datapipe.h +++ b/datapipe.h @@ -257,6 +257,7 @@ extern datapipe_t interaction_expected_pipe; extern datapipe_t charger_type_pipe; extern datapipe_t charger_state_pipe; extern datapipe_t battery_status_pipe; +extern datapipe_t battery_state_pipe; extern datapipe_t battery_level_pipe; extern datapipe_t topmost_window_pid_pipe; extern datapipe_t camera_button_state_pipe; diff --git a/mce-common.c b/mce-common.c index e65f60f0..1a3c2db8 100644 --- a/mce-common.c +++ b/mce-common.c @@ -85,6 +85,8 @@ static void common_dbus_send_charger_state (DBusMessage *const req); static gboolean common_dbus_get_charger_state_cb (DBusMessage *const req); static void common_dbus_send_battery_status (DBusMessage *const req); static gboolean common_dbus_get_battery_status_cb (DBusMessage *const req); +static void common_dbus_send_battery_state (DBusMessage *const req); +static gboolean common_dbus_get_battery_state_cb (DBusMessage *const req); static void common_dbus_send_battery_level (DBusMessage *const req); static gboolean common_dbus_get_battery_level_cb (DBusMessage *const req); static gboolean common_dbus_initial_cb (gpointer aptr); @@ -99,6 +101,7 @@ static void common_datapipe_usb_cable_state_cb (gconstpointer data); static void common_datapipe_charger_type_cb (gconstpointer data); static void common_datapipe_charger_state_cb (gconstpointer data); static void common_datapipe_battery_status_cb (gconstpointer data); +static void common_datapipe_battery_state_cb (gconstpointer data); static void common_datapipe_battery_level_cb (gconstpointer data); static void common_datapipe_proximity_sensor_actual_cb(gconstpointer data); static void common_datapipe_init (void); @@ -127,6 +130,9 @@ static charger_state_t charger_state = CHARGER_STATE_UNDEF; /** Battery status; assume undefined */ static battery_status_t battery_status = BATTERY_STATUS_UNDEF; +/** Battery state; assume unknown */ +static battery_state_t battery_state = BATTERY_STATE_UNKNOWN; + /** Battery charge level: assume 100% */ static gint battery_level = BATTERY_LEVEL_INITIAL; @@ -589,6 +595,69 @@ common_dbus_get_battery_status_cb(DBusMessage *const req) return TRUE; } +/* ------------------------------------------------------------------------- * + * battery_state + * ------------------------------------------------------------------------- */ + +/** Send battery_state D-Bus signal / method call reply + * + * @param req method call message to reply, or NULL to send signal + */ +static void +common_dbus_send_battery_state(DBusMessage *const req) +{ + static const char *last = 0; + + DBusMessage *msg = NULL; + + const char *value = battery_state_to_dbus(battery_state); + + if( req ) { + msg = dbus_new_method_reply(req); + } + else if( last == value ) { + goto EXIT; + } + else { + last = value; + msg = dbus_new_signal(MCE_SIGNAL_PATH, + MCE_SIGNAL_IF, + MCE_BATTERY_STATE_SIG); + } + + if( !dbus_message_append_args(msg, + DBUS_TYPE_STRING, &value, + DBUS_TYPE_INVALID) ) + goto EXIT; + + mce_log(LL_DEBUG, "%s: %s = %s", + req ? "reply" : "broadcast", + "battery_state", value); + + dbus_send_message(msg), msg = 0; + +EXIT: + + if( msg ) + dbus_message_unref(msg); +} + +/** Callback for handling battery_state D-Bus queries + * + * @param req method call message to reply + */ +static gboolean +common_dbus_get_battery_state_cb(DBusMessage *const req) +{ + mce_log(LL_DEBUG, "battery_state query from: %s", + mce_dbus_get_message_sender_ident(req)); + + if( !dbus_message_get_no_reply(req) ) + common_dbus_send_battery_state(req); + + return TRUE; +} + /* ------------------------------------------------------------------------- * * battery_level * ------------------------------------------------------------------------- */ @@ -694,6 +763,13 @@ static mce_dbus_handler_t common_dbus_handlers[] = .args = " \n" }, + { + .interface = MCE_SIGNAL_IF, + .name = MCE_BATTERY_STATE_SIG, + .type = DBUS_MESSAGE_TYPE_SIGNAL, + .args = + " \n" + }, { .interface = MCE_SIGNAL_IF, .name = MCE_BATTERY_LEVEL_SIG, @@ -734,6 +810,14 @@ static mce_dbus_handler_t common_dbus_handlers[] = .args = " \n" }, + { + .interface = MCE_REQUEST_IF, + .name = MCE_BATTERY_STATE_GET, + .type = DBUS_MESSAGE_TYPE_METHOD_CALL, + .callback = common_dbus_get_battery_state_cb, + .args = + " \n" + }, { .interface = MCE_REQUEST_IF, .name = MCE_BATTERY_LEVEL_GET, @@ -771,6 +855,7 @@ static gboolean common_dbus_initial_cb(gpointer aptr) common_dbus_send_charger_type(0); common_dbus_send_charger_state(0); common_dbus_send_battery_status(0); + common_dbus_send_battery_state(0); common_dbus_send_battery_level(0); common_dbus_initial_id = 0; @@ -918,6 +1003,32 @@ static void common_datapipe_battery_status_cb(gconstpointer data) return; } +/* ------------------------------------------------------------------------- * + * battery_state + * ------------------------------------------------------------------------- */ + +/** Callback for handling battery_state_pipe state changes + * + * @param data battery_state (as void pointer) + */ +static void common_datapipe_battery_state_cb(gconstpointer data) +{ + battery_state_t prev = battery_state; + battery_state = GPOINTER_TO_INT(data); + + if( battery_state == prev ) + goto EXIT; + + mce_log(LL_DEBUG, "battery_state = %s -> %s", + battery_state_repr(prev), + battery_state_repr(battery_state)); + + common_dbus_send_battery_state(0); + +EXIT: + return; +} + /* ------------------------------------------------------------------------- * * battery_level * ------------------------------------------------------------------------- */ @@ -992,6 +1103,10 @@ static datapipe_handler_t common_datapipe_handlers[] = .datapipe = &battery_status_pipe, .output_cb = common_datapipe_battery_status_cb, }, + { + .datapipe = &battery_state_pipe, + .output_cb = common_datapipe_battery_state_cb, + }, { .datapipe = &battery_level_pipe, .output_cb = common_datapipe_battery_level_cb, diff --git a/mce.conf b/mce.conf index f403ec59..8807dce4 100644 --- a/mce.conf +++ b/mce.conf @@ -219,6 +219,9 @@ + diff --git a/mce.h b/mce.h index 695a5e5f..2ae68b96 100644 --- a/mce.h +++ b/mce.h @@ -313,6 +313,19 @@ const char *tklock_status_repr(int status); /** Assumed initial battery level */ #define BATTERY_LEVEL_INITIAL 100 +/** Raw udev battery status */ +typedef enum +{ + BATTERY_STATE_UNKNOWN, + BATTERY_STATE_CHARGING, + BATTERY_STATE_DISCHARGING, + BATTERY_STATE_NOT_CHARGING, + BATTERY_STATE_FULL, +} battery_state_t; + +const char *battery_state_repr(battery_state_t state); +const char *battery_state_to_dbus(battery_state_t state); + /** Battery status */ typedef enum { BATTERY_STATUS_UNDEF = -1, /**< Battery status not known */ diff --git a/modules/battery-udev.c b/modules/battery-udev.c index e08f481e..ba2c5e98 100644 --- a/modules/battery-udev.c +++ b/modules/battery-udev.c @@ -116,6 +116,9 @@ typedef struct /** Battery FULL/OK/LOW/EMPTY; for use with battery_status_pipe */ battery_status_t battery_status; + /** Battery UNKNOWN|CHARGING|DISCHARGING|NOT_CHARGING|FULL"*/ + battery_state_t battery_state; + /** Charger connected; for use with charger_state_pipe */ charger_state_t charger_state; @@ -224,26 +227,27 @@ static bool udevproperty_set (udevproperty_t *self, const ch * UDEVDEVICE * ------------------------------------------------------------------------- */ -static charger_type_t udevdevice_lookup_charger_type(const char *name); -static void udevdevice_init_blacklist (void); -static void udevdevice_quit_blacklist (void); -static bool udevdevice_is_blacklisted (const char *name); -static udevdevice_t *udevdevice_create (const char *name); -static void udevdevice_delete (udevdevice_t *self); -static void udevdevice_delete_cb (void *self); -static const char *udevdevice_name (const udevdevice_t *self); -static udevproperty_t *udevdevice_get_prop (udevdevice_t *self, const char *key); -static udevproperty_t *udevdevice_add_prop (udevdevice_t *self, const char *key); -static bool udevdevice_set_prop (udevdevice_t *self, const char *key, const char *val); -static const char *udevdevice_get_str_prop (udevdevice_t *self, const char *key, const char *def); -static int udevdevice_get_int_prop (udevdevice_t *self, const char *key, int def); -static bool udevdevice_refresh (udevdevice_t *self, struct udev_device *dev); -static bool udevdevice_is_battery (udevdevice_t *self); -static bool udevdevice_is_charger (udevdevice_t *self); -static void udevdevice_evaluate_charger (udevdevice_t *self, mcebat_t *mcebat); -static void udevdevice_evaluate_charger_cb(gpointer key, gpointer value, gpointer aptr); -static void udevdevice_evaluate_battery (udevdevice_t *self, mcebat_t *mcebat); -static void udevdevice_evaluate_battery_cb(gpointer key, gpointer value, gpointer aptr); +static battery_state_t udevdevice_lookup_battery_state(const char *status); +static charger_type_t udevdevice_lookup_charger_type (const char *name); +static void udevdevice_init_blacklist (void); +static void udevdevice_quit_blacklist (void); +static bool udevdevice_is_blacklisted (const char *name); +static udevdevice_t *udevdevice_create (const char *name); +static void udevdevice_delete (udevdevice_t *self); +static void udevdevice_delete_cb (void *self); +static const char *udevdevice_name (const udevdevice_t *self); +static udevproperty_t *udevdevice_get_prop (udevdevice_t *self, const char *key); +static udevproperty_t *udevdevice_add_prop (udevdevice_t *self, const char *key); +static bool udevdevice_set_prop (udevdevice_t *self, const char *key, const char *val); +static const char *udevdevice_get_str_prop (udevdevice_t *self, const char *key, const char *def); +static int udevdevice_get_int_prop (udevdevice_t *self, const char *key, int def); +static bool udevdevice_refresh (udevdevice_t *self, struct udev_device *dev); +static bool udevdevice_is_battery (udevdevice_t *self); +static bool udevdevice_is_charger (udevdevice_t *self); +static void udevdevice_evaluate_charger (udevdevice_t *self, mcebat_t *mcebat); +static void udevdevice_evaluate_charger_cb (gpointer key, gpointer value, gpointer aptr); +static void udevdevice_evaluate_battery (udevdevice_t *self, mcebat_t *mcebat); +static void udevdevice_evaluate_battery_cb (gpointer key, gpointer value, gpointer aptr); /* ------------------------------------------------------------------------- * * UDEVTRACKER @@ -294,6 +298,7 @@ G_MODULE_EXPORT module_info_struct module_info = static mcebat_t mcebat_datapipe = { .battery_level = BATTERY_LEVEL_INITIAL, .battery_status = BATTERY_STATUS_UNDEF, + .battery_state = BATTERY_STATE_UNKNOWN, .charger_state = CHARGER_STATE_UNDEF, .charger_type = CHARGER_TYPE_NONE, }; @@ -303,6 +308,7 @@ static mcebat_t mcebat_datapipe = { static mcebat_t mcebat_actual = { .battery_level = BATTERY_LEVEL_INITIAL, .battery_status = BATTERY_STATUS_UNDEF, + .battery_state = BATTERY_STATE_UNKNOWN, .charger_state = CHARGER_STATE_UNDEF, .charger_type = CHARGER_TYPE_NONE, }; @@ -466,9 +472,12 @@ mcebat_dbus_evaluate_battery_status(void) { /* Handle charger-connected special cases */ if( mcebat_simulated.charger_state == CHARGER_STATE_ON ) { + mcebat_simulated.battery_state = BATTERY_STATE_CHARGING; + if( mcebat_simulated.battery_level >= 100 ) { /* Battery full reached */ mcebat_simulated.battery_status = BATTERY_STATUS_FULL; + mcebat_simulated.battery_state = BATTERY_STATE_FULL; goto EXIT; } if( mcebat_simulated.battery_status == BATTERY_STATUS_FULL && @@ -482,6 +491,9 @@ mcebat_dbus_evaluate_battery_status(void) goto EXIT; } } + else { + mcebat_simulated.battery_state = BATTERY_STATE_DISCHARGING; + } /* Evaluate based on battery level */ if( mcebat_simulated.battery_level <= BATTERY_CAPACITY_UNDEF ) @@ -804,6 +816,16 @@ mcebat_update(void) mce_datapipe_generate_activity(); } + if( prev.battery_state != curr->battery_state ) { + mce_log(LL_CRUCIAL, "battery_state: %s -> %s", + battery_state_repr(prev.battery_state), + battery_state_repr(curr->battery_state)); + + /* Battery charging state */ + datapipe_exec_full(&battery_state_pipe, + GINT_TO_POINTER(curr->battery_state)); + } + if( prev.battery_status != curr->battery_status ) { mce_log(LL_CRUCIAL, "battery_status: %s -> %s", battery_status_repr(prev.battery_status), @@ -1045,6 +1067,32 @@ udevproperty_set(udevproperty_t *self, const char *val) * UDEVDEVICE * ========================================================================= */ +/** Lookup mce battery state based on udev battery status property value + * + * @param status udev battery status + * + * @return battery_state_t enumeration value + */ + +static battery_state_t +udevdevice_lookup_battery_state(const char *status) +{ + battery_state_t state = BATTERY_STATE_UNKNOWN; + + if( !g_strcmp0(status, "Charging") ) + state = BATTERY_STATE_CHARGING; + else if( !g_strcmp0(status, "Discharging") ) + state = BATTERY_STATE_DISCHARGING; + else if( !g_strcmp0(status, "Not charging") ) + state = BATTERY_STATE_NOT_CHARGING; + else if( !g_strcmp0(status, "Full") ) + state = BATTERY_STATE_FULL; + else if( g_strcmp0(status, "Unknown") ) + mce_log(LL_WARN, "unrecognized power supply state '%s'", status); + + return state; +} + /** Lookup charger type based on device name / value of type property * * @param name string to match @@ -1496,9 +1544,11 @@ udevdevice_evaluate_battery(udevdevice_t *self, mcebat_t *mcebat) else mcebat->battery_status = BATTERY_STATUS_OK; - /* udev status is "Unknown|Charging|Discharging|Not charging|Full" - * - * "Charging" and "Full" override capacity based mce battery status + /* udev status is "Unknown|Charging|Discharging|Not charging|Full" */ + + mcebat->battery_state = udevdevice_lookup_battery_state(status); + + /* "Charging" and "Full" override capacity based mce battery status * evaluation above. * * How maintenance charging is reported after hitting battery @@ -1511,12 +1561,12 @@ udevdevice_evaluate_battery(udevdevice_t *self, mcebat_t *mcebat) * Also if battery device indicates that it is getting charged, * assume that a charger is connected. */ - if( !g_strcmp0(status, "Full") ) { + if( mcebat->battery_state == BATTERY_STATE_FULL ) { mcebat->charger_state = CHARGER_STATE_ON; mcebat->battery_status = BATTERY_STATUS_FULL; self->udd_full = true; } - else if( !g_strcmp0(status, "Charging") ) { + else if( mcebat->battery_state == BATTERY_STATE_CHARGING ) { mcebat->charger_state = CHARGER_STATE_ON; mcebat->battery_status = BATTERY_STATUS_OK; if( self->udd_full && capacity >= BATTERY_CAPACITY_FULL ) @@ -1546,6 +1596,10 @@ udevdevice_evaluate_battery(udevdevice_t *self, mcebat_t *mcebat) self->udd_full = false; } + /* Override udev status on heuristically determined battery full */ + if( mcebat->battery_status == BATTERY_STATUS_FULL ) + mcebat->battery_state = BATTERY_STATE_FULL; + mce_log(LL_DEBUG, "%s: battery @ cap=%d status=%s full=%d", udevdevice_name(self), capacity, status, self->udd_full); diff --git a/rpm/mce.spec b/rpm/mce.spec index cada2467..951a098e 100644 --- a/rpm/mce.spec +++ b/rpm/mce.spec @@ -20,7 +20,7 @@ BuildRequires: pkgconfig(dsme) >= 0.65.0 BuildRequires: pkgconfig(thermalmanager_dbus_if) BuildRequires: pkgconfig(libiphb) BuildRequires: pkgconfig(glib-2.0) >= 2.36.0 -BuildRequires: pkgconfig(mce) >= 1.27.0 +BuildRequires: pkgconfig(mce) >= 1.28.0 BuildRequires: pkgconfig(libngf0) >= 0.24 BuildRequires: pkgconfig(libsystemd-daemon) BuildRequires: kernel-headers >= 2.6.32 diff --git a/tools/mcetool.c b/tools/mcetool.c index 1e3941cc..ae0feb73 100644 --- a/tools/mcetool.c +++ b/tools/mcetool.c @@ -147,6 +147,7 @@ static bool xmce_set_battery_level (int leve static void xmce_get_cable_state (void); static void xmce_get_charger_state (void); static void xmce_get_battery_status (void); +static void xmce_get_battery_state (void); static void xmce_get_battery_level (void); static void xmce_get_battery_info (void); static void xmce_parse_notification_args (const char *args, char **title, dbus_int32_t *delay, dbus_int32_t *renew); @@ -2749,6 +2750,14 @@ static void xmce_get_battery_status(void) free(str); } +static void xmce_get_battery_state(void) +{ + char *str = 0; + xmce_ipc_string_reply(MCE_BATTERY_STATE_GET, &str, DBUS_TYPE_INVALID); + printf("%-"PAD1"s %s\n","Battery state:", str ?: "unknown"); + free(str); +} + static void xmce_get_battery_level(void) { gint num = -1; @@ -2762,6 +2771,7 @@ static void xmce_get_battery_info(void) xmce_get_charger_state(); xmce_get_battery_level(); xmce_get_battery_status(); + xmce_get_battery_state(); } /* ------------------------------------------------------------------------- *