分享

power

 昵称35587114 2016-08-07

现在的手机平板供电系统变得比以前的嵌入式设备复杂了,要考虑USB、AC和battery的供电,同时USB和AC还要充电,这一系列功能一般由电源管理芯片完成。Android设备使用的供电系统使用的是sys文件系统的固定位置,对应的硬件抽象层是android标准的直接接口,内核中使用的是power_supply框架,对芯片驱动填充好power_supply结构体,再进行注册即可。而我们在开发android设备初期的时候,充电和电量计给我们带来不小的麻烦,常见麻烦是:I2C通信失败(I2C总线上设备上电逻辑问题),电量显示不准(I2C读写函数处理问题),充电电流电压设置等。


概况

下面先给给感性认识的图:下图是从网上看到的结构图,此处我们重点在驱动层。


下图是华为mate7供电系统类型:


下图是华为mate7电池系统属性和uevent上报的值:



问:android怎么知道当前是什么供电,充电中与否?

答:uevent机制(实质是net_link方式的socket)(广泛应用于hotplug),充电插入与断开时,内核通过发送uevent信息,告诉android。

问:android如何知道各种参数并更新的?

答:通过kobject_uevent发送通知给上层,上层读取sys相关文件属性


驱动框架

power_supply驱动头文件#include <linux/power_supply.h>

框架核心文件:

  1. ccflags-$(CONFIG_POWER_SUPPLY_DEBUG) := -DDEBUG  
  2.   
  3. power_supply-y              := power_supply_core.o  
  4. power_supply-$(CONFIG_SYSFS)        += power_supply_sysfs.o  
  5. power_supply-$(CONFIG_LEDS_TRIGGERS)    += power_supply_leds.o  
  6.   
  7. obj-$(CONFIG_POWER_SUPPLY)  += power_supply.o  

由Makefile可见,power_supply.o目标是由3个核心文件组成。

power_supply_core.c主要提供psy的注册/注销,psy基本属性的设置(调用的是注册时传入的函数指针回调进行设置),还有供电改变power_supply_changed发送kobject_uevent给android的HAL层。

power_supply_sysfs.c顾名思义就是负责sysfs文件系统下的属性文件,主要负责,power_supply下的各种属性结点的show和store,还有填充uevent的信息。

power_supply_leds.c提供了充电,点亮满等trigger的注册,还有根据电池不同状态和容量时,执行LED动作。


struct power_supply是注册LED设备的基本数据结构,在注册的时候,需要适当填充好该结构体成员。

  1. struct power_supply {  
  2.     const char *name;   //LED名称  
  3.     enum power_supply_type type;    //供电类型,USB,battery,适配器?  
  4.     enum power_supply_property *properties; //该供电支持的属性数组  
  5.     size_t num_properties;  //该供电属性个数  
  6.   
  7.     char **supplied_to;     //可对什么供电,对系统还是OTG对外部设备  
  8.     size_t num_supplicants; //  
  9.     /* psy的num_properties个属性的获取/设置 */  
  10.     int (*get_property)(struct power_supply *psy,  
  11.                 enum power_supply_property psp,  
  12.                 union power_supply_propval *val);  
  13.     int (*set_property)(struct power_supply *psy,  
  14.                 enum power_supply_property psp,  
  15.                 const union power_supply_propval *val);  
  16.     int (*property_is_writeable)(struct power_supply *psy,  //属性psp是否可写,影响是否mode |= S_IWUSR,可选  
  17.                      enum power_supply_property psp);  
  18.     void (*external_power_changed)(struct power_supply *psy);//供电改变的回调函数,可选  
  19.     void (*set_charged)(struct power_supply *psy);  
  20.   
  21.     /* For APM emulation, think legacy userspace. */  
  22.     int use_for_apm;  
  23.   
  24.     /* private */  
  25.     struct device *dev;  
  26.     struct work_struct changed_work;  
  27.     spinlock_t changed_lock;  
  28.     bool changed;  
  29.     struct wake_lock work_wake_lock;  
  30.     /* 充电中,充满等状态关联的LED */  
  31. #ifdef CONFIG_LEDS_TRIGGERS  
  32.     struct led_trigger *charging_full_trig;  
  33.     char *charging_full_trig_name;  
  34.     struct led_trigger *charging_trig;  
  35.     char *charging_trig_name;  
  36.     struct led_trigger *full_trig;  
  37.     char *full_trig_name;  
  38.     struct led_trigger *online_trig;  
  39.     char *online_trig_name;  
  40.     struct led_trigger *charging_blink_full_solid_trig;  
  41.     char *charging_blink_full_solid_trig_name;  
  42. #endif  
  43. };  

