关于电量计上报信息过程——底层驱动篇

时间:2021-02-09 16:34:01

我们都知道手机上显示电池的电量,温度,电压,当前健康状态都是通过底层的电量计具体实现的,但是他又是怎么上报给上层最终显示给用户的呢。

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 */