基於sys檔案系統的LED驅動的移植【原創】

weixin_33912246發表於2017-06-05

基於RK3188平臺LED驅動程式的移植的移植。如有不正確之處,歡迎大家指點。

本文的LED驅動程式不是通過開啟裝置節點來訪問和控制LED的,是通過sys檔案系統來控制LED。

板子上有四盞燈以及對應的GPIO的引腳如下:

 

基於sys檔案系統的LED驅動核心已經提供了,我們需要做的事情沒有那麼多。核心通過的LED驅動程式走的是平臺匯流排的方式,板級檔案Board-rk3188-u4301.c (kernel\arch\arm\mach-rk3188) 裡新增LED的GPIO的資訊。

 1 static struct gpio_led rk29_leds[] = {
 2     {    
 3         .name = "power",    //在/sys/class/leds/ 目錄下顯示的檔名
 4         .gpio = RK30_PIN0_PB4,      //LED的GPIO口的引腳
 5         .default_state = LEDS_GPIO_DEFSTATE_OFF,    //設定預設的狀態
 6     },
 7     {    
 8         .name = "paper",
 9         .gpio = RK30_PIN0_PB5,
10         .default_state = LEDS_GPIO_DEFSTATE_OFF,
11     },
12     {    
13         .name = "connect",
14         .gpio = RK30_PIN0_PB6,
15         .default_state = LEDS_GPIO_DEFSTATE_OFF,
16     },
17     {    
18         .name = "status",
19         .gpio = RK30_PIN0_PB7,
20         .default_state = LEDS_GPIO_DEFSTATE_OFF,
21     },
22 };
23 
24 static struct gpio_led_platform_data rk29_leds_pdata = {
25     .leds = rk29_leds,
26     .num_leds = ARRAY_SIZE(rk29_leds),
27 };
28 
29 static struct platform_device rk29_device_gpio_leds = {
30     .name    = "leds-gpio",          //裝置的名稱,驅動就是根據這個檔案匹配的啊。
31     .id    = -1,
32     .dev    = {
33         .platform_data  = &rk29_leds_pdata,
34     },
35 };