该结构体中提供几个properties,你的get_property函数就得提供几个对应的get操作值,set_property函数一般用的不多(如用的话,property_is_writeable函数需要提供对应属性需要能return 1);

external_power_changed函数指针会在power_supply_changed被外部调用时执行;

set_charged函数指针会在供电为电池类型驱动中调用power_supply_set_battery_charged时执行;


至于其中的led部分要加入config选项,

使用LED的trigger,你的LED设备也需要注册,详见我的LED子系统文章。当你注册psy时,power_supply_register会去调用power_supply_create_triggers去创建trigger(battery类型的psy创建"%s-charging-or-full"(%s代表psy->name下同),"%s-charging","%s-full","%s-charging-blink-full-solid"四个trigger;而其他类型的psy只建立"%s-online"一个trigger),之后你在注册psy设备时,

在调用void power_supply_update_leds(struct power_supply *psy)时会触发去执行关联的LED设备。


以现在开发的平板充电为例:充电芯片bq24296,通过USB和AC适配器(USB-WALL)充电。

  1. static int bq2429x_usb_get_property(struct power_supply *psy,  
  2.                 enum power_supply_property psp,  
  3.                 union power_supply_propval *val)  
  4. {  
  5.     struct bq2429x *bq = container_of(psy, struct bq2429x, usb);  
  6.   
  7.     switch (psp) {  
  8.     case POWER_SUPPLY_PROP_ONLINE:  
  9.         if (bq2429x_get_vbus_type(bq) == BQ2429X_VBUS_USB_CHARGER)  
  10.             val->intval = 1;  
  11.         else  
  12.             val->intval = 0;  
  13.         break;  
  14.     case POWER_SUPPLY_PROP_CHARGE_TYPE:  
  15.         val->intval = bq2429x_charge_type(bq);  
  16.         break;  
  17.     default:  
  18.         return -EINVAL;  
  19.     }  
  20.   
  21.     return 0;  
  22. }  
  23.   
  24. static enum power_supply_property bq2429x_charger_props[] = {  
  25.     POWER_SUPPLY_PROP_ONLINE,   //外充电,哪个在线,cat属性值在线为1,否则0  
  26.     POWER_SUPPLY_PROP_CHARGE_TYPE,//充电类型,USB还是WALL  
  27. };  
  28.   
  29. static int bq2429x_psy_register(struct bq2429x *bq)  
  30. {  
  31.     int ret;  
  32.   
  33.     bq->usb.name = "bq2429x-usb";    //在power_supply下显示的名字  
  34.     bq->usb.type = POWER_SUPPLY_TYPE_USB;//该充电类型  
  35.     bq->usb.properties = bq2429x_charger_props;//该供电支持的属性  
  36.     bq->usb.num_properties = ARRAY_SIZE(bq2429x_charger_props);ni   
  37.     bq->usb.get_property = bq2429x_usb_get_property;//获取属性值  
  38.     bq->usb.external_power_changed = NULL;  
  39.   
  40.     ret = power_supply_register(bq->dev, &bq->usb);  
  41.     if (ret) {  
  42.         dev_err(bq->dev, "failed to register usb: %d\n", ret);  
  43.         return ret;  
  44.     }  
  45.   
  46.     bq->wall.name = "bq2429x-wall";  
  47.     bq->wall.type = POWER_SUPPLY_TYPE_MAINS;  
  48.     bq->wall.properties = bq2429x_charger_props;  
  49.     bq->wall.num_properties = ARRAY_SIZE(bq2429x_charger_props);  
  50.     bq->wall.get_property = bq2429x_wall_get_property;  
  51.     bq->wall.external_power_changed = NULL;  
  52.   
  53.     ret = power_supply_register(bq->dev, &bq->wall);  
  54.     if (ret) {  
  55.         dev_err(bq->dev, "failed to register wall: %d\n", ret);  
  56.         goto err_psy_wall;  
  57.     }  
  58.   
  59.     return 0;  
  60.   
  61. err_psy_wall:  
  62.     power_supply_unregister(&bq->usb);  
  63.     return ret;  
  64. }  

