Linux power supply class hacking

/***************************************************************************
 *                    Linux power supply class hacking
 * 声明:
 *     本文主要是记录linux电源管理的工作机制是什么,那些供Android jni使用
 * 的属性文件是如何生成的,调用机制是什么。
 *
 *                                         2016-2-23 深圳 南山平山村 曾剑锋
 **************************************************************************/



static int __init power_supply_class_init(void)
{
    power_supply_class = class_create(THIS_MODULE, "power_supply");

    if (IS_ERR(power_supply_class))
        return PTR_ERR(power_supply_class);

    power_supply_class->dev_uevent = power_supply_uevent;    ------------------+
    power_supply_init_attrs(&power_supply_dev_type);         ------------------*-+
                    |                                                          | |
    return 0;       +--------------------------------------------------------+ | |
}                                                                            | | |
                                                                             | | |
static void __exit power_supply_class_exit(void)                             | | |
{                                                                            | | |
    class_destroy(power_supply_class);                                       | | |
}                                                                            | | |
                                                                             | | |
subsys_initcall(power_supply_class_init);                                    | | |
module_exit(power_supply_class_exit);                                        | | |
                                                                             | | |
MODULE_DESCRIPTION("Universal power supply monitor class");                  | | |
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "                                 | | |
          "Szabolcs Gyurko, "                                                | | |
          "Anton Vorontsov <cbou@mail.ru>");                                 | | |
MODULE_LICENSE("GPL");                                                       | | |
                                                                             | | |
int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)   <-*-+ |
{                                                                            |   |
    struct power_supply *psy = dev_get_drvdata(dev);                         |   |
    int ret = 0, j;                                                          |   |
    char *prop_buf;                                                          |   |
    char *attrname;                                                          |   |
                                                                             |   |
    dev_dbg(dev, "uevent\n");                                                |   |
                                                                             |   |
    if (!psy || !psy->dev) {                                                 |   |
        dev_dbg(dev, "No power supply yet\n");                               |   |
        return ret;                                                          |   |
    }                                                                        |   |
                                                                             |   |
    dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);                       |   |
                                                                             |   |
    ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);            |   |
    if (ret)                                                                 |   |
        return ret;                                                          |   |
                                                                             |   |
    prop_buf = (char *)get_zeroed_page(GFP_KERNEL);                          |   |
    if (!prop_buf)                                                           |   |
        return -ENOMEM;                                                      |   |
                                                                             |   |
    for (j = 0; j < psy->num_properties; j++) {                              |   |
        struct device_attribute *attr;                                       |   |
        char *line;                                                          |   |
                                                                             |   |
        attr = &power_supply_attrs[psy->properties[j]];                      |   |
                                                                             |   |
        ret = power_supply_show_property(dev, attr, prop_buf);               |   |
        if (ret == -ENODEV || ret == -ENODATA) {                             |   |
            /* When a battery is absent, we expect -ENODEV. Don't abort;     |   |
               send the uevent with at least the the PRESENT=0 property */   |   |
            ret = 0;                                                         |   |
            continue;                                                        |   |
        }                                                                    |   |
                                                                             |   |
        if (ret < 0)                                                         |   |
            goto out;                                                        |   |
                                                                             |   |
        line = strchr(prop_buf, '\n');                                       |   |
        if (line)                                                            |   |
            *line = 0;                                                       |   |
                                                                             |   |
        attrname = kstruprdup(attr->attr.name, GFP_KERNEL);                  |   |
        if (!attrname) {                                                     |   |
            ret = -ENOMEM;                                                   |   |
            goto out;                                                        |   |
        }                                                                    |   |
                                                                             |   |
        dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);                    |   |
                                                                             |   |
        ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf); |   |
        kfree(attrname);                                                     |   |
        if (ret)                                                             |   |
            goto out;                                                        |   |
    }                                                                        |   |
                                                                             |   |
out:                                                                         |   |
    free_page((unsigned long)prop_buf);                                      |   |
                                                                             |   |
    return ret;                                                              |   |
}                                                                            |   |
                                                                             |   |
