diff --git a/battery/oneUpPower.c b/battery/oneUpPower.c index 923cd33..c0bf0b1 100644 --- a/battery/oneUpPower.c +++ b/battery/oneUpPower.c @@ -1,149 +1,93 @@ // SPDX-License-Identifier: GPL-2.0-only + + /* - * Power supply driver for testing. - * - * Copyright 2010 Anton Vorontsov - * - * Dynamic module parameter code from the Virtual Battery Driver - * Copyright (C) 2008 Pylone, Inc. - * By: Masashi YOKOTA - * Originally found here: - * http://downloads.pylone.jp/src/virtual_battery/virtual_battery-0.0.1.tar.bz2 + * Based heavily on: + * https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/drivers/power/test_power.c?id=refs/tags/v4.2.6/ */ +#include +#include +#include #include +#include +#include #include #include #include #include +#include #include enum test_power_id { - TEST_BATTERY, - TEST_AC, - TEST_POWER_NUM, + ONEUP_BATTERY, + ONEUP_AC, + ONEUP_POWER_NUM, }; // +// Useful definitions. Note that the TOTAL_* definitions need to be worked out... // +#define BLKDRV_NAME "oneUpPower" +#define TOTAL_LIFE_SECONDS (3 * 60 * 60) // Time in seconds +#define TOTAL_CHARGE (4800 * 1000) // Power in micro Amp Hours, uAH +#define TOTAL_CHARGE_FULL_SECONDS (60 * 60) // Time to full charge in seconds + // -#define BLKDRV_NAME "BAT0" -#define FMT_PREFIX ": %s[%d] " -#define DEBUG_INFO( fmt, arg...) printk( KERN_INFO BLKDRV_NAME FMT_PREFIX fmt, __func__, __LINE__, ##arg ) +// I2C Addresses +// +#define I2C_BUS 0x01 +#define BATTERY_ADDR 0x64 +#define CURRENT_HIGH_REG 0x0E +#define CURRENT_LOW_REG 0x0F +#define SOC_HIGH_REG 0x04 +#define SOC_LOW_REG 0x05 -#define TOTAL_LIFE_SECONDS (3 * 60 * 60) -#define TOTAL_CHARGE (2000 * 1000) // uAH -#define TOTAL_CHARGE_FULL_SECONDS (60 * 60) - -static int ac_online = 1; -static int battery_status = POWER_SUPPLY_STATUS_CHARGING; -static int battery_level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; -static int battery_health = POWER_SUPPLY_HEALTH_GOOD; -static int battery_present = 1; /* true */ -static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; -static int battery_capacity = 80; -static int battery_timeleft = TOTAL_LIFE_SECONDS; -static int battery_temperature = 30; -static int battery_voltage = (4200 * 1000); // uV -static bool module_initialized; - -static int power_get_ac_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - switch (psp) { - case POWER_SUPPLY_PROP_ONLINE: - val->intval = ac_online; - break; - default: - return -EINVAL; - } - return 0; -} - -static int get_battery_propertiesInts( struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val ) -{ - switch( psp ) { - case POWER_SUPPLY_PROP_STATUS: - val->intval = battery_status; - break; - case POWER_SUPPLY_PROP_CHARGE_TYPE: - val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; - break; - case POWER_SUPPLY_PROP_HEALTH: - val->intval = battery_health; - break; - case POWER_SUPPLY_PROP_PRESENT: - val->intval = battery_present; - break; - case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = battery_technology; - break; - case POWER_SUPPLY_PROP_CAPACITY_LEVEL: - val->intval = battery_level; - break; - case POWER_SUPPLY_PROP_CAPACITY: - val->intval = battery_capacity; - break; - case POWER_SUPPLY_PROP_CHARGE_EMPTY: - val->intval = 0; - break; - case POWER_SUPPLY_PROP_CHARGE_NOW: - val->intval = battery_capacity * TOTAL_CHARGE / 100; - break; - case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: - case POWER_SUPPLY_PROP_CHARGE_FULL: - val->intval = TOTAL_CHARGE; - break; - case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: - val->intval = battery_timeleft; - break; - case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: - val->intval = (100 - battery_capacity) * TOTAL_CHARGE_FULL_SECONDS / 100; - break; - case POWER_SUPPLY_PROP_TEMP: - val->intval = battery_temperature; - break; - case POWER_SUPPLY_PROP_VOLTAGE_NOW: - val->intval = battery_voltage; - break; - default: - pr_info("%s: some properties deliberately report errors.\n", - __func__); - return -EINVAL; - } - - return 0; -} +// +// Needed data structures +// +struct PowerStatus { + int status; // Status of the power supply + int capacity; // Capacity in percentage + int capacity_level; // What level are we at, CRITICAL,LOW,NORMAL,HIGH,FULL + int health; // State of the battery + int present; // Is the battery present (always YES) + int technology; // What technology is the battery (LION) + int timeleft; // How much time to we have left in seconds + int temperature; // What is the battery temperature + int voltage; // What is the current voltage of the battery + +} battery = { + .status = POWER_SUPPLY_STATUS_DISCHARGING, + .capacity = 90, + .capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH, + .health = POWER_SUPPLY_HEALTH_GOOD, + .present = 1, + .technology = POWER_SUPPLY_TECHNOLOGY_LION, + .timeleft = TOTAL_LIFE_SECONDS, + .temperature = 30, + .voltage = (4200 * 1000), // uV +}; +// +// Forward declairations +// static int get_battery_property(struct power_supply *psy, - enum power_supply_property psp, - union power_supply_propval *val) -{ - switch (psp) { - case POWER_SUPPLY_PROP_MODEL_NAME: - val->strval = "Test battery"; - break; - case POWER_SUPPLY_PROP_MANUFACTURER: - val->strval = "Linux"; - break; - case POWER_SUPPLY_PROP_SERIAL_NUMBER: - val->strval = UTS_RELEASE; - break; - default: - return get_battery_propertiesInts( psy, psp, val ); - } + enum power_supply_property psp, + union power_supply_propval *val); +static int get_ac_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val ); - return 0; -} +static int ac_online = 1; // Are we connected to an external power source? +static bool module_initialized = false; // Has the driver been initialized? +static struct task_struct *monitor_task = NULL; -static enum power_supply_property test_power_ac_props[] = { +static enum power_supply_property power_ac_props[] = { POWER_SUPPLY_PROP_ONLINE, }; -static enum power_supply_property test_power_battery_props[] = { +static enum power_supply_property power_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, @@ -164,228 +108,346 @@ static enum power_supply_property test_power_battery_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, }; -static char *test_power_ac_supplied_to[] = { +static char *ac_power_supplied_to[] = { "BAT0", }; -static struct power_supply *test_power_supplies[TEST_POWER_NUM]; +static struct power_supply *power_supplies[ONEUP_POWER_NUM]; -static const struct power_supply_desc test_power_desc[] = { - [TEST_BATTERY] = { +static const struct power_supply_desc power_descriptions[] = { + [ONEUP_BATTERY] = { .name = "BAT0", .type = POWER_SUPPLY_TYPE_BATTERY, - .properties = test_power_battery_props, - .num_properties = ARRAY_SIZE(test_power_battery_props), + .properties = power_battery_props, + .num_properties = ARRAY_SIZE(power_battery_props), .get_property = get_battery_property, }, - [TEST_AC] = { + [ONEUP_AC] = { .name = "AC0", .type = POWER_SUPPLY_TYPE_MAINS, - .properties = test_power_ac_props, - .num_properties = ARRAY_SIZE(test_power_ac_props), - .get_property = power_get_ac_property, + .properties = power_ac_props, + .num_properties = ARRAY_SIZE(power_ac_props), + .get_property = get_ac_property, }, }; -static const struct power_supply_config test_power_configs[] = { - { /* test_battery */ +static const struct power_supply_config power_configs[] = { + { /* battery */ }, { - /* test_ac */ - .supplied_to = test_power_ac_supplied_to, - .num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to), + /* ac */ + .supplied_to = ac_power_supplied_to, + .num_supplicants = ARRAY_SIZE(ac_power_supplied_to), }, }; -static int __init test_power_init(void) +/* + * set_power_states + * + * Given the current state of the capacity and status of the AC plug, + * make sure we normalize the data associated with those levels. + * + */ +static void set_power_states( void ) +{ + int capacity = battery.capacity; + + if( capacity > 95 ){ + battery.capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + } + else if( capacity > 85 ){ + battery.capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + } + else if( capacity > 40 ){ + battery.capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + } + else if( capacity > 30 ){ + battery.capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + } + else { + battery.capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + } + + if( ac_online ){ + if( capacity > 95 ){ + battery.status = POWER_SUPPLY_STATUS_FULL; + } + else { + battery.status = POWER_SUPPLY_STATUS_CHARGING; + } + } + else { + battery.status = POWER_SUPPLY_STATUS_DISCHARGING; + } +} + +static void check_ac_power( struct i2c_client *client ) +{ + int current_high; + int plugged_in; + + current_high = i2c_smbus_read_byte_data( client, CURRENT_HIGH_REG ); + if( (current_high & 0x80) > 0 ){ + plugged_in = 0; + } + else{ + plugged_in = 1; + } + + if( ac_online != plugged_in ){ + ac_online = plugged_in; + set_power_states(); + //power_supply_changed( power_supplies[ONEUP_AC] ); + } +} + +static void check_battery_state( struct i2c_client *client ) +{ + int SOCPercent; + + SOCPercent = i2c_smbus_read_byte_data( client, SOC_HIGH_REG ); + if( SOCPercent > 100 ) + SOCPercent = 100; + if( SOCPercent < 0 ) + SOCPercent = 0; + + if( battery.capacity != SOCPercent ){ + battery.capacity = SOCPercent; + set_power_states(); + power_supply_changed( power_supplies[ONEUP_BATTERY] ); + } +} + +static int system_monitor( void *args ) +{ + struct i2c_client *client = NULL; + struct i2c_adapter *adapter = NULL; + struct i2c_board_info board_info = {I2C_BOARD_INFO("argon40_battery", BATTERY_ADDR )}; + + pr_info( "Starting system monitor..." ); + + // + // Get an adapter so we can make an i2c client... + // + adapter = i2c_get_adapter( I2C_BUS ); + if( adapter == NULL ){ + pr_err( "Unable to get i2c adapter!" ); + return -1; + } + pr_info( "Created an I2C adapter..." ); + + // + // Build the i2c client... + // + client = i2c_new_client_device( adapter, &board_info ); + if( client == NULL ){ + pr_err( "Unable to create i2c client1!" ); + return -1; + } + + pr_info( "Created an I2C client device..." ); + + // + // Monitor until we are done... + // + while( true ){ + set_current_state( TASK_UNINTERRUPTIBLE ); + if( kthread_should_stop() ) + break; + + check_ac_power( client ); + check_battery_state( client ); + + set_current_state( TASK_INTERRUPTIBLE ); + schedule_timeout( HZ ); + } + + if( client ) + { + i2c_unregister_device( client ); + client = NULL; + } + + if( adapter ) + { + i2c_put_adapter( adapter ); + adapter = NULL; + } + pr_info( "system monitor is stopping..." ); + return 0; +} + +static int get_ac_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = ac_online; + break; + default: + return -EINVAL; + } + return 0; +} + +static int get_battery_int_property( struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val ) +{ + switch( psp ) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = battery.status; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = battery.health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = battery.present; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = battery.technology; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = battery.capacity_level; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battery.capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = battery.capacity * TOTAL_CHARGE / 100; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = TOTAL_CHARGE; + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + val->intval = battery.timeleft; + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + val->intval = (100 - battery.capacity) * TOTAL_CHARGE_FULL_SECONDS / 100; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = battery.temperature; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = battery.voltage; + break; + default: + pr_info("%s: some properties deliberately report errors.\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int get_battery_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = "oneUp Battery"; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "Argon40"; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = UTS_RELEASE; + break; + default: + return get_battery_int_property( psy, psp, val ); + } + + return 0; +} + +static int __init oneup_power_init(void) { int i; int ret; - BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies)); - BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_configs)); + pr_info( "Starting Power monitor..." ); + BUILD_BUG_ON(ONEUP_POWER_NUM != ARRAY_SIZE(power_supplies)); + BUILD_BUG_ON(ONEUP_POWER_NUM != ARRAY_SIZE(power_configs)); - for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) { - test_power_supplies[i] = power_supply_register(NULL, - &test_power_desc[i], - &test_power_configs[i]); - if (IS_ERR(test_power_supplies[i])) { + for (i = 0; i < ARRAY_SIZE(power_supplies); i++) { + power_supplies[i] = power_supply_register(NULL, + &power_descriptions[i], + &power_configs[i]); + if (IS_ERR(power_supplies[i])) { pr_err("%s: failed to register %s\n", __func__, - test_power_desc[i].name); - ret = PTR_ERR(test_power_supplies[i]); + power_descriptions[i].name); + ret = PTR_ERR(power_supplies[i]); goto failed; } } + monitor_task = kthread_run( system_monitor, NULL, "argon40_monitor" ); + if( monitor_task == NULL ){ + goto failed; + } + + set_power_states(); module_initialized = true; return 0; failed: + if( monitor_task ){ + kthread_stop( monitor_task ); + monitor_task = NULL; + } + while (--i >= 0) - power_supply_unregister(test_power_supplies[i]); + power_supply_unregister(power_supplies[i]); + return ret; } -module_init(test_power_init); +module_init(oneup_power_init); -static void __exit test_power_exit(void) +static void __exit oneup_power_exit(void) { int i; + // + // First up, stop the monitor task as its using resources + // + if( monitor_task ){ + kthread_stop( monitor_task ); + monitor_task = NULL; + } + /* Let's see how we handle changes... */ ac_online = 0; - battery_status = POWER_SUPPLY_STATUS_DISCHARGING; - for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) - power_supply_changed(test_power_supplies[i]); - pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n", - __func__); + battery.status = POWER_SUPPLY_STATUS_DISCHARGING; + + for (i = 0; i < ARRAY_SIZE(power_supplies); i++) + power_supply_changed(power_supplies[i]); + + pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n", __func__); ssleep(10); - for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) - power_supply_unregister(test_power_supplies[i]); + for (i = 0; i < ARRAY_SIZE(power_supplies); i++) + power_supply_unregister(power_supplies[i]); module_initialized = false; } -module_exit(test_power_exit); - - - -#define MAX_KEYLENGTH 256 -struct battery_property_map { - int value; - char const *key; -}; - -static struct battery_property_map map_ac_online[] = { - { 0, "off" }, - { 1, "on" }, - { -1, NULL }, -}; - -static struct battery_property_map map_health[] = { - { POWER_SUPPLY_HEALTH_GOOD, "good" }, - { POWER_SUPPLY_HEALTH_OVERHEAT, "overheat" }, - { POWER_SUPPLY_HEALTH_DEAD, "dead" }, - { POWER_SUPPLY_HEALTH_OVERVOLTAGE, "overvoltage" }, - { POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, "failure" }, - { -1, NULL }, -}; - -static struct battery_property_map map_present[] = { - { 0, "false" }, - { 1, "true" }, - { -1, NULL }, -}; - -static int map_get_value(struct battery_property_map *map, const char *key, - int def_val) -{ - char buf[MAX_KEYLENGTH]; - int cr; - - strscpy(buf, key, MAX_KEYLENGTH); - - cr = strnlen(buf, MAX_KEYLENGTH) - 1; - if (cr < 0) - return def_val; - if (buf[cr] == '\n') - buf[cr] = '\0'; - - while (map->key) { - if (strncasecmp(map->key, buf, MAX_KEYLENGTH) == 0) - return map->value; - map++; - } - - return def_val; -} - - -static const char *map_get_key(struct battery_property_map *map, int value, - const char *def_key) -{ - while (map->key) { - if (map->value == value) - return map->key; - map++; - } - - return def_key; -} +module_exit(oneup_power_exit); static inline void signal_power_supply_changed(struct power_supply *psy) { - if (module_initialized) - power_supply_changed(psy); + if (module_initialized) + power_supply_changed(psy); } -static int param_set_ac_online(const char *key, const struct kernel_param *kp) -{ - ac_online = map_get_value(map_ac_online, key, ac_online); - signal_power_supply_changed(test_power_supplies[TEST_AC]); - return 0; -} - -static int param_get_ac_online(char *buffer, const struct kernel_param *kp) -{ - return sprintf(buffer, "%s\n", - map_get_key(map_ac_online, ac_online, "unknown")); -} - -static int param_set_battery_health(const char *key, - const struct kernel_param *kp) -{ - battery_health = map_get_value(map_health, key, battery_health); - signal_power_supply_changed(test_power_supplies[TEST_BATTERY]); - return 0; -} - -static int param_get_battery_health(char *buffer, const struct kernel_param *kp) -{ - return sprintf(buffer, "%s\n", - map_get_key(map_ac_online, battery_health, "unknown")); -} - -static int param_set_battery_present(const char *key, - const struct kernel_param *kp) -{ - battery_present = map_get_value(map_present, key, battery_present); - signal_power_supply_changed(test_power_supplies[TEST_AC]); - return 0; -} - -static int param_get_battery_present(char *buffer, - const struct kernel_param *kp) -{ - return sprintf(buffer, "%s\n", - map_get_key(map_ac_online, battery_present, "unknown")); -} - -static const struct kernel_param_ops param_ops_ac_online = { - .set = param_set_ac_online, - .get = param_get_ac_online, -}; - -static const struct kernel_param_ops param_ops_battery_present = { - .set = param_set_battery_present, - .get = param_get_battery_present, -}; - -static const struct kernel_param_ops param_ops_battery_health = { - .set = param_set_battery_health, - .get = param_get_battery_health, -}; - -#define param_check_ac_online(name, p) __param_check(name, p, void); -#define param_check_battery_present(name, p) __param_check(name, p, void); - -module_param(ac_online, ac_online, 0644); -MODULE_PARM_DESC(ac_online, "AC charging state "); - -module_param(battery_present, battery_present, 0644); -MODULE_PARM_DESC(battery_present, - "battery presence state "); - -MODULE_DESCRIPTION("Power supply driver for testing"); -MODULE_AUTHOR("Anton Vorontsov "); +MODULE_DESCRIPTION("Power supply driver for Argon40 1UP"); +MODULE_AUTHOR("Jeff Curless "); MODULE_LICENSE("GPL"); diff --git a/battery/remove b/battery/remove index 630e203..a058f3e 100755 --- a/battery/remove +++ b/battery/remove @@ -1 +1 @@ -sudo rmmod test_power +sudo rmmod oneUpPower