zoukankan      html  css  js  c++  java
  • 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
    ");                                                |   |
                                                                                 |   |
        if (!psy || !psy->dev) {                                                 |   |
            dev_dbg(dev, "No power supply yet
    ");                               |   |
            return ret;                                                          |   |
        }                                                                        |   |
                                                                                 |   |
        dev_dbg(dev, "POWER_SUPPLY_NAME=%s
    ", 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, '
    ');                                       |   |
            if (line)                                                            |   |
                *line = 0;                                                       |   |
                                                                                 |   |
            attrname = kstruprdup(attr->attr.name, GFP_KERNEL);                  |   |
            if (!attrname) {                                                     |   |
                ret = -ENOMEM;                                                   |   |
                goto out;                                                        |   |
            }                                                                    |   |
                                                                                 |   |
            dev_dbg(dev, "prop %s=%s
    ", 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
    ",      | |      |
                    attr->attr.name);                                       | |      |
            else if (ret != -ENODEV)                                        | |      |
                dev_err(dev, "driver failed to report `%s' property
    ",     | |      |
                    attr->attr.name);                                       | |      |
            return ret;                                                     | |      |
        }                                                                   | |      |
                                                                            | |      |
        if (off == POWER_SUPPLY_PROP_STATUS)                                | |      |
            return sprintf(buf, "%s
    ", status_text[value.intval]);         | |      |
        else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE)                      | |      |
            return sprintf(buf, "%s
    ", charge_type[value.intval]);         | |      |
        else if (off == POWER_SUPPLY_PROP_HEALTH)                           | |      |
            return sprintf(buf, "%s
    ", health_text[value.intval]);         | |      |
        else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)                       | |      |
            return sprintf(buf, "%s
    ", technology_text[value.intval]);     | |      |
        else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)                   | |      |
            return sprintf(buf, "%s
    ", capacity_level_text[value.intval]); | |      |
        else if (off == POWER_SUPPLY_PROP_TYPE)                             | |      |
            return sprintf(buf, "%s
    ", type_text[value.intval]);           | |      |
        else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)                       | |      |
            return sprintf(buf, "%s
    ", value.strval);                      | |      |
                                                                            | |      |
        return sprintf(buf, "%d
    ", 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
    ");              ||
            retval = -ENOMEM;                                                       ||
            goto batt_failed_1;                                                     ||
        }                                                                           ||
                                                                                    ||
        di = kzalloc(sizeof(*di), GFP_KERNEL);                                      ||
        if (!di) {                                                                  ||
            dev_err(&client->dev, "failed to allocate device info data
    ");         ||
            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
    ", 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
    ", 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
    ");           |     |
                                                                               |     |
        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
    ", ret);    | |      |    |
            return ret;                                                   | |      |    |
        }                                                                 | |      |    |
                                                                          | |      |    |
        dev_info(di->dev, "support ver. %s enabled
    ", 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);
  • 相关阅读:
    正则表达式(含递归用法)
    hive tricks
    树的数据结构
    基本排序算法
    佛祖保佑永无BUG
    客户问:“能再便宜点吗”,90%的销售顾问都回答错了?
    AutoMapper的介绍与使用(二)
    AutoMapper的介绍与使用(一)
    hasattr()、getattr()、setattr()函数的使用
    类与对象-内存存储形态
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/5209339.html
Copyright © 2011-2022 走看看