static struct device_type power_supply_dev_type;        <--------------------*---+
                   ^                                                         |
/*                 +-----------------------------------------------+         |
 * The type of device, "struct device" is embedded in. A class     |         |
 * or bus can contain devices of different types                   |         |
 * like "partitions" and "disks", "mouse" and "event".             |         |
 * This identifies the device type and carries type-specific       |         |
 * information, equivalent to the kobj_type of a kobject.          |         |
 * If "name" is specified, the uevent will contain it in           |         |
 * the DEVTYPE variable.                                           |         |
 */                                                                |         |
struct device_type {                        <----------------------+         |
    const char *name;                                                        |
    const struct attribute_group **groups;                                   |
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);          |
    char *(*devnode)(struct device *dev, mode_t *mode);                      |
    void (*release)(struct device *dev);                                     |
                                                                             |
    const struct dev_pm_ops *pm;                                             |
};                                                                           |
                                                                             |
void power_supply_init_attrs(struct device_type *dev_type)      <------------+
{
    int i;
                                                                ----------+
    dev_type->groups = power_supply_attr_groups;                          |
                                                                          |
    for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)                  |
        __power_supply_attrs[i] = &power_supply_attrs[i].attr;    ------+ |
}                                                                       | |
                                                                        | |
static const struct attribute_group *power_supply_attr_groups[] = {  <--*-+
    &power_supply_attr_group,                         ------------+     |
    NULL,                                                         |     |
};                                                                |     |
                                                                  |     |
static struct attribute_group power_supply_attr_group = {   <-----+     |
    .attrs = __power_supply_attrs,                          ------+     |
    .is_visible = power_supply_attr_is_visible,                   |     |
};                                                                |     |
                                                                  |     |
static struct attribute *                                         |     |
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];   <-----+   --+
                                                                        |
/* Must be in the same order as POWER_SUPPLY_PROP_* */                  |
static struct device_attribute power_supply_attrs[] = {     <-----------+
    /* Properties of type `int' */
    POWER_SUPPLY_ATTR(status),
    POWER_SUPPLY_ATTR(charge_type),
    POWER_SUPPLY_ATTR(health),
    POWER_SUPPLY_ATTR(present),
    POWER_SUPPLY_ATTR(online),
    POWER_SUPPLY_ATTR(technology),
    POWER_SUPPLY_ATTR(cycle_count),
    POWER_SUPPLY_ATTR(voltage_max),
    POWER_SUPPLY_ATTR(voltage_min),
    POWER_SUPPLY_ATTR(voltage_max_design),
    POWER_SUPPLY_ATTR(voltage_min_design),
    POWER_SUPPLY_ATTR(voltage_now),
    POWER_SUPPLY_ATTR(voltage_avg),
    POWER_SUPPLY_ATTR(current_max),
    POWER_SUPPLY_ATTR(current_now),
    POWER_SUPPLY_ATTR(current_avg),
    POWER_SUPPLY_ATTR(power_now),
    POWER_SUPPLY_ATTR(power_avg),
    POWER_SUPPLY_ATTR(charge_full_design),
    POWER_SUPPLY_ATTR(charge_empty_design),
    POWER_SUPPLY_ATTR(charge_full),
    POWER_SUPPLY_ATTR(charge_empty),
    POWER_SUPPLY_ATTR(charge_now),
    POWER_SUPPLY_ATTR(charge_avg),
    POWER_SUPPLY_ATTR(charge_counter),
    POWER_SUPPLY_ATTR(energy_full_design),
    POWER_SUPPLY_ATTR(energy_empty_design),
    POWER_SUPPLY_ATTR(energy_full),
    POWER_SUPPLY_ATTR(energy_empty),
    POWER_SUPPLY_ATTR(energy_now),
    POWER_SUPPLY_ATTR(energy_avg),
    POWER_SUPPLY_ATTR(capacity),
    POWER_SUPPLY_ATTR(capacity_level),
    POWER_SUPPLY_ATTR(temp),
    POWER_SUPPLY_ATTR(temp_ambient),
    POWER_SUPPLY_ATTR(time_to_empty_now),
    POWER_SUPPLY_ATTR(time_to_empty_avg),
    POWER_SUPPLY_ATTR(time_to_full_now),
    POWER_SUPPLY_ATTR(time_to_full_avg),
    POWER_SUPPLY_ATTR(type),
    /* Properties of type `const char *' */
    POWER_SUPPLY_ATTR(model_name),        --------------+
    POWER_SUPPLY_ATTR(manufacturer),                    |
    POWER_SUPPLY_ATTR(serial_number),                   |
};               +--------------------------------------+
                 v
