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