我們在看看驅動檔案Leds-gpio.c (\\192.168.1.144\zsf\rk3188_5.1\android\kernel\drivers\leds)

  1 /*
  2  * LEDs driver for GPIOs
  3  *
  4  * Copyright (C) 2007 8D Technologies inc.
  5  * Raphael Assenat <raph@8d.com>
  6  * Copyright (C) 2008 Freescale Semiconductor, Inc.
  7  *
  8  * This program is free software; you can redistribute it and/or modify
  9  * it under the terms of the GNU General Public License version 2 as
 10  * published by the Free Software Foundation.
 11  *
 12  */
 13 #include <linux/kernel.h>
 14 #include <linux/init.h>
 15 #include <linux/platform_device.h>
 16 #include <linux/leds.h>
 17 #include <linux/of_platform.h>
 18 #include <linux/of_gpio.h>
 19 #include <linux/slab.h>
 20 #include <linux/workqueue.h>
 21 
 22 #include <asm/gpio.h>
 23 
 24 struct gpio_led_data {
 25     struct led_classdev cdev;
 26     unsigned gpio;
 27     struct work_struct work;
 28     u8 new_level;
 29     u8 can_sleep;
 30     u8 active_low;
 31     u8 blinking;
 32     int (*platform_gpio_blink_set)(unsigned gpio, int state,
 33             unsigned long *delay_on, unsigned long *delay_off);
 34 };
 35 
 36 static void gpio_led_work(struct work_struct *work)
 37 {
 38     struct gpio_led_data    *led_dat =
 39         container_of(work, struct gpio_led_data, work);
 40 
 41     if (led_dat->blinking) {
 42         led_dat->platform_gpio_blink_set(led_dat->gpio,
 43                          led_dat->new_level,
 44                          NULL, NULL);
 45         led_dat->blinking = 0;
 46     } else
 47         gpio_set_value_cansleep(led_dat->gpio, led_dat->new_level);
 48 }
 49 
 50 static void gpio_led_set(struct led_classdev *led_cdev,
 51     enum led_brightness value)
 52 {
 53     struct gpio_led_data *led_dat =
 54         container_of(led_cdev, struct gpio_led_data, cdev);
 55     int level;
 56 
 57     if (value == LED_OFF)
 58         level = 0;
 59     else
 60         level = 1;
 61 
 62     if (led_dat->active_low)
 63         level = !level;
 64 
 65     /* Setting GPIOs with I2C/etc requires a task context, and we don't
 66      * seem to have a reliable way to know if we're already in one; so
 67      * let's just assume the worst.
 68      */
 69     if (led_dat->can_sleep) {
 70         led_dat->new_level = level;
 71         schedule_work(&led_dat->work);
 72     } else {
 73         if (led_dat->blinking) {
 74             led_dat->platform_gpio_blink_set(led_dat->gpio, level,
 75                              NULL, NULL);
 76             led_dat->blinking = 0;
 77         } else
 78             gpio_set_value(led_dat->gpio, level);
 79     }
 80 }
 81 
 82 static int gpio_blink_set(struct led_classdev *led_cdev,
 83     unsigned long *delay_on, unsigned long *delay_off)
 84 {
 85     struct gpio_led_data *led_dat =
 86         container_of(led_cdev, struct gpio_led_data, cdev);
 87 
 88     led_dat->blinking = 1;
 89     return led_dat->platform_gpio_blink_set(led_dat->gpio, GPIO_LED_BLINK,
 90                         delay_on, delay_off);
 91 }
 92 
 93 static int __devinit create_gpio_led(const struct gpio_led *template,
 94     struct gpio_led_data *led_dat, struct device *parent,
 95     int (*blink_set)(unsigned, int, unsigned long *, unsigned long *))
 96 {
 97     int ret, state;
 98 
 99     led_dat->gpio = -1;
100 
101     /* skip leds that aren't available */
102     if (!gpio_is_valid(template->gpio)) {
103         printk(KERN_INFO "Skipping unavailable LED gpio %d (%s)\n",
104                 template->gpio, template->name);
105         return 0;
106     }
107 
108     ret = gpio_request(template->gpio, template->name);
109     if (ret < 0)
110         return ret;
111 
112     led_dat->cdev.name = template->name;
113     led_dat->cdev.default_trigger = template->default_trigger;
114     led_dat->gpio = template->gpio;
115     led_dat->can_sleep = gpio_cansleep(template->gpio);
116     led_dat->active_low = template->active_low;
117     led_dat->blinking = 0;
118     if (blink_set) {
119         led_dat->platform_gpio_blink_set = blink_set;
120         led_dat->cdev.blink_set = gpio_blink_set;
121     }
122     led_dat->cdev.brightness_set = gpio_led_set;
123     if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP)
124         state = !!gpio_get_value(led_dat->gpio) ^ led_dat->active_low;
125     else
126         state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
127     led_dat->cdev.brightness = state ? LED_FULL : LED_OFF;
128     if (!template->retain_state_suspended)
129         led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME;
130 
131     ret = gpio_direction_output(led_dat->gpio, led_dat->active_low ^ state);
132     if (ret < 0)
133         goto err;
134         
135     INIT_WORK(&led_dat->work, gpio_led_work);
136 
137     ret = led_classdev_register(parent, &led_dat->cdev);
138     if (ret < 0)
139         goto err;
140 
141     return 0;
142 err:
143     gpio_free(led_dat->gpio);
144     return ret;
145 }
146 
147 static void delete_gpio_led(struct gpio_led_data *led)
148 {
149     if (!gpio_is_valid(led->gpio))
150         return;
151     led_classdev_unregister(&led->cdev);
152     cancel_work_sync(&led->work);
153     gpio_free(led->gpio);
154 }
155 
156 struct gpio_leds_priv {
157     int num_leds;
158     struct gpio_led_data leds[];
159 };
160 
161 static inline int sizeof_gpio_leds_priv(int num_leds)
162 {
163     return sizeof(struct gpio_leds_priv) +
164         (sizeof(struct gpio_led_data) * num_leds);
165 }
166 
167 /* Code to create from OpenFirmware platform devices */
168 #ifdef CONFIG_LEDS_GPIO_OF
169 static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev)
170 {
171     struct device_node *np = pdev->dev.of_node, *child;
172     struct gpio_leds_priv *priv;
173     int count = 0, ret;
174 
175     /* count LEDs in this device, so we know how much to allocate */
176     for_each_child_of_node(np, child)
177         count++;
178     if (!count)
179         return NULL;
180 
181     priv = kzalloc(sizeof_gpio_leds_priv(count), GFP_KERNEL);
182     if (!priv)
183         return NULL;
184 
185     for_each_child_of_node(np, child) {
186         struct gpio_led led = {};
187         enum of_gpio_flags flags;
188         const char *state;
189 
190         led.gpio = of_get_gpio_flags(child, 0, &flags);
191         led.active_low = flags & OF_GPIO_ACTIVE_LOW;
192         led.name = of_get_property(child, "label", NULL) ? : child->name;
193         led.default_trigger =
194             of_get_property(child, "linux,default-trigger", NULL);
195         state = of_get_property(child, "default-state", NULL);
196         if (state) {
197             if (!strcmp(state, "keep"))
198                 led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
199             else if (!strcmp(state, "on"))
200                 led.default_state = LEDS_GPIO_DEFSTATE_ON;
201             else
202                 led.default_state = LEDS_GPIO_DEFSTATE_OFF;
203         }
204 
205         ret = create_gpio_led(&led, &priv->leds[priv->num_leds++],
206                       &pdev->dev, NULL);
207         if (ret < 0) {
208             of_node_put(child);
209             goto err;
210         }
211     }
212 
213     return priv;
214 
215 err:
216     for (count = priv->num_leds - 2; count >= 0; count--)
217         delete_gpio_led(&priv->leds[count]);
218     kfree(priv);
219     return NULL;
220 }
221 
222 static const struct of_device_id of_gpio_leds_match[] = {
223     { .compatible = "gpio-leds", },
224     {},
225 };
226 #else
227 static struct gpio_leds_priv * __devinit gpio_leds_create_of(struct platform_device *pdev)
228 {
229     return NULL;
230 }
231 #define of_gpio_leds_match NULL
232 #endif
233 
234 
235 static int __devinit gpio_led_probe(struct platform_device *pdev)
236 {
237     struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
238     struct gpio_leds_priv *priv;
239     int i, ret = 0;
240 
241     if (pdata && pdata->num_leds) {
242         priv = kzalloc(sizeof_gpio_leds_priv(pdata->num_leds),
243                 GFP_KERNEL);
244         if (!priv)
245             return -ENOMEM;
246 
247         priv->num_leds = pdata->num_leds;
248         for (i = 0; i < priv->num_leds; i++) {
249             ret = create_gpio_led(&pdata->leds[i],
250                           &priv->leds[i],
251                           &pdev->dev, pdata->gpio_blink_set);
252             if (ret < 0) {
253                 /* On failure: unwind the led creations */
254                 for (i = i - 1; i >= 0; i--)
255                     delete_gpio_led(&priv->leds[i]);
256                 kfree(priv);
257                 return ret;
258             }
259         }
260     } else {
261         priv = gpio_leds_create_of(pdev);
262         if (!priv)
263             return -ENODEV;
264     }
265 
266     platform_set_drvdata(pdev, priv);
267 
268     return 0;
269 }
270 
271 static int __devexit gpio_led_remove(struct platform_device *pdev)
272 {
273     struct gpio_leds_priv *priv = dev_get_drvdata(&pdev->dev);
274     int i;
275 
276     for (i = 0; i < priv->num_leds; i++)
277         delete_gpio_led(&priv->leds[i]);
278 
279     dev_set_drvdata(&pdev->dev, NULL);
280     kfree(priv);
281 
282     return 0;
283 }
284 
285 static struct platform_driver gpio_led_driver = {
286     .probe        = gpio_led_probe,
287     .remove        = __devexit_p(gpio_led_remove),
288     .driver        = {
289         .name    = "leds-gpio",
290         .owner    = THIS_MODULE,
291         .of_match_table = of_gpio_leds_match,
292     },
293 };
294 
295 MODULE_ALIAS("platform:leds-gpio");
296 
297 static int __init gpio_led_init(void)
298 {
299     printk(KERN_ERR"zbzhuang leds");
300 
301 
302     return platform_driver_register(&gpio_led_driver);
303 }
304 
305 static void __exit gpio_led_exit(void)
306 {
307     platform_driver_unregister(&gpio_led_driver);
308 }
309 
310 module_init(gpio_led_init);
311 module_exit(gpio_led_exit);
312 
313 MODULE_AUTHOR("Raphael Assenat <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
314 MODULE_DESCRIPTION("GPIO LED driver");
315 MODULE_LICENSE("GPL");
View Code

 驅動檔案就是根據名字跟 裝置進行匹配。匹配成功之後就會在建立sys檔案系統提供介面給應用程式控制裝置。

 在核心執行make menuconfig,要配置LED驅動的一些功能如閃爍和呼吸燈等功能,編譯進核心。

重新燒錄開發板的核心。之後通過串列埠進入開發板。在/sys/class/leds目錄下建立出了,我們板級檔案下新增的4個LED驅動。

下面我們演示如何通過sys檔案系統控制LED的亮滅。進入connect目錄。執行下面三條命令就可控制LED燈的亮滅和進入呼吸燈的模式。

 

 

 

 

 

 

 

相關文章