#define POWER_SUPPLY_ATTR(_name)                    \
{                                    \
    .attr = { .name = #_name },                    \
    .show = power_supply_show_property,                \     ---------+
    .store = power_supply_store_property,                \   ---------*-+
}                                                                     | |
                                                                      | |
static ssize_t power_supply_show_property(struct device *dev,   <-----+ |
                      struct device_attribute *attr,                    |
                      char *buf) {                                      |
    static char *type_text[] = {                                        |
        "Battery", "UPS", "Mains", "USB",                               |
        "USB_DCP", "USB_CDP", "USB_ACA"                                 |
    };                                                                  |
    static char *status_text[] = {                                      |
        "Unknown", "Charging", "Discharging", "Not charging", "Full"    |
    };                                                                  |
    static char *charge_type[] = {                                      |
        "Unknown", "N/A", "Trickle", "Fast"                             |
    };                                                                  |
    static char *health_text[] = {                                      |
        "Unknown", "Good", "Overheat", "Dead", "Over voltage",          |
        "Unspecified failure", "Cold",                                  |
    };                                                                  |
    static char *technology_text[] = {                                  |
        "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd",         |
        "LiMn"                                                          |
    };                                                                  |
    static char *capacity_level_text[] = {                              |
        "Unknown", "Critical", "Low", "Normal", "High", "Full"          |
    };                                                                  |
    ssize_t ret = 0;                                                    |
    struct power_supply *psy = dev_get_drvdata(dev);            --------*-+
    const ptrdiff_t off = attr - power_supply_attrs;                    | |
    union power_supply_propval value;                                   | |
                                                                        | |
    if (off == POWER_SUPPLY_PROP_TYPE)                                  | |
        value.intval = psy->type;                                       | |
    else                                                                | |
        ret = psy->get_property(psy, off, &value);              <-------*-*------+
                                                                        | |      |
    if (ret < 0) {                                                      | |      |
        if (ret == -ENODATA)                                            | |      |
            dev_dbg(dev, "driver has no data for `%s' property\n",      | |      |
                attr->attr.name);                                       | |      |
        else if (ret != -ENODEV)                                        | |      |
            dev_err(dev, "driver failed to report `%s' property\n",     | |      |
                attr->attr.name);                                       | |      |
        return ret;                                                     | |      |
    }                                                                   | |      |
                                                                        | |      |
    if (off == POWER_SUPPLY_PROP_STATUS)                                | |      |
        return sprintf(buf, "%s\n", status_text[value.intval]);         | |      |
    else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)                      | |      |
        return sprintf(buf, "%s\n", charge_type[value.intval]);         | |      |
    else if (off == POWER_SUPPLY_PROP_HEALTH)                           | |      |
        return sprintf(buf, "%s\n", health_text[value.intval]);         | |      |
    else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)                       | |      |
        return sprintf(buf, "%s\n", technology_text[value.intval]);     | |      |
    else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)                   | |      |
        return sprintf(buf, "%s\n", capacity_level_text[value.intval]); | |      |
    else if (off == POWER_SUPPLY_PROP_TYPE)                             | |      |
        return sprintf(buf, "%s\n", type_text[value.intval]);           | |      |
    else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)                       | |      |
        return sprintf(buf, "%s\n", value.strval);                      | |      |
                                                                        | |      |
    return sprintf(buf, "%d\n", value.intval);                          | |      |
}                                                                       | |      |
                                                                        | |      |
