Mostly working
Need to perform code cleanup, and make sure startup of the driver contains everything it needs to properly verify the status of the battery. Could use code to get the voltage etc, however I don't see that code in the argon40 python stuff so I'd have to investigate the I2C device.
This commit is contained in:
@@ -1,149 +1,93 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
|
||||
/*
|
||||
* Power supply driver for testing.
|
||||
*
|
||||
* Copyright 2010 Anton Vorontsov <cbouatmailru@gmail.com>
|
||||
*
|
||||
* Dynamic module parameter code from the Virtual Battery Driver
|
||||
* Copyright (C) 2008 Pylone, Inc.
|
||||
* By: Masashi YOKOTA <yokota@pylone.jp>
|
||||
* 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 <linux/fs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <generated/utsrelease.h>
|
||||
|
||||
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 <on|off>");
|
||||
|
||||
module_param(battery_present, battery_present, 0644);
|
||||
MODULE_PARM_DESC(battery_present,
|
||||
"battery presence state <good|overheat|dead|overvoltage|failure>");
|
||||
|
||||
MODULE_DESCRIPTION("Power supply driver for testing");
|
||||
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
|
||||
MODULE_DESCRIPTION("Power supply driver for Argon40 1UP");
|
||||
MODULE_AUTHOR("Jeff Curless <jeff@thecurlesses.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user