1. 程式人生 > >Linux下的LED子系統驅動

Linux下的LED子系統驅動

轉載自 http://my.csdn.net/weiqing1981127
最簡單的led驅動就是從埠輸出01來關閉或點亮燈。而我們這裡講的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