static ssize_t power_supply_store_property(struct device *dev,  <-------+ |      |
                       struct device_attribute *attr,                     |      |
                       const char *buf, size_t count) {                   |      |
    ssize_t ret;                                                          |      |
    struct power_supply *psy = dev_get_drvdata(dev);            ----------+      |
    const ptrdiff_t off = attr - power_supply_attrs;                      |      |
    union power_supply_propval value;                                     |      |
    long long_val;                                                        |      |
                                                                          |      |
    /* TODO: support other types than int */                              |      |
    ret = strict_strtol(buf, 10, &long_val);                              |      |
    if (ret < 0)                                                          |      |
        return ret;                                                       |      |
                                                                          |      |
    value.intval = long_val;                                              |      |
                                                                          |      |
    ret = psy->set_property(psy, off, &value);                            |      |
    if (ret < 0)                                                          |      |
        return ret;                                                       |      |
                                                                          |      |
    return count;                                                         |      |
}                                                                         |      |
                                                                          |      |
void *dev_get_drvdata(const struct device *dev)     <-----+    <----------+      |
{                                                         +                      |
    if (dev && dev->p)                                    +                      |
        return dev->p->driver_data;                       +                      |
    return NULL;                                          +                      |
}                                                         +                      |
EXPORT_SYMBOL(dev_get_drvdata);                           +                      |
                                                          +                      |
int dev_set_drvdata(struct device *dev, void *data) <-----+    <----------+      |
{                                                                         |      |
    int error;                                                            |      |
                                                                          |      |
    if (!dev->p) {                                                        |      |
        error = device_private_init(dev);                                 |      |
        if (error)                                                        |      |
            return error;                                                 |      |
    }                                                                     |      |
    dev->p->driver_data = data;                                           |      |
    return 0;                                                             |      |
}                                                                         |      |
EXPORT_SYMBOL(dev_set_drvdata);                                           |      |
                                                                          |      |
static inline void i2c_set_clientdata(struct i2c_client *dev, void *data) | ----+|
{                                                                         |     ||
    dev_set_drvdata(&dev->dev, data);        -----------------------------+     ||
}                                                                               ||
                                                                                ||
