首页 > 代码库 > RK3066 实现LED闪烁的代码分析
RK3066 实现LED闪烁的代码分析
实现LED灯的闪烁,需要在驱动里添加一个定时器函数,具体实现涉及到了LED GPIO驱动,用户空间程序调用驱动程序。
1.首先来看LED设备驱动注册过程,代码位于../kernel/drivers/leds/led-gpio.c中,
297 static int __init gpio_led_init(void)
298 {
299 return platform_driver_register(&gpio_led_driver);
300 }
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", //与platform_device结构体rk29_device_gpio_leds(见后面的添加过程)中定义的.name一致,platform总线通过name将两者关联;
290 .owner = THIS_MODULE,
291 .of_match_table = of_gpio_leds_match,
292 },
293 };
driver中的probe函数,其中pdev对应于 ../kernel/arch/arm/mach-rk30/board-rk30sdk-box.c 中定义的platform_device结构体rk29_device_gpio_leds,其中.name = "leds-gpio"
235 static int __devinit gpio_led_probe(struct platform_device *pdev)
236 {
237 struct gpio_led_platform_data *pdata = http://www.mamicode.com/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); //创建具体的设备,对应结构体rk29_leds中的LED1,LED2,HDMI-sw
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 }
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 }
247 /**
248 * led_classdev_register - register a new object of led_classdev class.
249 * @parent: The device to register.
250 * @led_cdev: the led_classdev structure for this device.
251 */
252 int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
253 {
254 led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
255 "%s", led_cdev->name); //device_create - creates a device and registers it with sysfs;创建设备节点在/sys/class/leds下创建LED1 LED2 HDMI-sw
256 if (IS_ERR(led_cdev->dev))
257 return PTR_ERR(led_cdev->dev);
258
259 #ifdef CONFIG_LEDS_TRIGGERS
260 init_rwsem(&led_cdev->trigger_lock);
261 #endif
262 /* add to the list of leds */
263 down_write(&leds_list_lock);
264 list_add_tail(&led_cdev->node, &leds_list); //把设备节点添加到leds的链表中
265 up_write(&leds_list_lock);
266
267 if (!led_cdev->max_brightness)
268 led_cdev->max_brightness = LED_FULL;
269
270 led_update_brightness(led_cdev);
271
272 init_timer(&led_cdev->blink_timer); //初始化一个定时器
273 led_cdev->blink_timer.function = led_timer_function; //注册一个定时器调用函数
274 led_cdev->blink_timer.data = http://www.mamicode.com/(unsigned long)led_cdev; //定时器调用函数参数
275
276 #ifdef CONFIG_LEDS_TRIGGERS
277 led_trigger_set_default(led_cdev);
278 #endif
279
280 printk(KERN_DEBUG "Registered led device: %s\n",
281 led_cdev->name);
282
283 return 0;
284 }
定时器调用函数,定时器到时启动
131 static void led_timer_function(unsigned long data)
132 {
133 struct led_classdev *led_cdev = (void *)data;
134 unsigned long brightness;
135 unsigned long delay;
136
137 if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
138 led_set_brightness(led_cdev, LED_OFF);
139 return;
140 }
141
142 brightness = led_get_brightness(led_cdev);
143 if (!brightness) {
144 /* Time to switch the LED on. */
145 brightness = led_cdev->blink_brightness;
146 delay = led_cdev->blink_delay_on;
147 } else {
148 /* Store the current brightness value to be able
149 * to restore it when the delay_off period is over.
150 */
151 led_cdev->blink_brightness = brightness;
152 brightness = LED_OFF;
153 delay = led_cdev->blink_delay_off;
154 }
155
156 led_set_brightness(led_cdev, brightness); //执行gpio高低切换的函数
157
158 mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay)); //可以修改定时器定时参数
159 }
添加平台设备rk29_device_gpio_leds,代码位于../kernel/arch/arm/mach-rk30/board-rk30-box.c中,
1061 static struct platform_device rk29_device_gpio_leds = {
1062 .name = "leds-gpio",
1063 .id = -1,
1064 .dev = {
1065 .platform_data = &rk29_leds_pdata,
1066 },
1067 };
1056 static struct gpio_led_platform_data rk29_leds_pdata = http://www.mamicode.com/{
1057 .leds = rk29_leds,
1058 .num_leds = ARRAY_SIZE(rk29_leds),
1059 };
1060
972 static struct gpio_led rk29_leds[] = {
......
1030 {
1031 .name = "LED1",
1032 .gpio = RK30_PIN4_PC6,
1033 .active_low = 0,
1034 .retain_state_suspended = 0,
1035 .default_state = LEDS_GPIO_DEFSTATE_OFF,
1036 },
1037
1038 {
1039 .name = "LED2",
1040 .gpio = RK30_PIN4_PC7,
1041 .active_low = 0,
1042 .retain_state_suspended = 0,
1043 .default_state = LEDS_GPIO_DEFSTATE_ON,
1044 },
1045
1046 {
1047 .name = "HDMI-sw",
1048 .gpio = RK30_PIN4_PD2,
1049 .active_low = 0,
1050 .retain_state_suspended = 0,
1051 .default_state = LEDS_GPIO_DEFSTATE_ON,
1052 },
1053
1054 };
2. 创建一个类,进入系统后可以看到 /sys/class/leds目录,驱动层代码位于../kernel/drivers/leds/led-class.c中,
339 static int __init leds_init(void)
340 {
341 leds_class = class_create(THIS_MODULE, "leds"); //对应于 /sys/class/leds
342 if (IS_ERR(leds_class))
343 return PTR_ERR(leds_class);
344 leds_class->suspend = led_suspend;
345 leds_class->resume = led_resume;
346 leds_class->dev_attrs = led_class_attrs; //属于类leds设备的属性
347 return 0;
348 }
属于类leds设备的属性的定义,用户空间程序通过读取设置这些属性来调用LED驱动
119 static struct device_attribute led_class_attrs[] = {
120 __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
121 __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
122 #ifdef CONFIG_LEDS_TRIGGERS
123 __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
124 #endif
125 __ATTR(blinkquick, 0644, NULL, led_blink_store_quick),
126 __ATTR(blinkslow, 0644, NULL, led_blink_store_slow),
127 __ATTR_NULL,
128
129 };
130
__ATTR的定义,属性名称,模式,读取函数,设置函数
70 #define __ATTR(_name,_mode,_show,_store) { \
71 .attr = {.name = __stringify(_name), .mode = _mode }, \
72 .show = _show, \
73 .store = _store, \
74 }
led_blink_store_slow函数的定义,
static ssize_t led_blink_store_slow(struct device *dev,
89 struct device_attribute *attr, const char *buf, size_t size)
90 {
91 struct led_classdev *led_cdev = dev_get_drvdata(dev);
92 ssize_t ret = -EINVAL;
93 char *after;
94 unsigned long state = simple_strtoul(buf, &after, 10);
95 size_t count = after - buf;
96 unsigned long delay_on = 500;
97 unsigned long delay_off = 500;
98
99 if (isspace(*after))
100 count++;
101
102 if (count == size) {
103 ret = count;
104 if(state == 0)
105 delay_on = 0;
106 }
107 led_blink_set(led_cdev,&delay_on,&delay_off);
108 return ret;
109 }
313 void led_blink_set(struct led_classdev *led_cdev,
314 unsigned long *delay_on,
315 unsigned long *delay_off)
316 {
317 del_timer_sync(&led_cdev->blink_timer);
318
319 if (led_cdev->blink_set &&
320 !led_cdev->blink_set(led_cdev, delay_on, delay_off))
321 return;
322
323 /* blink with 1 Hz as default if nothing specified */
324 if (!*delay_on && !*delay_off)
325 *delay_on = *delay_off = 500;
326
327 led_set_software_blink(led_cdev, *delay_on, *delay_off);
328 }
led_set_software_blink函数的定义,
169 static void led_set_software_blink(struct led_classdev *led_cdev,
170 unsigned long delay_on,
171 unsigned long delay_off)
172 {
173 int current_brightness;
174
175 current_brightness = led_get_brightness(led_cdev);
176 if (current_brightness)
177 led_cdev->blink_brightness = current_brightness;
178 if (!led_cdev->blink_brightness)
179 led_cdev->blink_brightness = led_cdev->max_brightness;
180
181 if (led_get_trigger_data(led_cdev) &&
182 delay_on == led_cdev->blink_delay_on &&
183 delay_off == led_cdev->blink_delay_off)
184 return;
185
186 led_stop_software_blink(led_cdev);
187
188 led_cdev->blink_delay_on = delay_on;
189 led_cdev->blink_delay_off = delay_off;
190
191 /* never on - don‘t blink */
192 if (!delay_on)
193 return;
194
195 /* never off - just set to brightness */
196 if (!delay_off) {
197 led_set_brightness(led_cdev, led_cdev->blink_brightness);
198 return;
199 }
200
201 mod_timer(&led_cdev->blink_timer, jiffies + 1); //将已初始化的定时器led_cdev->blink_timer添加到系统定时器链表中
202 }
3. 系统启动首先运行leds_init 创建了/sys/class/leds,将设备属性blinkslow与函数led_blink_store_slow相关连;而后执行gpio_led_init创建并注册设备,创建设备节点在/sys/class/leds下创建LED1 LED2 HDMI-sw,初始化一个定时器led_cdev->blink_timer注册一个定时器调用函数led_timer_function.
Android中的内核启动后,kernel会启动第一个用户级别的进程:init,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开 始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。init始终是第一个进程。Android init.*.rc文件由系统第一个启动的init程序解析,在init.rk30board.rc设置如下:
53 write /sys/class/leds/LED1/blinkslow 1
54 write /sys/class/leds/HDMI-sw/blinkslow 1
blinkslow写1又是如何 如何调用函数led_blink_store_slow的呢?
4. 通过在led_blink_set函数中添加 WARN_ON(1), 可以追踪函数调用过程,具体如下:
[ 3.315763] ------------[ cut here ]------------
[ 3.315829] WARNING: at /home/vichie/netcast/firefly-2.0/netcast/os/kernel/drivers/leds/led-class.c:315 led_blink_set+0x20/0x10c()
[ 3.315934] [<c043e61c>] (unwind_backtrace+0x0/0xf8) from [<c046fe8c>] (warn_slowpath_common+0x4c/0x64)
[ 3.316004] [<c046fe8c>] (warn_slowpath_common+0x4c/0x64) from [<c046fec0>] (warn_slowpath_null+0x1c/0x24)
[ 3.316081] [<c046fec0>] (warn_slowpath_null+0x1c/0x24) from [<c0757a2c>] (led_blink_set+0x20/0x10c)
[ 3.316150] [<c0757a2c>] (led_blink_set+0x20/0x10c) from [<c0757b94>] (led_blink_store_slow+0x7c/0x94)
[ 3.316219] [<c0757b94>] (led_blink_store_slow+0x7c/0x94) from [<c0667590>] (dev_attr_store+0x18/0x24)
[ 3.316291] [<c0667590>] (dev_attr_store+0x18/0x24) from [<c054b104>] (sysfs_write_file+0x168/0x198)
[ 3.316366] [<c054b104>] (sysfs_write_file+0x168/0x198) from [<c05009a4>] (vfs_write+0xa0/0x144)
[ 3.316432] [<c05009a4>] (vfs_write+0xa0/0x144) from [<c0500c30>] (sys_write+0x38/0x70)
[ 3.316492] [<c0500c30>] (sys_write+0x38/0x70) from [<c0439140>] (ret_fast_syscall+0x0/0x30)
[ 3.316548] ---[ end trace fd8d711c10e99270 ]---
同样在led_timer_function中添加 WARN_ON(1), 可以追踪到每次LED开关时led_timer_function被调用的过程:
shell@rk30sdk:/ $ [ 2817.591119] ------------[ cut here ]------------
[ 2817.591180] WARNING: at /home/vichie/netcast/firefly-2.0/netcast/os/kernel/drivers/leds/led-class.c:135 led_timer_function+0x14/0xd4()
[ 2817.591282] [<c043e61c>] (unwind_backtrace+0x0/0xf8) from [<c046fe8c>] (warn_slowpath_common+0x4c/0x64)
[ 2817.591351] [<c046fe8c>] (warn_slowpath_common+0x4c/0x64) from [<c046fec0>] (warn_slowpath_null+0x1c/0x24)
[ 2817.591423] [<c046fec0>] (warn_slowpath_null+0x1c/0x24) from [<c075795c>] (led_timer_function+0x14/0xd4)
[ 2817.591497] [<c075795c>] (led_timer_function+0x14/0xd4) from [<c047d7e0>] (run_timer_softirq+0x138/0x370)
[ 2817.591566] [<c047d7e0>] (run_timer_softirq+0x138/0x370) from [<c0476430>] (__do_softirq+0xcc/0x238)
[ 2817.591631] [<c0476430>] (__do_softirq+0xcc/0x238) from [<c0476a30>] (irq_exit+0x98/0xa0)
[ 2817.591695] [<c0476a30>] (irq_exit+0x98/0xa0) from [<c0433274>] (do_local_timer+0x70/0x94)
[ 2817.591756] [<c0433274>] (do_local_timer+0x70/0x94) from [<c0438c48>] (__irq_svc+0x48/0xe0)
[ 2817.591812] Exception stack(0xd6c5ff48 to 0xd6c5ff90)
[ 2817.591851] ff40: d6c5ff90 00000000 05737eba 00000290 04db99cd 00000290
[ 2817.591909] ff60: 000003ff feb3cfff c160e0c0 00000001 c14fe06c c0b5a530 c0ae60c0 d6c5ff90
[ 2817.591964] ff80: c0495564 c044d7d4 60000013 ffffffff
[ 2817.592008] [<c0438c48>] (__irq_svc+0x48/0xe0) from [<c044d7d4>] (rk30_idle+0x44/0x74)
[ 2817.592070] [<c044d7d4>] (rk30_idle+0x44/0x74) from [<c0745244>] (cpuidle_idle_call+0xac/0x200)
[ 2817.592134] [<c0745244>] (cpuidle_idle_call+0xac/0x200) from [<c0439ef4>] (cpu_idle+0xbc/0xf8)
RK3066 实现LED闪烁的代码分析