首页 > 代码库 > 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 }

 led_blink_set函数的定义,

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闪烁的代码分析