static int __init bq27x00_battery_probe(struct i2c_client *client,              ||
                 const struct i2c_device_id *id)                                ||
{                                                                               ||
    char *name;                                                                 ||
    struct bq27x00_device_info *di;                                             ||
    int num;                                                                    ||
    int retval = 0;                                                             ||
    u8 *regs;                                                                   ||
                                                                                ||
    /* Get new ID for the new battery device */                                 ||
    retval = idr_pre_get(&battery_id, GFP_KERNEL);                              ||
    if (retval == 0)                                                            ||
        return -ENOMEM;                                                         ||
    mutex_lock(&battery_mutex);                                                 ||
    retval = idr_get_new(&battery_id, client, &num);                            ||
    mutex_unlock(&battery_mutex);                                               ||
    if (retval < 0)                                                             ||
        return retval;                                                          ||
                                                                                ||
    name = kasprintf(GFP_KERNEL, "%s-%d", id->name, num);                       ||
    if (!name) {                                                                ||
        dev_err(&client->dev, "failed to allocate device name\n");              ||
        retval = -ENOMEM;                                                       ||
        goto batt_failed_1;                                                     ||
    }                                                                           ||
                                                                                ||
    di = kzalloc(sizeof(*di), GFP_KERNEL);                                      ||
    if (!di) {                                                                  ||
        dev_err(&client->dev, "failed to allocate device info data\n");         ||
        retval = -ENOMEM;                                                       ||
        goto batt_failed_2;                                                     ||
    }                                                                           ||
                                                                                ||
    di->id = num;                                                               ||
    di->dev = &client->dev;                                                     ||
    di->chip = id->driver_data;                                                 ||
    di->bat.name = name;                                                        ||
    di->bus.read = &bq27xxx_read_i2c;                                           ||
    di->bus.write = &bq27xxx_write_i2c;                                         ||
    di->bus.blk_read = bq27xxx_read_i2c_blk;                                    ||
    di->bus.blk_write = bq27xxx_write_i2c_blk;                                  ||
    di->dm_regs = NULL;                                                         ||
    di->dm_regs_count = 0;                                                      ||
                                                                                ||
    if (di->chip == BQ27200)                                                    ||
        regs = bq27200_regs;                                                    ||
    else if (di->chip == BQ27500)                                               ||
        regs = bq27500_regs;                                                    ||
    else if (di->chip == BQ27520)                                               ||
        regs = bq27520_regs;                                                    ||
    else if (di->chip == BQ2753X)                                               ||
        regs = bq2753x_regs;                                                    ||
    else if (di->chip == BQ274XX) {                                             ||
        regs = bq274xx_regs;                                                    ||
        di->dm_regs = bq274xx_dm_regs;                                          ||
        di->dm_regs_count = ARRAY_SIZE(bq274xx_dm_regs);                        ||
    } else if (di->chip == BQ276XX) {                                           ||
        /* commands are same as bq274xx, only DM is different */                ||
        regs = bq276xx_regs;                                                    ||
        di->dm_regs = bq276xx_dm_regs;                                          ||
        di->dm_regs_count = ARRAY_SIZE(bq276xx_dm_regs);                        ||
    } else {                                                                    ||
        dev_err(&client->dev,                                                   ||
            "Unexpected gas gague: %d\n", di->chip);                            ||
        regs = bq27520_regs;                                                    ||
    }                                                                           ||
                                                                                ||
    memcpy(di->regs, regs, NUM_REGS);                                           ||
                                                                                ||
    di->fw_ver = bq27x00_battery_read_fw_version(di);                           ||
    dev_info(&client->dev, "Gas Guage fw version is 0x%04x\n", di->fw_ver);     ||
                                                                                ||
    retval = bq27x00_powersupply_init(di);                         --------+    ||
    if (retval)                                                            |    ||
        goto batt_failed_3;                                                |    ||
                                                                           |    ||
    /* Schedule a polling after about 1 min */                             |    ||
    schedule_delayed_work(&di->work, 60 * HZ);                             |    ||
                                                                           |    ||
    i2c_set_clientdata(client, di);                                <-------|----+|
    retval = sysfs_create_group(&client->dev.kobj, &bq27x00_attr_group);   |     |
    if (retval)                                                            |     |
        dev_err(&client->dev, "could not create sysfs files\n");           |     |
                                                                           |     |
    return 0;                                                              |     |
                                                                           |     |
batt_failed_3:                                                             |     |
    kfree(di);                                                             |     |
batt_failed_2:                                                             |     |
    kfree(name);                                                           |     |
batt_failed_1:                                                             |     |
    mutex_lock(&battery_mutex);                                            |     |
    idr_remove(&battery_id, num);                                          |     |
    mutex_unlock(&battery_mutex);                                          |     |
                                                                           |     |
    return retval;                                                         |     |
}                                                                          |     |
                                                                           |     |
static int bq27x00_powersupply_init(struct bq27x00_device_info *di)  <-----+     |
{                                                    |                           |
    int ret;                                         +------------------+        |
         v--------------------------------------------------------------*------+ |
    di->bat.type = POWER_SUPPLY_TYPE_BATTERY;                           |      | |
    di->bat.properties = bq27x00_battery_props;                         |      | |
    di->bat.num_properties = ARRAY_SIZE(bq27x00_battery_props);         |      | |
    di->bat.get_property = bq27x00_battery_get_property;  ------------+-*------*-+
    di->bat.external_power_changed = bq27x00_external_power_changed;  | |      |
                                                                      | |      |
    INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll);               | |      |
    mutex_init(&di->lock);                                            | |      |
                                                                      | |      |
    ret = power_supply_register(di->dev, &di->bat);         ----------*-*------*----+
    if (ret) {                                                        | |      |    |
        dev_err(di->dev, "failed to register battery: %d\n", ret);    | |      |    |
        return ret;                                                   | |      |    |
    }                                                                 | |      |    |
                                                                      | |      |    |
    dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);   | |      |    |
                                                                      | |      |    |
    bq27x00_update(di);                                 --------------*-*-+    |    |
                                                                      | | |    |    |
    return 0;                                                         | | |    |    |
}                                                                     | | |    |    |
                                                                      | | |    |    |