两种供电注册方式如上,至于如何设置充电输入输出的电压电流大小,一般应芯片而异,此处bq24296充电芯片会根据插入的供电类型,发送一个中断给CPU,CPU再去读取充电芯片的对应寄存器判断充电类型,再去设置充电参数,这个一般在插入充电器线的时候就完成了。这段代码之后,在机器中显示效果如下:


平板的电池电量为例:bq27541为电池电量管理芯片,android手机电池状态信息如电量电压都是来自这的。

  1. static enum power_supply_property msm_bq_power_props[] = {  
  2.     POWER_SUPPLY_PROP_CAPACITY,  
  3.     POWER_SUPPLY_PROP_VOLTAGE_NOW,  
  4. };  
  5.   
  6. static int bq_power_get_property(struct power_supply *psy,  
  7.                     enum power_supply_property psp,  
  8.                     union power_supply_propval *val)  
  9. {  
  10.     switch (psp) {  
  11.     case POWER_SUPPLY_PROP_CAPACITY:    //电量百分比  
  12.         val->intval = bq27541_get_battery_capacity();  
  13.         break;  
  14.     case POWER_SUPPLY_PROP_VOLTAGE_NOW: //电压,单位uV  
  15.         val->intval = bq27541_get_battery_mvolts();  
  16.         break;  
  17.   
  18.     default:  
  19.         return -EINVAL;  
  20.     }  
  21.     return 0;  
  22. }  
  23.   
  24. static int __devinit bq27541_register_psy(struct bq27541_device_info *bq27541_dev)  
  25. {  
  26.     int retval;  
  27.   
  28.     /* setup & register the battery power supply */  
  29.     bq27541_dev->bq_psy.name = "bq";  
  30.     bq27541_dev->bq_psy.type = POWER_SUPPLY_TYPE_BATTERY;    //供电类型battery  
  31.     bq27541_dev->bq_psy.num_supplicants = 0;  
  32.     bq27541_dev->bq_psy.properties = msm_bq_power_props;  
  33.     bq27541_dev->bq_psy.num_properties = ARRAY_SIZE(msm_bq_power_props);  
  34.     bq27541_dev->bq_psy.get_property = bq_power_get_property;  
  35.   
  36.     retval =  power_supply_register(&bq27541_dev->client->dev, &bq27541_dev->bq_psy);  
  37.   
  38.     if (retval < 0) {  
  39.         pr_err("power_supply_register bq failed rc = %d\n", retval);  
  40.         goto unregister_bq;  
  41.     }  
  42.   
  43.     return 0;  
  44.   
  45. unregister_bq:  
  46.     power_supply_unregister(&bq27541_dev->bq_psy);  
  47.   
  48.     return retval;  
  49. }  

以上代码是注册电池供电设备,此处只支持上报电池容量和电池电压,在系统中显示如下:


不管是充电还是电池,android都是通过属性的读写来完成的,它会扫描/sys/class/power_supply下的所有文件,并按照type值来分类为USB,Battery还是MAINS等,这样android就可以直接读取电池相关信息。但是如果有2个电池power_supply我还没看过上层是以谁为准,第一个么?,有空再看看。


那么USB插上,拔下或者状态的改变到底是如何让android知道的呢?

前面提到了用uevent的确是这样。power_supply_core.c提供了函数void power_supply_changed(struct power_supply *psy),这个函数调用后,就会把当前psy属性的所有值发送给android。

那么对于不同的psy发送的附加内容什么,谁填充的,填充的格式是什么?

