天天看點

tiny4412 驅動 (5)HeartbeatHeartbeat

Heartbeat

struct list_head leds_list;           // 資源連結清單
struct list_head trigger_list;       // 驅動連結清單
           

對于連結清單,總有建立, 添加,周遊這三個要素,下面簡單看下這兩個連結清單的三要素

trigger_list

建立

driversleds/led-triggers.c

LIST_HEAD(trigger_list);
           

添加

driversleds/led-triggers.c

module_init(heartbeat_trig_init);
heartbeat_trig_init    // Ledtrig-heartbeat.c (drivers\leds\trigger)
led_trigger_register
    list_add_tail(&trig->next_trig, &trigger_list);
           

周遊

driversleds/led-triggers.c

led_trigger_store
led_trigger_show
led_trigger_set_default
led_trigger_register
    list_for_each_entry(_trig, &trigger_list, next_trig)
           

leds_list

建立

drivers/leds/led-core.c

LIST_HEAD(leds_list);
           

添加

drivers/leds/led-class.c

static struct platform_driver gpio_led_driver = {
    .probe      = gpio_led_probe,
    .shutdown   = gpio_led_shutdown,
    .driver     = {
        .name   = "leds-gpio",
        .of_match_table = of_gpio_leds_match,
    },
};
static const struct of_device_id of_gpio_leds_match[] = {
    { .compatible = "gpio-leds", },
    {},
};
module_platform_driver(gpio_led_driver);    // Leds-gpio.c (drivers\leds)
​
    gpio_led_probe
        gpio_leds_create
        create_gpio_led
            devm_of_led_classdev_register
                of_led_classdev_register
                    list_add_tail(&led_cdev->node, &leds_list);
           

周遊

drivers/leds/led-triggers.c

static struct led_trigger heartbeat_led_trigger = {
    .name     = "heartbeat",
    .activate = heartbeat_trig_activate,
    .deactivate = heartbeat_trig_deactivate,
    .groups = heartbeat_trig_groups,
};
​
heartbeat_trig_init       //  driversleds/trigger/Ledtrig-heartbeat.c
    led_trigger_register(&heartbeat_led_trigger);
    led_trigger_unregister
        list_for_each_entry(led_cdev, &leds_list, node) 
           

先看資源,即裝置樹

vim arch/arm/boot/dts/exynos4412-tiny4412.dts

leds {
                compatible = "gpio-leds";
​
                led1 {
                        label = "led1";
                        gpios = <&gpm4 0 GPIO_ACTIVE_LOW>;
                        default-state = "off";
                        linux,default-trigger = "heartbeat";
                };
​
                led2 {
                        label = "led2";
                        gpios = <&gpm4 1 GPIO_ACTIVE_LOW>;
                        default-state = "off";
                };
​
                led3 {
                        label = "led3";
                        gpios = <&gpm4 2 GPIO_ACTIVE_LOW>;
                        default-state = "off";
                };
​
                led4 {
                        label = "led4";
                        gpios = <&gpm4 3 GPIO_ACTIVE_LOW>;
                        default-state = "off";
                        linux,default-trigger = "mmc0";
                };
        };
           

led1是系統heartbeat,這裡主要分析它。

其驅動程式在 drivers/leds/leds-gpio.c, 比對compatible = "gpio-leds"

static const struct of_device_id of_gpio_leds_match[] = {
        { .compatible = "gpio-leds", },
        {},
};
           

在gpio_led_probe裡面會解析裝置樹

gpio_led_probe
    gpio_leds_create
        fwnode_property_read_string(child, "label", &led.name);   // label
        devm_fwnode_get_gpiod_from_child(dev, NULL, child,
                                 GPIOD_ASIS,
                                 led.name);                     // name
        fwnode_property_read_string(child, "linux,default-trigger",
                        &led.default_trigger);
        fwnode_property_read_string(child, "default-state",
                         &state)
        create_gpio_led(&led, led_dat, dev, np, NULL);
            devm_of_led_classdev_register(parent, np, &led_dat->cdev);
                of_led_classdev_register -->
                    list_add_tail(&led_cdev->node, &leds_list); // leds_list連結清單産生了節點
           

驅動

那麼trigger驅動在哪裡呢

在drivers/leds/led-triggers.c

module_init(heartbeat_trig_init);
heartbeat_trig_init -->
led_trigger_register -->
list_for_each_entry(led_cdev, &leds_list, node) {
    down_write(&led_cdev->trigger_lock);
    if (!led_cdev->trigger && led_cdev->default_trigger &&
        !strcmp(led_cdev->default_trigger, trig->name))
        led_trigger_set(led_cdev, trig);
    up_write(&led_cdev->trigger_lock);
} // 周遊leds_list鍊
           

如果要控制

LED

,需要調用函數:

static struct led_trigger heartbeat_led_trigger = {
    .name     = "heartbeat",
    .activate = heartbeat_trig_activate,
    .deactivate = heartbeat_trig_deactivate,
};
           

在led_trigger_set函數裡面

void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
{
    if (led_cdev->trigger) {
        write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
        list_del(&led_cdev->trig_list);
        write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
            flags);
        cancel_work_sync(&led_cdev->set_brightness_work);
        led_stop_software_blink(led_cdev);
        if (led_cdev->trigger->deactivate)      // deactivate
            led_cdev->trigger->deactivate(led_cdev);
        led_cdev->trigger = NULL;
        led_set_brightness(led_cdev, LED_OFF);
    }
    if (trig) {
        write_lock_irqsave(&trig->leddev_list_lock, flags);
        list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
        write_unlock_irqrestore(&trig->leddev_list_lock, flags);
        led_cdev->trigger = trig;
        if (trig->activate)                     // activate
            trig->activate(led_cdev);
    }
}
           

而在heartbeat_trig_activate裡面

timer_setup(&heartbeat_data->timer, led_heartbeat_function, 0);
led_heartbeat_function(&heartbeat_data->timer);
           

操作

make menuconfig

Device Drivers  ---> 
	[*] LED Support  --->
		<*>   LED Class Support 
		[*]   LED Trigger support  --->
			<*>   LED Heartbeat Trigger
           

重新燒錄核心并進入檔案系統後,在/sys/class/leds裡面下有led0這個目錄

echo 0 > brightness            #停止閃爍
echo heartbeat > trigger       #繼續閃爍 
           

繼續閱讀