struct bq27x00_device_info {                                 <--------*-+ |    |    |
    struct device         *dev;                                       |   |    |    |
    int            id;                                                |   |    |    |
    enum bq27x00_chip    chip;                                        |   |    |    |
                                                                      |   |    |    |
    struct bq27x00_reg_cache cache;    ---------+            <--------*---*----*---+|
    int charge_design_full;                     |                     |   |    |   ||
                                                |                     |   |    |   ||
    unsigned long last_update;                  |                     |   |    |   ||
    struct delayed_work work;                   |                     |   |    |   ||
                                                |                     |   |    |   ||
    struct power_supply    bat;                 |            <--------*---*----+   ||
                                                |                     |   |        ||
    struct bq27x00_access_methods bus;          |                     |   |        ||
                                                |                     |   |        ||
    struct mutex lock;                          |                     |   |        ||
};                                              |                     |   |        ||
                                                |                     |   |        ||
struct bq27x00_reg_cache {            <---------+                     |   |        ||
    int temperature;                                                  |   |        ||
    int time_to_empty;                                                |   |        ||
    int time_to_empty_avg;                                            |   |        ||
    int time_to_full;                                                 |   |        ||
    int charge_full;                                                  |   |        ||
    int cycle_count;                                                  |   |        ||
    int capacity;                                                     |   |        ||
    int flags;                                                        |   |        ||
                                                                      |   |        ||
    int current_now;                                                  |   |        ||
};                                                                    |   |        ||
                                                                      |   |        ||
static int bq27x00_battery_get_property(struct power_supply *psy, <---+   |        ||
                    enum power_supply_property psp,                       |        ||
                    union power_supply_propval *val)                      |        ||
{                                                                         |        ||
    int ret = 0;                                                          |        ||
    struct bq27x00_device_info *di = to_bq27x00_device_info(psy);         |        ||
                                                                          |        ||
    mutex_lock(&di->lock);                                                |        ||
    if (time_is_before_jiffies(di->last_update + 5 * HZ)) {               |        ||
        cancel_delayed_work_sync(&di->work);                              |        ||
        bq27x00_battery_poll(&di->work.work);                             |        ||
    }                                                                     |        ||
    mutex_unlock(&di->lock);                                              |        ||
                                                                          |        ||
    if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0)          |        ||
        return -ENODEV;                                                   |        ||
                                                                          |        ||
    switch (psp) {                                                        |        ||
    case POWER_SUPPLY_PROP_STATUS:                                        |        ||
        ret = bq27x00_battery_status(di, val);                            |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_VOLTAGE_NOW:                                   |        ||
        ret = bq27x00_battery_voltage(di, val);                           |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_PRESENT:                                       |        ||
        val->intval = di->cache.flags < 0 ? 0 : 1;                        |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_CURRENT_NOW:                                   |        ||
        ret = bq27x00_battery_current(di, val);                           |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_CAPACITY:                                      |        ||
        ret = bq27x00_simple_value(di->cache.capacity, val);              |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_TEMP:                                          |        ||
        ret = bq27x00_battery_temperature(di, val);                       |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:                             |        ||
        ret = bq27x00_simple_value(di->cache.time_to_empty, val);         |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:                             |        ||
        ret = bq27x00_simple_value(di->cache.time_to_empty_avg, val);     |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:                              |        ||
        ret = bq27x00_simple_value(di->cache.time_to_full, val);          |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_TECHNOLOGY:                                    |        ||
        val->intval = POWER_SUPPLY_TECHNOLOGY_LION;                       |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_CHARGE_NOW:                                    |        ||
        ret = bq27x00_simple_value(bq27x00_battery_read_nac(di), val);    |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_CHARGE_FULL:                                   |        ||
        ret = bq27x00_simple_value(di->cache.charge_full, val);           |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:                            |        ||
        ret = bq27x00_simple_value(di->charge_design_full, val);          |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_CYCLE_COUNT:                                   |        ||
        ret = bq27x00_simple_value(di->cache.cycle_count, val);           |        ||
        break;                                                            |        ||
    case POWER_SUPPLY_PROP_ENERGY_NOW:                                    |        ||
        ret = bq27x00_battery_energy(di, val);                            |        ||
        break;                                                            |        ||
    default:                                                              |        ||
        return -EINVAL;                                                   |        ||
    }                                                                     |        ||
                                                                          |        ||
    return ret;                                                           |        ||
}                                                                         |        ||
                                                                          |        ||
