时间:2024-08-02 16:09
人气:
作者:admin

LED子系统基于一个统一的驱动模型,这个模型定义了一系列标准的应用程序接口(API),用以执行LED的基本操作,如点亮、熄灭和调节闪烁频率。此外,该模型还规定了设备树节点的格式标准,使得硬件信息的描述变得规范化和统一化。
LED驱动程序主要分为两大类:
开发者在编写LED驱动时,需要遵循LED子系统定义的接口规范,实现包括但不限于以下几个关键函数:
同时,硬件相关的信息,如GPIO配置和亮度调节范围,需要在设备树中明确描述,以便驱动程序能够正确地识别和使用这些硬件资源。
设备树的使用,为硬件描述提供了一种结构化的方法,使得LED子系统的驱动程序能够更加灵活地适应不同的硬件环境。
LED子系统通过其标准化的驱动框架,极大地降低了LED驱动程序的开发难度,让开发者可以更加便捷地实现LED控制功能,而无需深入了解底层硬件的具体实现。
Led子系统相关描述可在内核源码中的文档中查看:Documentation/leds/leds-class.txt。
led 子系统是一个简单的 Linux 子系统 ,在目录 /sys/class/leds 下展示该子系统设备,每个设备都有自己的属性:

brightness:设置 LED 亮度,范围0~max_brightness
max_brightness:最大亮度(255或其他数字)
trigger:触发方式,如 heartbeat、mmc0、backlight、gpio
delay_off、delay_on:trigger为timer时,LED亮灭的时间,单位ms
Led子系统头文件:kernel/include/linux/leds.h
enumled_brightness{
LED_OFF=0,//全暗
LED_HALF=127,//一半亮度
LED_FULL=255,//最大亮度
};
维护 LED 子系统的所有触发器,为触发器提供注册操作函数:
注销操作函数:
以及其它触发器相关的操作函数
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;
};
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;
};
#defineLEDS_GPIO_DEFSTATE_OFF0
#defineLEDS_GPIO_DEFSTATE_ON1
#defineLEDS_GPIO_DEFSTATE_KEEP2
->DeviceDrivers
->LEDSupport(NEW_LEDS[=y])
->LEDSupportforGPIOconnectedLEDs

可以看出,把Linux内部自带的LED灯驱动编译进内核以后,CONFIG_LEDS_GPIO就会等于‘y’:

LED_GPIO灯驱动文件为/drivers/leds/leds-gpio.c,可以通过makefile文件查看:/drivers/leds/Makefile:

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);
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;i< priv->num_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(ret< 0)
returnret;
}
}else{
priv=gpio_leds_create(pdev);
if(IS_ERR(priv))
returnPTR_ERR(priv);
}
platform_set_drvdata(pdev,priv);
return0;
}
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(ret< 0){
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;
}
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(state< 0)
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(ret< 0)
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;
}
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_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;
};
};
关于sysfs相关文章:《linux--sysfs文件系统》




#echotimer>/sys/class/leds/blue1/trigger
#echo100>/sys/class/leds/blue1/delay_on
#echo200>/sys/class/leds/blue1/delay_off
