LINUX内核中的gpio-led子系统
我们可以在板子初始化时将GPIO-LED注册为platform_device,并指定该led的触发器,比如内核定义的heartbeat触发器,在系统运行期间会一直闪。
arch/arm/mach-davinci/board-da850-evm.c
#define DA850_HEARTBEAT_LED GPIO_TO_PIN(6, 12)
static const short da850_evm_tl_user_led_pins[] = { // 这是引脚复用的列表 DA850_GPIO6_12, -1 }; static struct gpio_led da850_evm_tl_leds[] = { [0] = { .active_low = 0, .gpio = DA850_HEARTBEAT_LED, // GPIO引脚定义 .name = "heartbeat", // GPIO的名字,传递给gpio_request() .default_trigger = "heartbeat", // 触发器的名字 }, }; static struct gpio_led_platform_data da850_evm_tl_leds_pdata = { .leds = da850_evm_tl_leds, .num_leds = ARRAY_SIZE(da850_evm_tl_leds), }; static struct platform_device da850_evm_tl_leds_device = { .name = "leds-gpio", // platform_driver类型 .id = -1, .dev = { .platform_data = &da850_evm_tl_leds_pdata } }; static void da850_evm_tl_leds_init(void) { int ret; ret = davinci_cfg_reg_list(da850_evm_tl_user_led_pins); // 设置引脚复用,该函数是davinci系列处理器自有的 if (ret) pr_warning("da850_evm_tl_leds_init : User LED mux failed :" "%d\n", ret); ret = platform_device_register(&da850_evm_tl_leds_device); if (ret) { pr_warning("Could not register som GPIO expander LEDS"); } }
/drivers/leds/leds-gpio.c文件中,如下定义platform_driver
static const struct of_device_id of_gpio_leds_match[] = { { .compatible = "gpio-leds", }, {}, }; static struct platform_driver gpio_led_driver = { .probe = gpio_led_probe, .remove = __devexit_p(gpio_led_remove), .driver = { .name = "leds-gpio", .owner = THIS_MODULE, .of_match_table = of_gpio_leds_match, }, };
module_platform_driver(gpio_led_driver);
linux内核在初始化时,会根据platform_driver中定义的兼容名称列表,找到所有使用该platform_driver的platform_device,并逐个调用gpio_led_probe。
gpio_led_probe会申请每个GPIO,并调用led_classdev_register函数。
led_classdev_register()函数在led-calss.c文件中定义,该函数会将GPIO-LED加入名为leds_list的列表中。leds_list在led-core.c文件中定义。
//////////////////////////////////////////////////////////////////////////////////////////////////
触发器列表trigger_list在led-triggers.c中定义,该文件中同时定义了加入新触发器到该列表的函数led_trigger_register。
heartbeat触发器的定义在ledtrig-heartbeat.c文件中,核心是一个定时器。
static void heartbeat_trig_activate(struct led_classdev *led_cdev) { struct heartbeat_trig_data *heartbeat_data; heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL); if (!heartbeat_data) return; led_cdev->trigger_data = heartbeat_data; setup_timer(&heartbeat_data->timer, led_heartbeat_function, (unsigned long) led_cdev); heartbeat_data->phase = 0; led_heartbeat_function(heartbeat_data->timer.data); } static struct led_trigger heartbeat_led_trigger = { .name = "heartbeat", .activate = heartbeat_trig_activate, .deactivate = heartbeat_trig_deactivate, }; static int __init heartbeat_trig_init(void) { return led_trigger_register(&heartbeat_led_trigger); } static void __exit heartbeat_trig_exit(void) { led_trigger_unregister(&heartbeat_led_trigger); }
heartbeat触发器在初始化中,调用led_trigger_register注册触发器,并与使用该触发器的gpio-led绑定。
led_trigger_register函数的一部分:
/* Register with any LEDs that have this as a default trigger */ down_read(&leds_list_lock); 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, trigger->name)) led_trigger_set(led_cdev, trigger); up_write(&led_cdev->trigger_lock); }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
其实本来是想在自己的驱动中定义一个led-trigger的,弄明白了之后,我放弃了——对于我正在编写的专用驱动,还不如直接操作GPIO省事。