static void bq27x00_update(struct bq27x00_device_info *di)   <------------+        ||
{                                                                                  ||
    struct bq27x00_reg_cache cache = {0, };                                        ||
    bool is_bq27500 = di->chip == BQ27500;                                         ||
                                                                                   ||
    cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500);                 ||
    if (cache.flags >= 0) {                                                        ||
        cache.capacity = bq27x00_battery_read_rsoc(di);                            ||
        cache.temperature = bq27x00_read(di, BQ27x00_REG_TEMP, false);             ||
        cache.time_to_empty = bq27x00_battery_read_time(di, BQ27x00_REG_TTE);      ||
        cache.time_to_empty_avg = bq27x00_battery_read_time(di, BQ27x00_REG_TTECP);||
        cache.time_to_full = bq27x00_battery_read_time(di, BQ27x00_REG_TTF);       ||
        cache.charge_full = bq27x00_battery_read_lmd(di);                          ||
        cache.cycle_count = bq27x00_battery_read_cyct(di);                         ||
                                                                                   ||
        if (!is_bq27500)                                                           ||
            cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false);           ||
                                                                                   ||
        /* We only have to read charge design full once */                         ||
        if (di->charge_design_full <= 0)                                           ||
            di->charge_design_full = bq27x00_battery_read_ilmd(di);                ||
    }                                                                              ||
                                                                                   ||
    /* Ignore current_now which is a snapshot of the current battery state         ||
     * and is likely to be different even between two consecutive reads */         ||
    if (memcmp(&di->cache, &cache, sizeof(cache) - sizeof(int)) != 0) {      ------+|
        di->cache = cache;                                                          |
        power_supply_changed(&di->bat);                                             |
    }                                                                               |
                                                                                    |
    di->last_update = jiffies;                                                      |
}                                                                                   |
                                                                                    |
int power_supply_register(struct device *parent, struct power_supply *psy)   <------+
{
    struct device *dev;
    int rc;

    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;

    device_initialize(dev);

    dev->class = power_supply_class;
    dev->type = &power_supply_dev_type;
    dev->parent = parent;
    dev->release = power_supply_dev_release;
    dev_set_drvdata(dev, psy);
    psy->dev = dev;

    INIT_WORK(&psy->changed_work, power_supply_changed_work);

    rc = kobject_set_name(&dev->kobj, "%s", psy->name);
    if (rc)
        goto kobject_set_name_failed;

    spin_lock_init(&psy->changed_lock);
    wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply");

    rc = device_add(dev);
    if (rc)
        goto device_add_failed;

    rc = power_supply_create_triggers(psy);
    if (rc)
        goto create_triggers_failed;

    power_supply_changed(psy);

    goto success;

create_triggers_failed:
    wake_lock_destroy(&psy->work_wake_lock);
    device_del(dev);
kobject_set_name_failed:
device_add_failed:
    put_device(dev);
success:
    return rc;
}
EXPORT_SYMBOL_GPL(power_supply_register);

 

posted on 2016-02-23 11:33  zengjf  阅读(550)  评论(0编辑  收藏  举报

导航