Linux 电池 驱动

2019-07-14 03:45发布

电池驱动 涉及文件

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; }