關於電量計上報信息過程——底層驅動篇

咱們都知道手機上顯示電池的電量,溫度,電壓,當前健康狀態都是經過底層的電量計具體實現的,可是他又是怎麼上報給上層最終顯示給用戶的呢。linux

一、瞭解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 };

二、power_supply_register()註冊該power_supply:spa

真正的想要在sys/class/power_supply目錄下添加一個新的device,咱們能夠直接使用linux內核爲咱們已經建立好的接口。rest

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目錄下添加了一個新的設備接口;接下來就是如何使用該接口爲咱們作事情了。code

在226行,咱們就知道本身要幹什麼了,對就是power_supply_changed,他的工做相當重要,沒有它上層就不知道何時去底層獲取電池當前的實時信息。blog

先貼下它的源碼吧.接口

 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 */  
相關文章
相關標籤/搜索