在power_supply_sysfs.c中,power_supply_uevent函数负责填充,填充格式由其下函数add_uevent_var完成,当你调用power_supply_changed,就会把你当前psy的所有属性值发送给android的HAL,uevent相关函数代码片如下:

  1. //以下2个函数是power_supply_core.c中的函数  
  2. static int __init power_supply_class_init(void)  
  3. {  
  4.     power_supply_class = class_create(THIS_MODULE, "power_supply");  
  5.   
  6.     if (IS_ERR(power_supply_class))  
  7.         return PTR_ERR(power_supply_class);  
  8.   
  9.     power_supply_class->dev_uevent = power_supply_uevent;//设备填充env的函数指针  
  10.     power_supply_init_attrs(&power_supply_dev_type);  
  11.   
  12.     return 0;  
  13. }  
  14.     //供电状态改变的work  
  15. static void power_supply_changed_work(struct work_struct *work)  
  16. {  
  17.     unsigned long flags;  
  18.     struct power_supply *psy = container_of(work, struct power_supply,  
  19.                         changed_work);  
  20.   
  21.     dev_dbg(psy->dev, "%s\n", __func__);  
  22.   
  23.     spin_lock_irqsave(&psy->changed_lock, flags);  
  24.     if (psy->changed) {  
  25.         psy->changed = false;  
  26.         spin_unlock_irqrestore(&psy->changed_lock, flags);  
  27.   
  28.         class_for_each_device(power_supply_class, NULL, psy,  
  29.                       __power_supply_changed_work);  
  30.   
  31.         power_supply_update_leds(psy);  
  32.   
  33.         kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);//发送uevent给android的HAL层  
  34.         spin_lock_irqsave(&psy->changed_lock, flags);  
  35.     }  
  36.     if (!psy->changed)  
  37.         wake_unlock(&psy->work_wake_lock);  
  38.     spin_unlock_irqrestore(&psy->changed_lock, flags);  
  39. }  
  40.   
  41.   
  42. //power_supply_sysfs.c中的uevent填充env函数  
  43. int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)  
  44. {  
  45.     struct power_supply *psy = dev_get_drvdata(dev);  
  46.     int ret = 0, j;  
  47.     char *prop_buf;  
  48.     char *attrname;  
  49.   
  50.     dev_dbg(dev, "uevent\n");  
  51.   
  52.     if (!psy || !psy->dev) {  
  53.         dev_dbg(dev, "No power supply yet\n");  
  54.         return ret;  
  55.     }  
  56.   
  57.     dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);  
  58.   
  59.     ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);    //例如 POWER_SUPPLY_NAME=bq2429x-usb  
  60.     if (ret)  
  61.         return ret;  
  62.   
  63.     prop_buf = (char *)get_zeroed_page(GFP_KERNEL);  
  64.     if (!prop_buf)  
  65.         return -ENOMEM;  
  66.   
  67.     for (j = 0; j < psy->num_properties; j++) {  
  68.         struct device_attribute *attr;  
  69.         char *line;  
  70.   
  71.         attr = &power_supply_attrs[psy->properties[j]];  
  72.   
  73.         ret = power_supply_show_property(dev, attr, prop_buf);  
  74.         if (ret == -ENODEV || ret == -ENODATA) {  
  75.             /* When a battery is absent, we expect -ENODEV. Don't abort; 
  76.                send the uevent with at least the the PRESENT=0 property */  
  77.             ret = 0;  
  78.             continue;  
  79.         }  
  80.   
  81.         if (ret < 0)  
  82.             goto out;  
  83.   
  84.         line = strchr(prop_buf, '\n');  
  85.         if (line)  
  86.             *line = 0;  
  87.   
  88.         attrname = kstruprdup(attr->attr.name, GFP_KERNEL);  
  89.         if (!attrname) {  
  90.             ret = -ENOMEM;  
  91.             goto out;  
  92.         }  
  93.   
  94.         dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);  
  95.         /* 下面根据属性个数,env可能填充的例如 
  96.             POWER_SUPPLY_ONLINE=1 
  97.             POWER_SUPPLY_CHARGE_TYPE=Fast 
  98.         */  
  99.         ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);  
  100.         kfree(attrname);  
  101.         if (ret)  
  102.             goto out;  
  103.     }  
  104.   
  105. out:  
  106.     free_page((unsigned long)prop_buf);  
  107.   
  108.     return ret;  
  109. }  

power_supply发送的uevent都是KOBJ_CHANGE,其它定义如下:

static const char *kobject_actions[] = {
       [KOBJ_ADD] =            "add",
       [KOBJ_REMOVE] =           "remove",
       [KOBJ_CHANGE] =            "change",
       [KOBJ_MOVE] =         "move",
       [KOBJ_ONLINE] =             "online",
       [KOBJ_OFFLINE] =     "offline",
};





    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多