/**
* @file battery-upower.c
* Battery module -- this implements battery and charger logic for MCE
*
* Copyright (C) 2013 Jolla Ltd.
*
* @author Simo Piiroinen
*
* mce is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* mce 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mce. If not, see .
*/
#include "../mce.h"
#include "../mce-log.h"
#include "../mce-dbus.h"
#include
#include
#include
#if 0 // DEBUG: make all logging from this module "critical"
# undef mce_log
# define mce_log(LEV, FMT, ARGS...) \
mce_log_file(LL_CRIT, __FILE__, __FUNCTION__, FMT , ## ARGS)
#endif
/* ========================================================================= *
* CONFIGURATION
* ========================================================================= */
/** Delay from 1st property change to state machine update; [ms] */
#define UPDATE_DELAY 100
/** Whether to support legacy pattery low led pattern; nonzero for yes */
#define SUPPORT_BATTERY_LOW_LED_PATTERN 0
/** Well known D-Bus service name for upowerd */
#define UPOWER_SERVICE "org.freedesktop.UPower"
/** UPower D-Bus interface name */
#define UPOWER_INTERFACE "org.freedesktop.UPower"
/** UPower D-Bus object path */
#define UPOWER_PATH "/org/freedesktop/UPower"
/** Upower Device D-Bus interface name */
#define UPOWER_INTERFACE_DEVICE "org.freedesktop.UPower.Device"
/* ========================================================================= *
* union uval_t
* ========================================================================= */
/** Placeholder for any basic dbus data type */
typedef union
{
dbus_int16_t i16;
dbus_int32_t i32;
dbus_int64_t i64;
dbus_uint16_t u16;
dbus_uint32_t u32;
dbus_uint64_t u64;
dbus_bool_t b;
unsigned char o;
char *s;
double d;
} uval_t;
/* ========================================================================= *
* struct MceBattery holds battery data available via UPower
* ========================================================================= */
/** Values for upower device State property */
enum
{
UPOWER_STATE_UNKNOWN = 0,
UPOWER_STATE_CHARGING = 1,
UPOWER_STATE_DISCHARGING = 2,
UPOWER_STATE_EMPTY = 3,
UPOWER_STATE_FULLY_CHARGED = 4,
UPOWER_STATE_PENDING_CHARGE = 5,
UPOWER_STATE_PENDING_DISCHARGE = 6,
};
/** Values for upower device Type property */
enum
{
UPOWER_TYPE_UNKNOWN = 0,
UPOWER_TYPE_LINE_POWER = 1,
UPOWER_TYPE_BATTERY = 2,
UPOWER_TYPE_UPS = 3,
UPOWER_TYPE_MONITOR = 4,
UPOWER_TYPE_MOUSE = 5,
UPOWER_TYPE_KEYBOARD = 6,
UPOWER_TYPE_PDA = 7,
UPOWER_TYPE_PHONE = 8,
};
/** Values for upower device Technology property */
enum
{
UPOWER_TECHNOLOGY_UNKNOWN = 0,
UPOWER_TECHNOLOGY_LITHIUM_ION = 1,
UPOWER_TECHNOLOGY_LITHIUM_POLYMER = 2,
UPOWER_TECHNOLOGY_LITHIUM_IRON_PHOSPHATE = 3,
UPOWER_TECHNOLOGY_LEAD_ACID = 4,
UPOWER_TECHNOLOGY_NICKEL_CADMIUM = 5,
UPOWER_TECHNOLOGY_NICKEL_METAL_HYDRIDE = 6,
};
/** Battery properties available via upower */
typedef struct
{
int Percentage;
int State;
} UpowerBattery;
static void upowbat_init(void);
/* ========================================================================= *
* struct MceBattery holds mce legacy compatible battery data
* ========================================================================= */
/** Battery properties in mce statemachine compatible form */
typedef struct
{
/** Battery charge percentage; for use with battery_level_pipe */
int level;
/** Battery FULL/OK/LOW/EMPTY; for use with battery_status_pipe */
int status;
/** Charger connected; for use with charger_state_pipe */
charger_state_t charger;
} MceBattery;
static void mcebat_init(void);
static void mcebat_update_from_upowbat(void);
static gboolean mcebat_update_cb(gpointer user_data);
static void mcebat_update_cancel(void);
static void mcebat_update_schedule(void);
/* ========================================================================= *
* UPOWER PROPERTY
* ========================================================================= */
/** UPower property object */
typedef struct
{
char *p_key;
int p_type;
uval_t p_val;
} uprop_t;
/** Invalidate property
*
* @param self property
*/
static void uprop_set_invalid(uprop_t *self)
{
switch( self->p_type ) {
case DBUS_TYPE_INVALID:
break;
case DBUS_TYPE_BYTE:
case DBUS_TYPE_BOOLEAN:
case DBUS_TYPE_INT16:
case DBUS_TYPE_UINT16:
case DBUS_TYPE_INT32:
case DBUS_TYPE_UINT32:
case DBUS_TYPE_INT64:
case DBUS_TYPE_UINT64:
case DBUS_TYPE_DOUBLE:
break;
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE:
free(self->p_val.s), self->p_val.s = 0;
break;
default:
case DBUS_TYPE_UNIX_FD:
case DBUS_TYPE_ARRAY:
case DBUS_TYPE_VARIANT:
case DBUS_TYPE_STRUCT:
case DBUS_TYPE_DICT_ENTRY:
break;
}
self->p_type = DBUS_TYPE_INVALID;
}
/** Get property value from dbus message iterator
*
* @param self property
* @param iter dbus message parse position
*/
static bool uprop_set_from_iter(uprop_t *self, DBusMessageIter *iter)
{
bool res = false;
int type = dbus_message_iter_get_arg_type(iter);
uprop_set_invalid(self);
if( dbus_type_is_basic(type) ) {
dbus_message_iter_get_basic(iter, &self->p_val);
switch( dbus_message_iter_get_arg_type(iter) ) {
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE:
self->p_val.s = strdup(self->p_val.s);
break;
default:
break;
}
self->p_type = type, res = true;
}
return res;
}
/** Get property value as integer number
*
* @param self property
* @param val where to store the number
*
* @return true on success, false on failure
*/
static bool uprop_get_int(const uprop_t *self, int *val)
{
bool res = true;
switch( self->p_type ) {
case DBUS_TYPE_BYTE: *val = (int)self->p_val.o; break;
case DBUS_TYPE_BOOLEAN: *val = (int)self->p_val.b; break;
case DBUS_TYPE_INT16: *val = (int)self->p_val.i16; break;
case DBUS_TYPE_UINT16: *val = (int)self->p_val.u16; break;
case DBUS_TYPE_INT32: *val = (int)self->p_val.i32; break;
case DBUS_TYPE_UINT32: *val = (int)self->p_val.u32; break;
case DBUS_TYPE_INT64: *val = (int)self->p_val.i64; break;
case DBUS_TYPE_UINT64: *val = (int)self->p_val.u64; break;
case DBUS_TYPE_DOUBLE: *val = (int)(self->p_val.d + 0.5); break;
default:
res = false;
break;
}
return res;
}
/** Get property value as string
*
* @param self property
* @param val where to store the string
*
* @return true on success, false on failure
*/
static bool uprop_get_string(const uprop_t *self, const char **val)
{
bool res = true;
switch( self->p_type ) {
case DBUS_TYPE_STRING:
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE:
*val = self->p_val.s;
break;
default:
res = false;
break;
}
return res;
}
/** Create propety object
*
* @param key property name
*
* @return property object
*/
static uprop_t * uprop_create(const char *key)
{
uprop_t *self = calloc(1, sizeof *self);
self->p_key = strdup(key);;
self->p_type = DBUS_TYPE_INVALID;
return self;
}
/** Delete propety object
*
* @param self property object
*/
static void uprop_delete(uprop_t *self)
{
if( self != 0 ) {
uprop_set_invalid(self);
free(self->p_key);
free(self);
}
}
/** Type agnostic delete propety object callback function
*
* @param self property object as void pointer
*/
static void uprop_delete_cb(void *self)
{
uprop_delete(self);
}
/* ========================================================================= *
* SET OF UPOWER PROPERTIES
* ========================================================================= */
/** UPower device object */
typedef struct updev_t
{
char *d_path;
GList *d_prop;
} updev_t;
/** Create UPower device object
*
* @param path dbus object path
*
* @return device object
*/
static updev_t *updev_create(const char *path)
{
updev_t *self = calloc(1, sizeof *self);
self->d_path = strdup(path);
self->d_prop = 0;
return self;
}
/** Delete UPower device object
*
* @param self device object
*/
static void updev_delete(updev_t *self)
{
if( self != 0 ) {
g_list_free_full(self->d_prop, uprop_delete_cb);
free(self->d_path);
free(self);
}
}
/** Type agnostic delete UPower device object callback
*
* @param self device object as void pointer
*/
static void updev_delete_cb(void *self)
{
updev_delete(self);
}
/** Mark all device object properties as invalid
*
* @param self device object
*/
static void updev_set_invalid_all(updev_t *self)
{
for( GList *now = self->d_prop; now; now = g_list_next(now) )
uprop_set_invalid(now->data);
}
/** Find device object property
*
* @param self device object
* @param key property name
*
* @return property object, or NULL
*/
static uprop_t *updev_get_prop(const updev_t *self, const char *key)
{
uprop_t *res = 0;
for( GList *now = self->d_prop; now; now = g_list_next(now) ) {
uprop_t *prop = now->data;
if( strcmp(prop->p_key, key) )
continue;
res = prop;
break;
}
return res;
}
/** Find or create device object property
*
* @param self device object
* @param key property name
*
* @return property object
*/
static uprop_t *updev_add_prop(updev_t *self, const char *key)
{
uprop_t *res = updev_get_prop(self, key);
if( !res ) {
res = uprop_create(key);
self->d_prop = g_list_append(self->d_prop, res);
}
return res;
}
/** Get device object property value as integer number
*
* @param self device object
* @param key property name
* @param val where to store the integer number
*
* @return true on success, otherwise false
*/
static bool updev_get_int(const updev_t *self, const char *key, int *val)
{
bool res = false;
uprop_t *prop = updev_get_prop(self, key);
if( prop )
res = uprop_get_int(prop, val);
return res;
}
/** Get device object property value as string
*
* @param self device object
* @param key property name
* @param val where to store the string
*
* @return true on success, otherwise false
*/
static bool updev_get_string(const updev_t *self, const char *key,
const char **val)
{
bool res = false;
uprop_t *prop = updev_get_prop(self, key);
if( prop )
res = uprop_get_string(prop, val);
return res;
}
/** Device object is battery predicate
*
* @param self device object
*
* @return true if device object is battery, false otherwise
*/
static bool updev_is_battery(const updev_t *self)
{
bool is_battery = false;
if( !self )
goto EXIT;
const char *native_path = 0;
if( !updev_get_string(self, "NativePath", &native_path) )
goto EXIT;
if( !native_path || strcmp(native_path, "battery") )
goto EXIT;
is_battery = true;
EXIT:
return is_battery;
}
/* ========================================================================= *
* LIST OF UPOWER DEVICES
* ========================================================================= */
/** List of UPower device objects */
static GList *devlist = 0;
/** Find device object
*
* @param path dbus object path
*
* @return device object, or NULL if not found
*/
static updev_t *devlist_get_dev(const char *path)
{
updev_t *res = 0;
for( GList *now = devlist; now; now = g_list_next(now) ) {
updev_t *dev = now->data;
if( strcmp(dev->d_path, path) )
continue;
res = dev;
break;
}
return res;
}
/** Find 1st battery device object
*
* @return device object, or NULL if not found
*/
static updev_t *devlist_get_dev_battery(void)
{
updev_t *res = 0;
for( GList *now = devlist; now; now = g_list_next(now) ) {
updev_t *dev = now->data;
if( !updev_is_battery(dev) )
continue;
res = dev;
break;
}
return res;
}
/** Find or create device object
*
* @param path dbus object path
*
* @return device object
*/
static updev_t *devlist_add_dev(const char *path)
{
updev_t *res = devlist_get_dev(path);
if( !res ) {
res = updev_create(path);
devlist = g_list_append(devlist, res);
}
return res;
}
/** Forget device object
*
* @param path dbus object path
*/
static void devlist_rem_dev(const char *path)
{
for( GList *now = devlist; now; now = g_list_next(now) ) {
updev_t *dev = now->data;
if( strcmp(dev->d_path, path) )
continue;
if( updev_is_battery(dev) )
mcebat_update_schedule();
devlist = g_list_remove_link(devlist, now);
updev_delete(dev);
break;
}
}
/** Forget all device objects
*/
static void devlist_rem_dev_all(void)
{
g_list_free_full(devlist, updev_delete_cb);
devlist = 0;
}
/* ========================================================================= *
* struct UpowerBattery
* ========================================================================= */
/** UPower battery state data */
static UpowerBattery upowbat;
/** Initialize UPower battery state data
*/
static void
upowbat_init(void)
{
memset(&upowbat, 0, sizeof upowbat);
upowbat.Percentage = 50;
upowbat.State = UPOWER_STATE_UNKNOWN;
}
/** Update UPower battery state data
*/
static void
upowbat_update(void)
{
updev_t *dev = devlist_get_dev_battery();
if( dev ) {
int val = 0;
if( updev_get_int(dev, "Percentage", &val) && upowbat.Percentage != val ) {
mce_log(LL_DEBUG, "Percentage: %d -> %d", upowbat.Percentage, val);
upowbat.Percentage = val;
}
if( updev_get_int(dev, "State", &val) && upowbat.State != val ) {
mce_log(LL_DEBUG, "State: %d -> %d", upowbat.State, val);
upowbat.State = val;
}
}
}
/* ========================================================================= *
* struct MceBattery
* ========================================================================= */
/** Timer for processing battery status changes */
static guint mcebat_update_id = 0;
/** Current battery status in mce legacy compatible form */
static MceBattery mcebat;
/** Provide intial guess of mce battery status
*/
static void
mcebat_init(void)
{
memset(&mcebat, 0, sizeof mcebat);
mcebat.level = 50;
mcebat.status = BATTERY_STATUS_UNDEF;
mcebat.charger = CHARGER_STATE_UNDEF;
}
/** Update mce battery status from UPower battery data
*/
static void
mcebat_update_from_upowbat(void)
{
mcebat.level = upowbat.Percentage;
mcebat.status = BATTERY_STATUS_OK;
mcebat.charger = CHARGER_STATE_OFF;
// FIXME: hardcoded 5% as low battery limit
if( mcebat.level < 5 )
mcebat.status = BATTERY_STATUS_LOW;
switch( upowbat.State ) {
case UPOWER_STATE_UNKNOWN:
mcebat.charger = CHARGER_STATE_UNDEF;
break;
case UPOWER_STATE_CHARGING:
mcebat.charger = CHARGER_STATE_ON;
break;
case UPOWER_STATE_DISCHARGING:
break;
case UPOWER_STATE_EMPTY:
mcebat.status = BATTERY_STATUS_EMPTY;
break;
case UPOWER_STATE_FULLY_CHARGED:
mcebat.status = BATTERY_STATUS_FULL;
mcebat.charger = CHARGER_STATE_ON;
break;
case UPOWER_STATE_PENDING_CHARGE:
mcebat.charger = CHARGER_STATE_ON;
break;
case UPOWER_STATE_PENDING_DISCHARGE:
break;
default:
break;
}
}
/** Process accumulated upower battery status changes
*
* @param user_data (not used)
*
* @return FALSE (to stop timer from repeating)
*/
static gboolean
mcebat_update_cb(gpointer user_data)
{
(void)user_data;
if( !mcebat_update_id )
return FALSE;
mce_log(LL_INFO, "----( state machine )----");
/* Get a copy of current status */
MceBattery prev = mcebat;
/* Update from UPower based information */
upowbat_update();
mcebat_update_from_upowbat();
/* Process changes */
if( mcebat.charger != prev.charger ) {
mce_log(LL_INFO, "charger: %s -> %s",
charger_state_repr(prev.charger),
charger_state_repr(mcebat.charger));
/* Charger connected state */
datapipe_exec_full(&charger_state_pipe, GINT_TO_POINTER(mcebat.charger));
/* Charging led pattern */
if( mcebat.charger == CHARGER_STATE_ON ) {
datapipe_exec_full(&led_pattern_activate_pipe,
MCE_LED_PATTERN_BATTERY_CHARGING);
}
else {
datapipe_exec_full(&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_BATTERY_CHARGING);
}
/* Generate activity */
mce_datapipe_generate_activity();
}
if( mcebat.status != prev.status ) {
mce_log(LL_INFO, "status: %d -> %d", prev.status, mcebat.status);
/* Battery full led pattern */
if( mcebat.status == BATTERY_STATUS_FULL ) {
datapipe_exec_full(&led_pattern_activate_pipe,
MCE_LED_PATTERN_BATTERY_FULL);
}
else {
datapipe_exec_full(&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_BATTERY_FULL);
}
#if SUPPORT_BATTERY_LOW_LED_PATTERN
/* Battery low led pattern */
if( mcebat.status == BATTERY_STATUS_LOW ||
mcebat.status == BATTERY_STATUS_EMPTY ) {
datapipe_exec_full(&led_pattern_activate_pipe,
MCE_LED_PATTERN_BATTERY_LOW);
}
else {
datapipe_exec_full(&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_BATTERY_LOW);
}
#endif /* SUPPORT_BATTERY_LOW_LED_PATTERN */
/* Battery charge state */
datapipe_exec_full(&battery_status_pipe,
GINT_TO_POINTER(mcebat.status));
}
if( mcebat.level != prev.level ) {
mce_log(LL_INFO, "level: %d -> %d", prev.level, mcebat.level);
/* Battery charge percentage */
datapipe_exec_full(&battery_level_pipe,
GINT_TO_POINTER(mcebat.level));
}
/* Clear the timer id and do not repeat */
return mcebat_update_id = 0, FALSE;
}
/** Cancel processing of upower battery status changes
*/
static void
mcebat_update_cancel(void)
{
if( mcebat_update_id )
g_source_remove(mcebat_update_id), mcebat_update_id = 0;
}
/** Initiate delayed processing of upower battery status changes
*/
static void
mcebat_update_schedule(void)
{
if( !mcebat_update_id )
mcebat_update_id = g_timeout_add(UPDATE_DELAY, mcebat_update_cb, 0);
}
/* ========================================================================= *
* UPOWER IPC
* ========================================================================= */
/** Handle reply to async UPower device properties query
*/
static void xup_properties_get_all_cb(DBusPendingCall *pc, void *aptr)
{
(void)aptr;
bool res = false;
DBusError err = DBUS_ERROR_INIT;
DBusMessage *rsp = 0;
const char *path = aptr;
updev_t *dev = devlist_add_dev(path);
mce_log(LL_INFO, "path = %s", path);
DBusMessageIter body, arr, dic, var;
updev_set_invalid_all(dev);
if( !(rsp = dbus_pending_call_steal_reply(pc)) )
goto EXIT;
if( dbus_set_error_from_message(&err, rsp) ) {
mce_log(LL_ERR, "als lux error reply: %s: %s",
err.name, err.message);
goto EXIT;
}
if( !dbus_message_iter_init(rsp, &body) )
goto EXIT;
if( dbus_message_iter_get_arg_type(&body) != DBUS_TYPE_ARRAY )
goto EXIT;
dbus_message_iter_recurse(&body, &arr);
dbus_message_iter_next(&body);
while( dbus_message_iter_get_arg_type(&arr) == DBUS_TYPE_DICT_ENTRY ) {
dbus_message_iter_recurse(&arr, &dic);
dbus_message_iter_next(&arr);
const char *key = 0;
if( dbus_message_iter_get_arg_type(&dic) != DBUS_TYPE_STRING )
goto EXIT;
dbus_message_iter_get_basic(&dic, &key);
dbus_message_iter_next(&dic);
if( !key )
goto EXIT;
if( dbus_message_iter_get_arg_type(&dic) != DBUS_TYPE_VARIANT )
goto EXIT;
dbus_message_iter_recurse(&dic, &var);
dbus_message_iter_next(&dic);
uprop_t *prop = updev_add_prop(dev, key);
uprop_set_from_iter(prop, &var);
}
mce_log(LL_DEBUG, "%s is %sBATTERY", path,
updev_is_battery(dev) ? "" : "NOT ");
if( updev_is_battery(dev) )
mcebat_update_schedule();
res = true;
EXIT:
if( !res ) mce_log(LL_WARN, "failed to parse reply");
if( rsp ) dbus_message_unref(rsp);
dbus_error_free(&err);
}
/** Start async UPower device properties query
*/
static void xup_properties_get_all(const char *path)
{
DBusConnection *bus = 0;
DBusMessage *req = 0;
DBusPendingCall *pc = 0;
const char *arg = UPOWER_INTERFACE_DEVICE;
if( !(bus = dbus_connection_get()) )
goto EXIT;
req = dbus_message_new_method_call(UPOWER_SERVICE,
path,
DBUS_INTERFACE_PROPERTIES,
"GetAll");
if( !req )
goto EXIT;
if( !dbus_message_append_args(req,
DBUS_TYPE_STRING, &arg,
DBUS_TYPE_INVALID) )
goto EXIT;
if( !dbus_connection_send_with_reply(bus, req, &pc, -1) )
goto EXIT;
if( !pc )
goto EXIT;
mce_dbus_pending_call_blocks_suspend(pc);
if( !dbus_pending_call_set_notify(pc, xup_properties_get_all_cb,
strdup(path), free) )
goto EXIT;
EXIT:
if( pc ) dbus_pending_call_unref(pc);
if( req ) dbus_message_unref(req);
if( bus ) dbus_connection_unref(bus);
}
/** Handle reply to async UPower device enumeration query
*/
static void xup_enumerate_devices_cb(DBusPendingCall *pc, void *aptr)
{
(void)aptr;
bool res = false;
DBusError err = DBUS_ERROR_INIT;
DBusMessage *rsp = 0;
char **vec = 0;
int cnt = 0;
if( !(rsp = dbus_pending_call_steal_reply(pc)) )
goto EXIT;
if( dbus_set_error_from_message(&err, rsp) ) {
mce_log(LL_ERR, "%s: %s", err.name, err.message);
goto EXIT;
}
if( !dbus_message_get_args(rsp, &err,
DBUS_TYPE_ARRAY,
DBUS_TYPE_OBJECT_PATH, &vec, &cnt,
DBUS_TYPE_INVALID) ) {
mce_log(LL_ERR, "%s: %s", err.name, err.message);
goto EXIT;
}
for( int i = 0; i < cnt; ++i ) {
mce_log(LL_DEBUG, "[%d] '%s'", i, vec[i]);
xup_properties_get_all(vec[i]);
}
res = true;
EXIT:
if( !res ) mce_log(LL_WARN, "failed to parse reply");
dbus_free_string_array(vec);
if( rsp ) dbus_message_unref(rsp);
dbus_error_free(&err);
}
/** Start async UPower device enumeration query
*/
static void xup_enumerate_devices(void)
{
dbus_send(UPOWER_SERVICE, UPOWER_PATH, UPOWER_INTERFACE,
"EnumerateDevices", xup_enumerate_devices_cb,
DBUS_TYPE_INVALID);
}
/** Handle addition of UPowerd device object
*/
static gboolean xup_device_added_cb(DBusMessage *const msg)
{
DBusError err = DBUS_ERROR_INIT;
const char *path = 0;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID) ) {
mce_log(LL_ERR, "%s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "dev = %s", path);
xup_properties_get_all(path);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle UPowerd device object property changes
*/
static gboolean xup_device_changed_cb(DBusMessage *const msg)
{
DBusError err = DBUS_ERROR_INIT;
const char *path = 0;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID) ) {
mce_log(LL_ERR, "%s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "dev = %s", path);
updev_t *dev = devlist_get_dev(path);
/* Get properties if we know that it is battery, or
* if we do not know what it is yet */
if( !dev || updev_is_battery(dev) )
xup_properties_get_all(path);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle removal of UPowerd device object
*/
static gboolean xup_device_removed_cb(DBusMessage *const msg)
{
DBusError err = DBUS_ERROR_INIT;
const char *path = 0;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &path,
DBUS_TYPE_INVALID) ) {
mce_log(LL_ERR, "%s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "dev = %s", path);
devlist_rem_dev(path);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle UPowerd dbus name ownership change signal
*/
static gboolean xup_name_owner_cb(DBusMessage *const msg)
{
DBusError err = DBUS_ERROR_INIT;
const char *service = 0;
const char *old_owner = 0;
const char *new_owner = 0;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &service,
DBUS_TYPE_STRING, &old_owner,
DBUS_TYPE_STRING, &new_owner,
DBUS_TYPE_INVALID) ) {
mce_log(LL_ERR, "%s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "upowerd %s", *new_owner ? "running" : "stopped");
/* Flush cached device object properties when upowerd
* stops or starts */
devlist_rem_dev_all();
/* If upowerd started up, get fresh list of device paths */
if( *new_owner )
xup_enumerate_devices();
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Module name */
#define MODULE_NAME "battery_upower"
/** Functionality provided by this module */
static const gchar *const provides[] = { MODULE_NAME, NULL };
/** Module information */
G_MODULE_EXPORT module_info_struct module_info =
{
/** Name of the module */
.name = MODULE_NAME,
/** Module provides */
.provides = provides,
/** Module priority */
.priority = 100
};
/** Array of dbus message handlers */
static mce_dbus_handler_t battery_upower_dbus_handlers[] =
{
/* signals */
{
.interface = UPOWER_INTERFACE,
.name = "DeviceAdded",
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = xup_device_added_cb,
},
{
.interface = UPOWER_INTERFACE,
.name = "DeviceChanged",
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = xup_device_changed_cb,
},
{
.interface = UPOWER_INTERFACE,
.name = "DeviceRemoved",
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = xup_device_removed_cb,
},
{
.interface = DBUS_INTERFACE_DBUS,
.name = "NameOwnerChanged",
.rules = "arg0='"UPOWER_SERVICE"'",
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = xup_name_owner_cb,
},
/* sentinel */
{
.interface = 0
}
};
/** Add dbus handlers
*/
static void mce_battery_init_dbus(void)
{
mce_dbus_handler_register_array(battery_upower_dbus_handlers);
}
/** Remove dbus handlers
*/
static void mce_battery_quit_dbus(void)
{
mce_dbus_handler_unregister_array(battery_upower_dbus_handlers);
}
/** Init function for the battery and charger module
*
* @todo XXX status needs to be set on error!
*
* @param module Unused
*
* @return NULL on success, a string with an error message on failure
*/
G_MODULE_EXPORT const gchar *g_module_check_init(GModule *module);
const gchar *g_module_check_init(GModule *module)
{
(void)module;
/* reset data used by the state machine */
mcebat_init();
upowbat_init();
/* Add dbus handlers */
mce_battery_init_dbus();
/* Initiate available device objects query.
* Properties will be probed when reply arrives.
* This will start upowerd if not already running.
*/
xup_enumerate_devices();
return NULL;
}
/** Exit function for the battery and charger module
*
* @param module Unused
*/
G_MODULE_EXPORT void g_module_unload(GModule *module);
void g_module_unload(GModule *module)
{
(void)module;
/* Remove dbus handlers */
mce_battery_quit_dbus();
devlist_rem_dev_all();
mcebat_update_cancel();
}