我们都知道手机上显示电池的电量,温度,电压,当前健康状态都是通过底层的电量计具体实现的,但是他又是怎么上报给上层最终显示给用户的呢。
1、了解struct power_supply结构体:
153 struct power_supply { 154 const char *name;//创建新接口的目录名字 155 enum power_supply_type type;//这个type是显示当前是什么类型供电(USB\MAINS\BATTEY) 156 enum power_supply_property *properties;//所要显示的电池属性(电压、电流、容量、健康、制造商、cycles等) 157 size_t num_properties;(这个就是power_supply_property显示了多少个属性ARRAY_SIZE(properties)) 158 159 char **supplied_to;//这个可以理解为给谁提供电的意思吧,比如USB或AC充电时,他肯定是充给电池,所以他的supply_to属性就是“Battery”了; 160 size_t num_supplicants; 161 162 int (*get_property)(struct power_supply *psy, 163 enum power_supply_property psp, 164 union power_supply_propval *val);//获得属性 165 int (*set_property)(struct power_supply *psy, 166 enum power_supply_property psp, 167 const union power_supply_propval *val);//设置属性 168 int (*property_is_writeable)(struct power_supply *psy, 169 enum power_supply_property psp); 170 void (*external_power_changed)(struct power_supply *psy); 171 void (*set_charged)(struct power_supply *psy); 172 173 /* For APM emulation, think legacy userspace. */ 174 int use_for_apm; 175 176 /* private */ 177 struct device *dev; 178 struct work_struct changed_work; 179 spinlock_t changed_lock; 180 bool changed;//属性是否改变,当调用power_supply_changed时该位会被设置为true,这个待会介绍power_supply_changed时再详细介绍 181 182 #ifdef CONFIG_LEDS_TRIGGERS 183 struct led_trigger *charging_full_trig; 184 char *charging_full_trig_name; 185 struct led_trigger *charging_trig; 186 char *charging_trig_name; 187 struct led_trigger *full_trig; 188 char *full_trig_name; 189 struct led_trigger *online_trig; 190 char *online_trig_name; 191 struct led_trigger *charging_blink_full_solid_trig; 192 char *charging_blink_full_solid_trig_name; 193 #endif 194 };
2、power_supply_register()注册该power_supply:
真正的想要在sys/class/power_supply目录下添加一个新的device,我们可以直接使用linux内核为我们已经创建好的接口。
189 int power_supply_register(struct device *parent, struct power_supply *psy) 190 { 191 struct device *dev; 192 int rc; 193 194 dev = kzalloc(sizeof(*dev), GFP_KERNEL); 195 if (!dev) 196 return -ENOMEM; 197 198 device_initialize(dev); 199 200 dev->class = power_supply_class;//这个是一个全局类,定义在power_supply_core.c文件中,并且使用了EXPORT_SYMBOL_GPL(); 201 dev->type = &power_supply_dev_type;这个是struct device_type类型,同样也是一个静态全局变量;定义在power_supply_core.c文件中; 202 dev->parent = parent; 203 dev->release = power_supply_dev_release;//在power_supply_core.c文件中实现的一个释放内存的函数; 204 dev_set_drvdata(dev, psy); 205 psy->dev = dev; 206 207 INIT_WORK(&psy->changed_work, power_supply_changed_work);//将该中作队列实现函数加到系统共享工作队列链表中; 208 209 rc = kobject_set_name(&dev->kobj, "%s", psy->name);//创建一个以psy->name为名的kobject底层接口; 210 if (rc) 211 goto kobject_set_name_failed; 212 213 rc = device_add(dev); 214 if (rc) 215 goto device_add_failed; 216 217 spin_lock_init(&psy->changed_lock); 218 rc = device_init_wakeup(dev, true);//这个表示该设备设置为可以是wakeup device。 219 if (rc) 220 goto wakeup_init_failed; 221 222 rc = power_supply_create_triggers(psy);//这个是充电指示灯的更新接口。 223 if (rc) 224 goto create_triggers_failed; 225 226 power_supply_changed(psy);//上层上报当前注册的设备power_supply属性信息 227 228 goto success; 229 230 create_triggers_failed: 231 wakeup_init_failed: 232 device_del(dev); 233 kobject_set_name_failed: 234 device_add_failed: 235 put_device(dev); 236 success: 237 return rc; 238 } 239 EXPORT_SYMBOL_GPL(power_supply_register);
该接口一旦调用成功,表示我们已经成功在sys/class/power_supply目录下添加了一个新的设备接口;接下来就是如何使用该接口为我们做事情了。
在226行,我们就知道自己要干什么了,对就是power_supply_changed,他的工作至关重要,没有它上层就不知道什么时候去底层获取电池当前的实时信息。
先贴下它的源码吧.
68 void power_supply_changed(struct power_supply *psy) 69 { 70 unsigned long flags; 71 72 dev_dbg(psy->dev, "%s\n", __func__); 73 74 spin_lock_irqsave(&psy->changed_lock, flags); 75 psy->changed = true; 76 pm_stay_awake(psy->dev); 77 spin_unlock_irqrestore(&psy->changed_lock, flags); 78 schedule_work(&psy->changed_work); 79 } 80 EXPORT_SYMBOL_GPL(power_supply_changed);
pm_stay_awake()该接口的最终目的是创建一个以psy->name为名的wakeup唤醒锁;
然后就是调度changed_work工作队列;
changed_work工作队列,在前面有看过其实现函数如下:
42 static void power_supply_changed_work(struct work_struct *work) 43 { 44 unsigned long flags; 45 struct power_supply *psy = container_of(work, struct power_supply, 46 changed_work); 47 48 dev_dbg(psy->dev, "%s\n", __func__); 49 50 spin_lock_irqsave(&psy->changed_lock, flags); 51 if (psy->changed) {//在power_supply_changed函数中已经被赋值为true; 52 psy->changed = false; 53 spin_unlock_irqrestore(&psy->changed_lock, flags); 54 55 class_for_each_device(power_supply_class, NULL, psy, 56 __power_supply_changed_work);//找到添加到power_supply 类下面的相对应的设备 57 58 power_supply_update_leds(psy);//更新充电指示灯,该函数接口声明在drivers/power/power_supply.h中,下面我会把它粘贴出来,只有定义了CONFIG_LEDS_TRIGGERS,才会调用其实现的函数,否则,它将什么都不敢。
59 kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);//这个就是关键了,这个向上上报一个uevent事件,上层收到该事件后,就会去读取电池的相应信息。这个将上层时再讲。
61 spin_lock_irqsave(&psy->changed_lock, flags);
62 }
63 if (!psy->changed)
64 pm_relax(psy->dev); //释放wakeup唤醒锁。
65 spin_unlock_irqrestore(&psy->changed_lock, flags); 66 }
#ifdef CONFIG_LEDS_TRIGGERS 30 31 extern void power_supply_update_leds(struct power_supply *psy); 32 extern int power_supply_create_triggers(struct power_supply *psy); 33 extern void power_supply_remove_triggers(struct power_supply *psy); 34 35 #else 36 37 static inline void power_supply_update_leds(struct power_supply *psy) {} 38 static inline int power_supply_create_triggers(struct power_supply *psy) 39 { return 0; } 40 static inline void power_supply_remove_triggers(struct power_supply *psy) {} 41 42 #endif /* CONFIG_LEDS_TRIGGERS */