驅(qū)動書寫指南系列會提供另一個角度的驅(qū)動分析,linux內(nèi)核把各驅(qū)動共同的部分抽象出來,做在一起稱為框架。就比如說文件系統(tǒng),linux內(nèi)核定義好了文件系統(tǒng)中最通用的打開文件、讀寫文件等公共接口,但是并沒有實(shí)現(xiàn)函數(shù)。這些定義好的接口,可以認(rèn)為是框架。等到了真正的文件系統(tǒng)實(shí)現(xiàn)的時候 ,才會填充這些open、read等函數(shù)。對于實(shí)現(xiàn)文件系統(tǒng)的程序員來說,就是填充框架外的其他內(nèi)容,一般都是和硬件相關(guān)性比較大。
power supply core介紹
在本文中,主要介紹怎么注冊自己的ec驅(qū)動。ec驅(qū)動的框架部分,power supply都是實(shí)現(xiàn)過了。
在上文的 介紹里補(bǔ)充一點(diǎn)內(nèi)容,power supply 硬件屬性分別是什么意思,在寫ec驅(qū)動的途中,一大部分時間花在研究這幾個屬性分別是描述什么的以及我需要什么屬性,大部分還是蝸窩科技寫的對幾個屬性的解釋,能找到的中文資料非常非常少,還是挺值得記錄一下的。
enum power_supply_property {
/* Properties of type `int' */
POWER_SUPPLY_PROP_STATUS = 0,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_AUTHENTIC,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CYCLE_COUNT,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_VOLTAGE_OCV,
POWER_SUPPLY_PROP_VOLTAGE_BOOT,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CURRENT_BOOT,
POWER_SUPPLY_PROP_POWER_NOW,
POWER_SUPPLY_PROP_POWER_AVG,
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_EMPTY,
POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_CHARGE_AVG,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
POWER_SUPPLY_PROP_ENERGY_FULL,
POWER_SUPPLY_PROP_ENERGY_EMPTY,
POWER_SUPPLY_PROP_ENERGY_NOW,
POWER_SUPPLY_PROP_ENERGY_AVG,
POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_MAX,
POWER_SUPPLY_PROP_TEMP_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
POWER_SUPPLY_PROP_TEMP_AMBIENT,
POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN,
POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_SCOPE,
POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
POWER_SUPPLY_PROP_CALIBRATE,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
POWER_SUPPLY_PROP_SERIAL_NUMBER,
};
- status:表示電池狀態(tài),充電中、放電、未充電和電池電量已滿
- charge type:表示充電類型,快充、點(diǎn)滴式充電器(trikle charge)
- health:表示電池健康屬性,主要用來表示電池健康、過熱、損壞、過壓等
- present:表示電池或者適配器是否存在
- online:表示電池或者適配器是否在線,有時候可能電池存在但是并沒有接線。有的ec芯片這兩個狀態(tài)都是可以讀到的。
- authentic:真實(shí)參數(shù),暫時在內(nèi)核里沒發(fā)現(xiàn)按個ec驅(qū)動使用了這個屬性。這個屬性是為了給用戶看,移動設(shè)備的適配器 或者電池是不是非標(biāo)準(zhǔn)、不合格的。
- technology:表示電池采用的技術(shù),lion(鋰離子電池)、nimh(鎳氫電池)、lipo(鋰聚合物電池)、nicd(鎳鉻電池)、limn(錳酸鋰電池)
- voltage:電壓參數(shù),框架定義了最大、最小、設(shè)計(jì)最大、設(shè)計(jì)最小等,框架定義的單位是μV
- current:電流參數(shù),框架定義了最大、目前和平均等,放電為負(fù)值,單位是μA,
- power:電流參數(shù),和current不同的是,這個電流參數(shù)是智能電池導(dǎo)出的。智能電池使用功率作為工作單位和充電放電測量單位,所以不適合再導(dǎo)出在current屬性里了。
- charge:容量參數(shù),框架定義了設(shè)計(jì)滿、設(shè)計(jì)空、充滿、空等等,框架定義的很多設(shè)計(jì)值都是用來計(jì)算電池的健康屬性的,比如說設(shè)計(jì)滿容量是3000,但是目前充滿只有1500了,那就說明電池的健康系數(shù)是50%。只是舉個例子,實(shí)際上電池健康系數(shù)不可能用僅僅一個屬性來計(jì)算,單位是μAh
- energy:功率參數(shù),用來計(jì)算功耗,單位是μWh
- capacity:容量百分比,常被桌面用來顯示電池圖標(biāo)選中后,出現(xiàn)的提示框里顯示電池容量多少。
- temp:溫度參數(shù),注意這個問題是電池溫度,并不是cpu溫度,單位是攝氏度
- time:時間參數(shù),充滿時間、放空時間等等,單位是s
最后還有幾個字符串類型的屬性,模塊名稱、生產(chǎn)商和序列號。
實(shí)現(xiàn)自己的ec驅(qū)動
定義psy設(shè)備
第一步是明確設(shè)備的充電類型,框架定義的充電類型有:
enum power_supply_type {
POWER_SUPPLY_TYPE_UNKNOWN = 0,
POWER_SUPPLY_TYPE_BATTERY,
POWER_SUPPLY_TYPE_UPS,
POWER_SUPPLY_TYPE_MAINS,
POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */
POWER_SUPPLY_TYPE_USB_DCP, /* Dedicated Charging Port */
POWER_SUPPLY_TYPE_USB_CDP, /* Charging Downstream Port */
POWER_SUPPLY_TYPE_USB_ACA, /* Accessory Charger Adapters */
POWER_SUPPLY_TYPE_USB_TYPE_C, /* Type C Port */
POWER_SUPPLY_TYPE_USB_PD, /* Power Delivery Port */
POWER_SUPPLY_TYPE_USB_PD_DRP, /* PD Dual Role Port */
POWER_SUPPLY_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */
};
一般來講筆記本都是這兩個充電類型:mains類型和battery類型,移動設(shè)備需要定義usb的充電類型。先定義兩個最常見的psy設(shè)備:適配器和電池,適配器的充電類型是mains,電池是battery。
static const struct power_supply_desc shiwen_ac_desc = {
.name = "shiwen_ac",
.type = POWER_SUPPLY_TYPE_MAINS,
.properties = shiwen_power_ac_props,
.num_properties = ARRAY_SIZE(shiwen_power_ac_props),
.get_property = shiwen_power_get_ac_property,
};
static const struct power_supply_desc shiwen_bat_desc = {
.name = "shiwen_battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = shiwen_power_battery_props,
.num_properties = ARRAY_SIZE(shiwen_power_battery_props),
.get_property = shiwen_power_get_battery_property,
};
選擇psy設(shè)備屬性
定義好之后,再選擇每個psy設(shè)備的硬件屬性,這部分要看ec芯片提供了什么數(shù)據(jù)然后選擇定義什么屬性。上一節(jié)提到的屬性是框架定義的全部屬性,ec芯片不可能提供這全部的數(shù)據(jù)。這里寫的示例,就簡單選擇幾個上層需要的屬性吧。適配器就一個是否在線屬性,電池桌面需要的屬性有電池存在標(biāo)志、充滿時間、放空時間、電池狀態(tài)、電池容量百分比、電池容量等級、模塊名、制造商。
static enum power_supply_property shiwen_power_ac_props[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static enum power_supply_property shiwen_power_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
POWER_SUPPLY_PROP_TIME_TO_EMPTY,
POWER_SUPPLY_PROP_TIME_TO_FULL,
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
};
填充獲取psy屬性的方法
接著要告訴內(nèi)核如何獲取前面定義的psy屬性,psy core留了一個get_property接口,需要驅(qū)動工程師把方法填充在接口里。這部分就完全和硬件相關(guān)了,不同的ec芯片,讀取psy屬性方法不同,比如說本文的示例代碼里僅僅實(shí)現(xiàn)了一個固定值。
struct int shiwen_power_get_battery_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
case POWER_SUPPLY_PROP_ONLINE:
val- >intval = 1; //always assume ac online
break;
default:
val- >intval = -1;
pr_err("property error
");
}
struct int shiwen_power_get_battery_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val- >strval = "Shiwen example driver";
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val- >strval = "Shiwen example";
break;
case POWER_SUPPLY_PROP_STATUS:
val- >intval = Charging; //always assume battery charging
break;
case POWER_SUPPLY_PROP_PRESENT:
val- >intval = 1; //always assume battery present
break;
case POWER_SUPPLY_PROP_CAPACITY:
val- >intval = 100; //always assume battery capacity 100%
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
val- >intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY:
val- >intval = 7200; //always assume battery need 2 hours to empty
break;
case POWER_SUPPLY_PROP_TIME_TO_FULL:
val- >intval = 0;
break;
default:
val- >intval =-1;
pr_err("property error
");
break;
}
到這一步,兩個psy設(shè)備定義完成了。接下來只需要把注冊設(shè)備,在sys下就能看到接口了。
注冊設(shè)備
設(shè)備注冊,psy core提供了接口,只需要調(diào)用接口并做好錯誤處理即可。
static int __init shiwen_power_init(void)
{
shiwen_ac = power_supply_register(NULL, &shiwen_ac_desc, NULL);
if (IS_ERR(shiwen_ac)) {
pr_err("%s: failed to register
",__func__, shiwen_ac_desc.name);
ret = PTR_ERR(shiwen_ac);
goto failed_ac;
}
shiwen_bat = power_supply_register(NULL, &shiwen_bat_desc, NULL);
if (IS_ERR(shiwen_ac)) {
pr_err("%s: failed to register %s
", __func__, shiwen_bat_desc.name);
ret = PTR_ERR(shiwen_bat);
goto failed_bat;
}
return 0;
failed_bat:
power_supply_unregister(shiwen_bat);
failed_ac:
power_supply_unregister(shiwen_ac);
return ret;
}
查看結(jié)果
到這里,非常簡單的ec示例驅(qū)動注冊就完成了,在sys下查看一下是否正確注冊了設(shè)備,并且獲取到正確的內(nèi)容。
deepin@deepin-PC:~$ ls /sys/class/power_supply/
shiwen_ac shiwen_battery
deepin@deepin-PC:~$
deepin@deepin-PC:~$ ls /sys/class/power_supply/shiwen_ac/
online power subsystem type uevent
deepin@deepin-PC:~$ cat /sys/class/power_supply/shiwen_ac/uevent
POWER_SUPPLY_NAME=shiwen_ac
POWER_SUPPLY_ONLINE=1
deepin@deepin-PC:~$ ls /sys/class/power_supply/shiwen_battery
capacity capacity_level manufacturer mcu_time_effect model_name power present status subsystem time_to_empty_avg time_to_full_avg type uevent
deepin@deepin-PC:~$ cat /sys/class/power_supply/shiwen_battery/uevent
POWER_SUPPLY_NAME=shiwen_battery
POWER_SUPPLY_STATUS=Charging
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_CAPACITY=100
POWER_SUPPLY_CAPACITY_LEVEL=POWER_SUPPLY_CAPACITY_LEVEL_FULL
POWER_SUPPLY_TIME_TO_EMPTY_AVG=7200
POWER_SUPPLY_TIME_TO_FULL_AVG=0
POWER_SUPPLY_MODEL_NAME=Shiwen example driver
POWER_SUPPLY_MANUFACTURER=Shiwen example
到這,就大功告成啦。sys下文件創(chuàng)建、sys下文件的讀寫都有sysfs框架和psy core框架幫我們實(shí)現(xiàn)。上文是一個非常非常簡單的psy驅(qū)動實(shí)現(xiàn)流程,ec狀態(tài)的改變驅(qū)動并沒有關(guān)注,都是依賴上層daemon或者讀取 sys下的文件來感知,對于用戶來說相當(dāng)于是輪詢讀取。實(shí)際上日常我們遇到的ec芯片都沒有這么原始,ec事件基本上都是中斷通知,所以一個完成的ec驅(qū)動還要在上面的流程里面加上中斷的注冊和處理代碼。
增加中斷處理
關(guān)注過我之前寫過兩個中斷介紹文章的童鞋肯定知道驅(qū)動注冊中斷的第一步,肯定是實(shí)現(xiàn)中斷處理函數(shù)。處理函數(shù)也和硬件有關(guān)系,有的ec芯片ac插拔事件和電池事件的中斷是分開的,假設(shè)我們虛構(gòu)的ec芯片只有ac和電池的插拔才能觸發(fā)中斷(看內(nèi)核psy實(shí)現(xiàn)的芯片,這樣的中斷也是最常見的)。假設(shè)ac事件和電池事件共享155號中斷(隨意選的,沒有任何理論依據(jù)),那么中斷處理函數(shù)處理第一步肯定是確定中斷源。接著根據(jù)中斷源,更新一下設(shè)備狀態(tài)即可。
static irqreturn_t shiwen_ec_interrupt(int irq, void *dev_id)
{
int status;
int source;
source = SHIWEN_READ_INTSOURCE_REG();
if (source = SHIWEN_BATTERY)
power_supply_changed(shiwen_battery);
else if(source = SHIWEN_AC)
power_supply_changed(shiwen_ac);
else
//there are no psy event
return IRQ_NONE;
return IRQ_HANDLED;
}
更新整個psy設(shè)備狀態(tài)的函數(shù)是psy core框架提供的,很好用吧。psy會根據(jù)參數(shù)傳進(jìn)去的設(shè)備,調(diào)用獲取屬性函數(shù),更新psy設(shè)備屬性。這個處理函數(shù)非常簡單,因?yàn)閑c芯片的中斷就很簡單。如果是比較復(fù)雜的ec芯片的話,可能中斷源分的比較多,比如說電壓變化、容量變化、電流變化等等都會有一種中斷的產(chǎn)生。相對的,中斷處理函數(shù)就不能簡單的使用power_supply_changed更新整個psy的狀態(tài)了,需要自己實(shí)現(xiàn)一個。注意:為了避免讀到的狀態(tài)有問題,需要在中斷產(chǎn)生后等一段時間再去讀ec狀態(tài),所以中斷處理函數(shù)一般都需要有延時讀取ec狀態(tài)。power_supply_changed函數(shù)內(nèi)部實(shí)現(xiàn)是采用work queue方式讀取狀態(tài)的,我們自己實(shí)現(xiàn)的話,為了簡單且保險,可以用delay work。假設(shè)說,需要實(shí)現(xiàn)一個電壓變化中斷的處理函數(shù):
static irqreturn_t shiwen_voltage_interrupt(int irq, void *dev_id)
{
int status;
schedule_delayed_work(&ec_work, JIFFIES_NUM);
return IRQ_HANDLED;
}
static void ec_work_func(struct work_struct *work)
{
shiwen_update_voltage();
}
在 中斷處理函數(shù)里面,僅有調(diào)用delay work內(nèi)容,延時過了之后函數(shù)會被加載工作隊(duì)列里,借此達(dá)到延時的目的。接下來指定一下延時工作進(jìn)程需要執(zhí)行的函數(shù),這是工作進(jìn)程要求的。這就是一個基本的讀取某一個屬性的中斷函數(shù)實(shí)現(xiàn),沒實(shí)現(xiàn)真正電壓讀取是因?yàn)?,電壓讀取是真正和硬件相關(guān)的,每款芯片都不一樣,我也實(shí)在是虛構(gòu)不出來了orz…
處理函數(shù)寫完了之后,中斷注冊到內(nèi)核里就可以使用啦。剩下的中斷觸發(fā)是設(shè)備的事,中斷觸發(fā)之后的感知是cpu的事,感知到中斷之后在調(diào)到處理函數(shù)之前是內(nèi)核中斷子系統(tǒng)的事,前面中斷的兩篇文章介紹過內(nèi)核已經(jīng)做好了,驅(qū)動并不關(guān)心。中斷的注冊一般放在psy設(shè)備注冊之后,修改過的psy設(shè)備注冊代碼如下:
static int __init shiwen_power_init(void)
{
shiwen_ac = power_supply_register(NULL, &shiwen_ac_desc, NULL);
......
shiwen_bat = power_supply_register(NULL, &shiwen_bat_desc, NULL);
......
//shiwen_ac and shiwen_battery called shiwen_battery_ac
ret =request_irq(155, shiwen_ec_interrupt, IRQF_SHARED, shiwen_battery_ac);
INIT_DELAYED_WORK(&ec_work, ec_work_func);
return 0;
......
}
增加了中斷注冊個delay work初始化的代碼(如果需要自己使用delay work讀取某一個屬性的時候),代碼寫到這里,一個帶基本功能的ec驅(qū)動就做完了??窗桑€是很簡單的吧。
-
鋰離子電池
+關(guān)注
關(guān)注
85文章
3205瀏覽量
77479 -
充電器
+關(guān)注
關(guān)注
100文章
4056瀏覽量
114489 -
適配器
+關(guān)注
關(guān)注
8文章
1914瀏覽量
67850 -
中斷處理
+關(guān)注
關(guān)注
0文章
94瀏覽量
10947 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21608
發(fā)布評論請先 登錄
相關(guān)推薦
評論