电池驱动 涉及文件
power_supply.h
power_supply_core.c 电池核心
power_supply_leds.c 充电指示灯
power_supply_sysfs.c 数据上报
max17040_battery.c 电源芯片
/* power_supply_core.c
注册class
注册uevent
电池信息变化更新事件 power_supply_changed()
power_supply_sysfs.c
事件跟新事件中状态 power_supply_uevent()
max17040_battery.c
注册IIC设备
注册电源属性 max17040_battery_props()
注册延时工作(循环读取电源状态) max17040_work()
事件更新 max17040_get_property()
流程
max17040_work()-> power_supply_changed()-> power_supply_uevent() -> max17040_get_property()
power_supply_core.c
static int __init power_supply_class_init(void)
{
power_supply_class = class_create(THIS_MODULE, "power_supply"); //注册class到sys
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;
}
subsys_initcall(power_supply_class_init); //初始化
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; //绑定class
dev->type = &power_supply_dev_type; //绑定属性列表函数
dev->parent = parent;
dev->release = power_supply_dev_release;
dev_set_drvdata(dev, psy);
psy->dev = dev;
rc = kobject_set_name(&dev->kobj, "%s", psy->name);
if (rc)
goto kobject_set_name_failed;
rc = device_add(dev);
if (rc)
goto device_add_failed;
INIT_WORK(&psy->changed_work, power_supply_changed_work); //注册工作队列
spin_lock_init(&psy->changed_lock);
wake_lock_init(&psy->work_wake_lock, WAKE_LOCK_SUSPEND, "power-supply");
rc = power_supply_create_triggers(psy);
if (rc)
goto create_triggers_failed;
power_supply_changed(psy); //调度
#if defined CONFIG_VBATTERY_BACKEND || defined CONFIG_VBATTERY_BACKEND_MODULE
blocking_notifier_call_chain(&vbattery_be_notifier_list, 0, psy);
#endif
goto success;
create_triggers_failed:
wake_lock_destroy(&psy->work_wake_lock);
device_unregister(psy->dev);
kobject_set_name_failed:
device_add_failed:
kfree(dev);
success:
return rc;
}
static void power_supply_changed_work(struct work_struct *work)
{
unsigned long flags;
struct power_supply *psy = container_of(work, struct power_supply,
changed_work);
dev_dbg(psy->dev, "%s
", __func__);
spin_lock_irqsave(&psy->changed_lock, flags);
if (psy->changed) {
psy->changed = false;
spin_unlock_irqrestore(&psy->changed_lock, flags);
class_for_each_device(power_supply_class, NULL, psy,
__power_supply_changed_work);
power_supply_update_leds(psy);
kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);//更新事件,触发事件函数
spin_lock_irqsave(&psy->changed_lock, flags);
}
if (!psy->changed)
wake_unlock(&psy->work_wake_lock);
spin_unlock_irqrestore(&psy->changed_lock, flags);
}
void power_supply_changed(struct power_supply *psy)
{
unsigned long flags;
dev_dbg(psy->dev, "%s
", __func__);
spin_lock_irqsave(&psy->changed_lock, flags);
psy->changed = true;
wake_lock(&psy->work_wake_lock);
spin_unlock_irqrestore(&psy->changed_lock, flags);
schedule_work(&psy->changed_work); //开始工作
}
--------------------------
power_supply_sysfs.c文件
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) {
/* 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 ssize_t power_supply_show_property(struct device *dev,
struct device_attribute *attr,
char *buf) {
static char *type_text[] = {
"Battery", "UPS", "Mains", "USB"
};
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);//调用电池的get_property更新数据
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);
}
----------------------
max17040_battery.c//文件
static struct i2c_driver max17040_i2c_driver = {
.driver = {
.name = "max17040",
},
.probe = max17040_probe,
.remove = __devexit_p(max17040_remove),
.suspend = max17040_suspend,
.resume = max17040_resume,
.id_table = max17040_id,
};
static int __init max17040_init(void)
{
return i2c_add_driver(&max17040_i2c_driver);//注册IIC设备
}
static enum power_supply_property max17040_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
};
static int __devinit max17040_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct max17040_chip *chip;
int ret;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO;
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->client = client;
chip->pdata = client->dev.platform_data;
i2c_set_clientdata(client, chip);
chip->battery.name = "battery";//电源名称
chip->battery.type = POWER_SUPPLY_TYPE_BATTERY; //属性 :电池
chip->battery.get_property = max17040_get_property; //注册函数get_property
chip->battery.properties = max17040_battery_props; //注册更新电池状态列表
chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props);//状态个数
ret = power_supply_register(&client->dev, &chip->battery);
if (ret) {
dev_err(&client->dev, "failed: power supply register
");
kfree(chip);
return ret;
}
max17040_reset(client);
max17040_get_version(client);
INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);//注册延时工作
schedule_delayed_work(&chip->work, MAX17040_DELAY); //调度延时工作
return 0;
}
static void max17040_work(struct work_struct *work)
{
struct max17040_chip *chip;
int old_usb_online, old_online, old_vcell, old_soc;
chip = container_of(work, struct max17040_chip, work.work);
#ifdef MAX17040_SUPPORT_CURVE
/* The module need to be update per hour (60*60)/3 = 1200 */
if (g_TimeCount >= 1200) {
handle_model(0);
g_TimeCount = 0;
}
g_TimeCount++;
#endif
old_online = chip->online;//(1)、保存老的电池信息,如电量、AC、USB是否插入;
old_usb_online = chip->usb_online;
old_vcell = chip->vcell;
old_soc = chip->soc;
max17040_get_online(chip->client);//(2)、读取电池新的状态信息
max17040_get_vcell(chip->client);
max17040_get_soc(chip->client);
max17040_get_status(chip->client);
if ((old_vcell != chip->vcell) || (old_soc != chip->soc)) {//(3)、如果电压电量有变化,就上报系统;
/* printk(KERN_DEBUG "power_supply_changed for battery
"); */
power_supply_changed(&chip->battery);
}
#if !defined(CONFIG_CHARGER_PM2301)//(4)、如果用PM2301充电IC,USB充电功能不用;
if (old_usb_online != chip->usb_online) {
/* printk(KERN_DEBUG "power_supply_changed for usb
"); */
power_supply_changed(&chip->usb);
}
#endif
if (old_online != chip->online) {//(5)、如果有DC插入,则更新充电状态;
/* printk(KERN_DEBUG "power_supply_changed for AC
"); */
power_supply_changed(&chip->ac);
}
schedule_delayed_work(&chip->work, MAX17040_DELAY);
}
static void max17040_get_vcell(struct i2c_client *client)
{
struct max17040_chip *chip = i2c_get_clientdata(client);
u8 msb;
u8 lsb;
msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
chip->vcell = (msb << 4) + (lsb >> 4);
}
static int max17040_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct max17040_chip *chip = container_of(psy,
struct max17040_chip, battery);
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = chip->status;
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = chip->online;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = chip->vcell;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = chip->soc;
break;
default:
return -EINVAL;
}
return 0;
}