Led子系統(tǒng)框架
在Linux內核中,LED子系統(tǒng)扮演著控制LED燈的核心角色,它通過一套規(guī)范化的驅動架構,簡化了LED驅動程序的開發(fā)流程,讓開發(fā)者能夠更專注于功能實現而非硬件層面的復雜性。
核心架構
LED子系統(tǒng)基于一個統(tǒng)一的驅動模型,這個模型定義了一系列標準的應用程序接口(API),用以執(zhí)行LED的基本操作,如點亮、熄滅和調節(jié)閃爍頻率。此外,該模型還規(guī)定了設備樹節(jié)點的格式標準,使得硬件信息的描述變得規(guī)范化和統(tǒng)一化。
驅動類型
LED驅動程序主要分為兩大類:
- LED Class驅動:這是一種普適性的驅動實現,能夠跨多種硬件平臺工作,支持不同類型的LED。
- Platform驅動:與特定硬件平臺緊密相關,需要根據平臺特性定制開發(fā)。
開發(fā)過程
開發(fā)者在編寫LED驅動時,需要遵循LED子系統(tǒng)定義的接口規(guī)范,實現包括但不限于以下幾個關鍵函數:
- probe接口:初始化驅動,準備硬件資源。
- remove接口:卸載驅動,釋放資源。
- set_brightness接口:調整LED的亮度。
同時,硬件相關的信息,如GPIO配置和亮度調節(jié)范圍,需要在設備樹中明確描述,以便驅動程序能夠正確地識別和使用這些硬件資源。
設備樹集成
設備樹的使用,為硬件描述提供了一種結構化的方法,使得LED子系統(tǒng)的驅動程序能夠更加靈活地適應不同的硬件環(huán)境。
總結
LED子系統(tǒng)通過其標準化的驅動框架,極大地降低了LED驅動程序的開發(fā)難度,讓開發(fā)者可以更加便捷地實現LED控制功能,而無需深入了解底層硬件的具體實現。
Led子系統(tǒng)描述
Led子系統(tǒng)相關描述可在內核源碼中的文檔中查看:Documentation/leds/leds-class.txt。
led 子系統(tǒng)是一個簡單的 Linux 子系統(tǒng) ,在目錄 /sys/class/leds 下展示該子系統(tǒng)設備,每個設備都有自己的屬性:
brightness:設置 LED 亮度,范圍0~max_brightness
max_brightness:最大亮度(255或其他數字)
trigger:觸發(fā)方式,如 heartbeat、mmc0、backlight、gpio
delay_off、delay_on:trigger為timer時,LED亮滅的時間,單位ms
Led子系統(tǒng)頭文件:kernel/include/linux/leds.h
enumled_brightness{
LED_OFF=0,//全暗
LED_HALF=127,//一半亮度
LED_FULL=255,//最大亮度
};
Led子系統(tǒng)框架分析
Led子系統(tǒng)框架代碼分析
led-class.c
- led-class.c:led子系統(tǒng)框架的入口
-
維護 LED 子系統(tǒng)的所有 LED 設備,為 LED 設備提供注冊操作函數:
- led_classdev_register()
- devm_led_classdev_register()
-
注銷操作函數:
- led_classdev_unregister()
- devm_led_classdev_unregister()
-
電源管理的休眠和恢復操作函數:
- led_classdev_suspend()
- led_classdev_resume();
- 用戶態(tài)操作接口:brightness 、max_brightness
led-core.c
-
抽象出 LED 操作邏輯,封裝成函數導出,供其它文件使用:
- 核心初始化:led_init_core()
- 設置led閃爍時間:led_blink_set()
- 閃爍一次:led_blink_set_oneshot()
- led停止閃爍:led_stop_software_blink()
- 設置led的亮度:led_set_brightness()
- 更新亮度:led_update_brightness
- 用戶態(tài)關閉:led_sysfs_disable
- 用戶態(tài)打開:led_sysfs enable
- leds鏈表:leds_list
- leds鏈表鎖:leds_list_lock
led-triggers.c
-
維護 LED 子系統(tǒng)的所有觸發(fā)器,為觸發(fā)器提供注冊操作函數:
- led_trigger_register()
- devm_led_trigger_register()
- led_trigger_register_simple()
-
注銷操作函數:
- led_trigger_unregister()
- led_trigger_unregister_simple()
-
以及其它觸發(fā)器相關的操作函數
ledtrig-timer.c、ledtrig-xxx.c
- 以 leds-gpio.c 為例:
- 通過設備樹匹配到設備信息后,將調用 probe() 函數,
- 根據設備信息設置led_classdev,
- 調用 devm_led_classdev_register() 注冊 LED 設備。
Led子系統(tǒng)框架結構體分析
結構體:led_classdev
structled_classdev{
constchar*name;//名字
enumled_brightnessbrightness;//亮度
enumled_brightnessmax_brightness;//最大亮度
intflags;
/*Lower16bitsreflectstatus*/
#defineLED_SUSPENDED(1<0)
/*Upper16bitsreflectcontrolinformation*/
#defineLED_CORE_SUSPENDRESUME(1<16)
#defineLED_BLINK_ONESHOT(1<17)
#defineLED_BLINK_ONESHOT_STOP(1<18)
#defineLED_BLINK_INVERT(1<19)
#defineLED_SYSFS_DISABLE(1<20)
#defineSET_BRIGHTNESS_ASYNC(1<21)
#defineSET_BRIGHTNESS_SYNC(1<22)
#defineLED_DEV_CAP_FLASH(1<23)
//設置亮度API
void(*brightness_set)(structled_classdev*led_cdev,enumled_brightnessbrightness);
int(*brightness_set_sync)(structled_classdev*led_cdev,enumled_brightnessbrightness);
//獲取亮度API
enumled_brightness(*brightness_get)(structled_classdev*led_cdev);
//閃爍時點亮和熄滅的時間設置
int(*blink_set)(structled_classdev*led_cdev,unsignedlong*delay_on,unsignedlong*delay_off);
structdevice*dev;
conststructattribute_group**groups;
//leds-list的node
structlist_headnode;
//默認trigger的名字
constchar*default_trigger;
//閃爍的開關時間
unsignedlongblink_delay_on,blink_delay_off;
//閃爍的定時器鏈表
structtimer_listblink_timer;
//閃爍的亮度
intblink_brightness;
void(*flash_resume)(structled_classdev*led_cdev);
structwork_structset_brightness_work;
intdelayed_set_value;
#ifdefCONFIG_LEDS_TRIGGERS
//trigger的鎖
structrw_semaphoretrigger_lock;
//led的trigger
structled_trigger*trigger;
//trigger的鏈表
structlist_headtrig_list;
//trigger的數據
void*trigger_data;
boolactivated;
#endif
structmutexled_access;
};
結構體:gpio_led
structgpio_led{
constchar*name;
constchar*default_trigger;
unsignedgpio;
unsignedactive_low:1;
unsignedretain_state_suspended:1;
unsignedpanic_indicator:1;
unsigneddefault_state:2;
/*default_stateshouldbeoneofLEDS_GPIO_DEFSTATE_(ON|OFF|KEEP)*/
structgpio_desc*gpiod;
};
-
其中:
- name: led名字
- default_trigger: LED 燈在Linux 系統(tǒng)中的默認功能,比如作為系統(tǒng)心跳指示燈等等。
- default_state: 默認狀態(tài),如:
#defineLEDS_GPIO_DEFSTATE_OFF0
#defineLEDS_GPIO_DEFSTATE_ON1
#defineLEDS_GPIO_DEFSTATE_KEEP2
Led子系統(tǒng)LED_GPIO
LED_GPIO驅動使能
- 在內核源碼中進入menuconfig
->DeviceDrivers
->LEDSupport(NEW_LEDS[=y])
->LEDSupportforGPIOconnectedLEDs
可以看出,把Linux內部自帶的LED燈驅動編譯進內核以后,CONFIG_LEDS_GPIO就會等于‘y’:
Linux自帶LED_GPIO驅動
LED_GPIO燈驅動文件為/drivers/leds/leds-gpio.c,可以通過makefile文件查看:/drivers/leds/Makefile:
- leds-gpio.c驅動文件:
staticconststructof_device_idof_gpio_leds_match[]={
{.compatible="gpio-leds",},
{},
};
......
staticstructplatform_drivergpio_led_driver={
.probe=gpio_led_probe,
.remove=gpio_led_remove,
.driver={
.name="leds-gpio",
.of_match_table=of_gpio_leds_match,
},
};
module_platform_driver(gpio_led_driver);
- LED 驅動的匹配表,此表只有一個匹配項,compatible內容為“gpio-leds”,因此設備樹中的 LED 燈設備節(jié)點的 compatible 屬性值也要為“gpio-leds”,否則設備和驅動匹配不成功,驅動就沒法工作。
gpio_led_probe 函數簡析
staticintgpio_led_probe(structplatform_device*pdev)
{
structgpio_led_platform_data*pdata=dev_get_platdata(&pdev->dev);
structgpio_leds_priv*priv;
inti,ret=0;
if(pdata&&pdata->num_leds){
priv=devm_kzalloc(&pdev->dev,struct_size(priv,leds,pdata->num_leds),
GFP_KERNEL);
if(!priv)
return-ENOMEM;
priv->num_leds=pdata->num_leds;
for(i=0;inum_leds;i++){
conststructgpio_led*template=&pdata->leds[i];
structgpio_led_data*led_dat=&priv->leds[i];
if(template->gpiod)
led_dat->gpiod=template->gpiod;
else
led_dat->gpiod=
gpio_led_get_gpiod(&pdev->dev,
i,template);
if(IS_ERR(led_dat->gpiod)){
dev_info(&pdev->dev,"SkippingunavailableLEDgpio%d(%s)n",
template->gpio,template->name);
continue;
}
ret=create_gpio_led(template,led_dat,
&pdev->dev,NULL,
pdata->gpio_blink_set);
if(ret0)
returnret;
}
}else{
priv=gpio_leds_create(pdev);
if(IS_ERR(priv))
returnPTR_ERR(priv);
}
platform_set_drvdata(pdev,priv);
return0;
}
- 進入probe函數,pdata此時為空,進入gpio_leds_create:
staticstructgpio_leds_priv*gpio_leds_create(structplatform_device*pdev)
{
structdevice*dev=&pdev->dev;
structfwnode_handle*child;
structgpio_leds_priv*priv;
intcount,ret;
count=device_get_child_node_count(dev);
if(!count)
returnERR_PTR(-ENODEV);
priv=devm_kzalloc(dev,struct_size(priv,leds,count),GFP_KERNEL);
if(!priv)
returnERR_PTR(-ENOMEM);
device_for_each_child_node(dev,child){
structgpio_led_data*led_dat=&priv->leds[priv->num_leds];
structgpio_ledled={};
constchar*state=NULL;
/*
*AcquiregpiodfromDTwithuninitializedlabel,which
*willbeupdatedafterLEDclassdeviceisregistered,
*OnlythenthefinalLEDnameisknown.
*/
led.gpiod=devm_fwnode_get_gpiod_from_child(dev,NULL,child,
GPIOD_ASIS,
NULL);
if(IS_ERR(led.gpiod)){
fwnode_handle_put(child);
returnERR_CAST(led.gpiod);
}
led_dat->gpiod=led.gpiod;
if(!fwnode_property_read_string(child,"default-state",
&state)){
if(!strcmp(state,"keep"))
led.default_state=LEDS_GPIO_DEFSTATE_KEEP;
elseif(!strcmp(state,"on"))
led.default_state=LEDS_GPIO_DEFSTATE_ON;
else
led.default_state=LEDS_GPIO_DEFSTATE_OFF;
}
if(fwnode_property_present(child,"retain-state-suspended"))
led.retain_state_suspended=1;
if(fwnode_property_present(child,"retain-state-shutdown"))
led.retain_state_shutdown=1;
if(fwnode_property_present(child,"panic-indicator"))
led.panic_indicator=1;
ret=create_gpio_led(&led,led_dat,dev,child,NULL);
if(ret0){
fwnode_handle_put(child);
returnERR_PTR(ret);
}
/*SetgpiodlabeltomatchthecorrespondingLEDname.*/
gpiod_set_consumer_name(led_dat->gpiod,
led_dat->cdev.dev->kobj.name);
priv->num_leds++;
}
returnpriv;
}
- 函數gpio_leds_create描述:
- 調用device_get_child_node_count函數統(tǒng)計子節(jié)點數量,一般在在設備樹中創(chuàng)建一個節(jié)點表示LED燈,然后在這個節(jié)點下面為每個LED燈創(chuàng)建一個子節(jié)點。因此子節(jié)點數量也是LED燈的數量。
- 遍歷每個子節(jié)點,獲取每個子節(jié)點的信息:
- devm_get_gpiod_from_child獲取每個gpio燈的gpio_desc信息。
- 獲取label屬性,label作為led的名字
- 獲取“l(fā)inux,default-trigger”屬性,可以通過此屬性設置某個 LED 燈在Linux 系統(tǒng)中的默認功能,比如作為系統(tǒng)心跳指示燈等等。
- 獲取“default-state”屬性值,也就是 LED 燈的默認狀態(tài)屬性
- create_gpio_led 函數創(chuàng)建 LED 相關的 io,常用gpio操作
create_gpio_led 函數簡析
staticintcreate_gpio_led(conststructgpio_led*template,
structgpio_led_data*led_dat,structdevice*parent,
structfwnode_handle*fwnode,gpio_blink_set_tblink_set)
{
structled_init_datainit_data={};
intret,state;
led_dat->cdev.default_trigger=template->default_trigger;
led_dat->can_sleep=gpiod_cansleep(led_dat->gpiod);
if(!led_dat->can_sleep)
led_dat->cdev.brightness_set=gpio_led_set;
else
led_dat->cdev.brightness_set_blocking=gpio_led_set_blocking;
led_dat->blinking=0;
if(blink_set){
led_dat->platform_gpio_blink_set=blink_set;
led_dat->cdev.blink_set=gpio_blink_set;
}
if(template->default_state==LEDS_GPIO_DEFSTATE_KEEP){
state=gpiod_get_value_cansleep(led_dat->gpiod);
if(state0)
returnstate;
}else{
state=(template->default_state==LEDS_GPIO_DEFSTATE_ON);
}
led_dat->cdev.brightness=state?LED_FULL:LED_OFF;
if(!template->retain_state_suspended)
led_dat->cdev.flags|=LED_CORE_SUSPENDRESUME;
if(template->panic_indicator)
led_dat->cdev.flags|=LED_PANIC_INDICATOR;
if(template->retain_state_shutdown)
led_dat->cdev.flags|=LED_RETAIN_AT_SHUTDOWN;
ret=gpiod_direction_output(led_dat->gpiod,state);
if(ret0)
returnret;
if(template->name){
led_dat->cdev.name=template->name;
ret=devm_led_classdev_register(parent,&led_dat->cdev);
}else{
init_data.fwnode=fwnode;
ret=devm_led_classdev_register_ext(parent,&led_dat->cdev,
&init_data);
}
returnret;
}
-
函數create_gpio_led描述:
- 先獲取gpiod信息
- 配置led_dat屬性,包括default_state, brightness等信息
- 配置gpio方向
- 將led注冊給LED子系統(tǒng)
gpio_led_set函數簡析
staticvoidgpio_led_set(structled_classdev*led_cdev,
enumled_brightnessvalue)
{
structgpio_led_data*led_dat=cdev_to_gpio_led_data(led_cdev);
intlevel;
if(value==LED_OFF)
level=0;
else
level=1;
if(led_dat->blinking){
led_dat->platform_gpio_blink_set(led_dat->gpiod,level,
NULL,NULL);
led_dat->blinking=0;
}else{
if(led_dat->can_sleep)
gpiod_set_value_cansleep(led_dat->gpiod,level);
else
gpiod_set_value(led_dat->gpiod,level);
}
}
-
函數gpio_led_set描述:
- 先獲取led_gpio信息
- 獲取led 的brightness的value
- 通過gpiod_set_value設置gpio的電平
DTS編寫
gpio_leds:gpio-leds{
compatible="gpio-leds";
led@1{
gpios=<&gpio3?RK_PA4?GPIO_ACTIVE_HIGH>;
label="blue1";//Blue1
retain-state-suspended;
};
led@2{
gpios=<&gpio2?RK_PB2?GPIO_ACTIVE_HIGH>;
label="blue2";//Blue2
retain-state-suspended;
};
};
- 創(chuàng)建一個節(jié)點表示 LED 燈設備,比如gpio_leds,如果板子上有多個 LED 燈的話每個 LED燈都作為gpio_leds的子節(jié)點。
- gpio_leds節(jié)點的 compatible 屬性值一定要為“gpio-leds”。
- 設置 label 屬性,此屬性為可選,每個子節(jié)點都有一個 label 屬性,label 屬性一般表示LED 燈的名字,比如以顏色區(qū)分的話就是 red、green 等等。
- 每個子節(jié)點必須要設置 gpios 屬性值,表示此 LED 所使用的 GPIO 引腳!
-
可以設置“l(fā)inux,default-trigger”屬性值,也就是設置 LED 燈的默認功能,可以查閱Documentation/devicetree/bindings/leds/common.txt 這個文檔來查看可選功能,比
- backlight:LED 燈作為背光。
- default-on:LED 燈打開
- heartbeat:LED 燈作為心跳指示燈,可以作為系統(tǒng)運行提示燈。
- ide-disk:LED 燈作為硬盤活動指示燈。
- timer:LED 燈周期性閃爍,由定時器驅動,閃爍頻率可以修改
- 可以設置“default-state”屬性值,可以設置為 on、off 或 keep,為 on 的時候 LED 燈默認打開,為 off 的話 LED 燈默認關閉,為 keep 的話 LED 燈保持當前模式。
基于Sysfs操作Led-gpio
關于sysfs相關文章:《linux--sysfs文件系統(tǒng)》
- 點亮LED-GPIO--輸入:echo 255 > /sys/class/leds/blue1/brightness
- 關閉LED-GPIO--輸入:echo 0 > /sys/class/leds/blue1/brightness
- 閃爍LED-GPIO--輸入:
#echotimer>/sys/class/leds/blue1/trigger
#echo100>/sys/class/leds/blue1/delay_on
#echo200>/sys/class/leds/blue1/delay_off
-
led
+關注
關注
240文章
22905瀏覽量
650082 -
Linux
+關注
關注
87文章
11123瀏覽量
207892 -
子系統(tǒng)
+關注
關注
0文章
109瀏覽量
12345
發(fā)布評論請先 登錄
相關推薦
評論