Linux下的LED子系統驅動
阿新 • • 發佈:2019-02-04
轉載自 http://my.csdn.net/weiqing1981127
最簡單的led驅動就是從埠輸出0或1來關閉或點亮燈。而我們這裡講的led子系統,主要是對led事件進行了分裝和優化,這裡我們主要講的是可以實現跨平臺的led驅動。不管你是使用三星的平臺,還是Atmel的平臺,你只要知道如何在你的BSP中新增平臺數據,並且知道如何在應用程式中使用這個驅動,那麼你就不用因為新的平臺而再次編寫led驅動。
按鍵驅動屬於input子系統,原始碼路徑在/driver/leds下,我們的跨平臺按鍵驅動檔案是/driver/leds/leds-gpio.c,關於led子系統的核心檔案是Led-class.c和Led-core.c
檢視/driver/leds/Makefile
obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
檢視/driver/leds/Konfig
config LEDS_GPIO
tristate"LED Support for GPIO connected LEDs"
dependson LEDS_CLASS && GENERIC_GPIO
其中
config LEDS_CLASS
tristate"LED Class Support"
所以配置核心makemenuconfig 時,需要選中這兩項。
現在先來看如何移植,比如我們現在要給mini2440開發板上的led1到key4編寫led驅動,根據資料知道,led1到led4用的是GPB5-GPB8埠。下面就看移植程式碼了,在mach-mini2440.c這個mini2440開發板的BSP中新增如下程式碼
static struct gpio_led s3c_gpio_leds[] = {
{
.name = "led1" ,
.gpio = S3C2410_GPB(5),
.active_low= 1,
},
{
.name = "led2",
.gpio = S3C2410_GPB(6),
.active_low= 1,
},
{
.name = "led3" ,
.gpio = S3C2410_GPB(7),
.active_low= 1,
},
{
.name = "led4",
.gpio = S3C2410_GPB(8),
.active_low= 1,
},
};
static struct gpio_led_platform_datas3c_gpio_led_data = {
.leds = s3c_gpio_leds,
.num_leds = ARRAY_SIZE(s3c_gpio_leds),
};
static struct platform_device s3c_leds_gpio= {
.name = "leds-gpio",
.id = -1,
.dev = {
.platform_data = &s3c_gpio_led_data,
},
};
然後把這個s3c_leds_gpio加入到mini2440_devices陣列
static struct platform_device*mini2440_devices[] __initdata = {
……
&s3c_leds_gpio, //新增
};
最後新增標頭檔案
#include <linux/leds.h>
這樣配置完後,進行makezImage生成zImage核心映象。
下面大致說說/driver/leds/leds-gpio.c
直接看平臺驅動定義
static struct platform_drivergpio_led_driver = {
.probe = gpio_led_probe, //探測
.remove = __devexit_p(gpio_led_remove),
.driver = {
.name = "leds-gpio", //驅動名
.owner = THIS_MODULE,
},
};
下面看probe探測函式
static int __devinit gpio_led_probe(structplatform_device *pdev)
{
structgpio_led_platform_data *pdata = pdev->dev.platform_data;
structgpio_led_data *leds_data;
inti, ret = 0;
if(!pdata)
return-EBUSY;
leds_data= kzalloc(sizeof(struct gpio_led_data) * pdata->num_leds, //分配空間
GFP_KERNEL);
if(!leds_data)
return-ENOMEM;
for(i = 0; i < pdata->num_leds; i++) {
ret= create_gpio_led(&pdata->leds[i], &leds_data[i],
&pdev->dev,pdata->gpio_blink_set); //建立led裝置
if(ret < 0)
gotoerr;
}
platform_set_drvdata(pdev,leds_data);
return0;
err:
for(i = i - 1; i >= 0; i--)
delete_gpio_led(&leds_data[i]);
kfree(leds_data);
returnret;
}
繼續跟蹤probe中的create_gpio_led函式
static int __devinit create_gpio_led(conststruct gpio_led *template,
structgpio_led_data *led_dat, struct device *parent,
int(*blink_set)(unsigned, unsigned long *, unsigned long *))
{
intret, state;
led_dat->gpio= -1;
if(!gpio_is_valid(template->gpio)) {
printk(KERN_INFO"Skipping unavailable LED gpio %d (%s)\n",
template->gpio,template->name);
return0;
}
ret= gpio_request(template->gpio, template->name);
if(ret < 0)
returnret;
led_dat->cdev.name= template->name;
led_dat->cdev.default_trigger= template->default_trigger;
led_dat->gpio= template->gpio;
led_dat->can_sleep= gpio_cansleep(template->gpio);
led_dat->active_low= template->active_low;
if(blink_set) {
led_dat->platform_gpio_blink_set= blink_set;
led_dat->cdev.blink_set= gpio_blink_set; //定義函式
}
led_dat->cdev.brightness_set= gpio_led_set; //定義函式
if(template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
state= !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
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;
ret= gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
if(ret < 0)
gotoerr;
INIT_WORK(&led_dat->work,gpio_led_work); //初始化工作佇列
ret= led_classdev_register(parent, &led_dat->cdev); //向leds類中註冊裝置
if(ret < 0)
gotoerr;
return0;
err:
gpio_free(led_dat->gpio);
returnret;
}
講到這,讓我們恍然明白,這個leds子系統跟之前分析過的backlight背光子系統是非常類似的。backlight背光子系統在核心層定義了一套device_attribute機制,並且定義了操作backlight背光的操作函式集的介面,同時在device_attribute的show和store屬性中會呼叫backlight背光的操作函式集。然後我們使用這個backlight背光子系統核心層,編寫自己的驅動時,只需要向backlight背光子系統註冊裝置,並填充backlight背光的操作函式集即可。使用者層只需要通過echo向brightness中寫數字觸發store屬性,或者通過cat向brightness中讀數字觸發show屬性。
再回過頭來看看我們這個leds子系統,在create_gpio_led函式中有這樣一行程式碼led_dat->cdev.brightness_set= gpio_led_set;其中的函式gpio_led_set是設定燈亮滅的函式,這就是相當於我們給led子系統的操作函式賦值,跟蹤led_classdev_register函式,你會發現在那裡會看到定義了leds類屬性,並建立了device_attribute機制,同時同時在device_attribute的store屬性中會呼叫我們這裡定義的的操作函式gpio_led_set。
所以,leds子系統的原始碼我就分析到這裡了,我在學習backlight子系統的時候,就細細分析過這樣一個類裡包含裝置屬性,以及裝置操作函式,這跟leds-gpio.c程式碼原理類似,如果需要可以參考。
LED驅動測試
使用者可以先通過cd/sys/class/leds開啟led子系統下的裝置
然後應用層通過訪問/sys/class/leds/led1來設定等的亮滅
點亮led1:
echo 1 > /sys/class/leds/led1
關閉led1:
echo 0 > /sys/class/leds/led1