linux led子系统(一)
就像学编程第一个范例helloworld一样,学嵌入式,单片机、fpga之类的第一个范例就是点亮一盏灯。对于庞大的linux系统,当然可以编写一个字符设备驱动来实现我们需要的led灯,也可以直接利用gpio口,应用程序来拉高拉低管脚控制。不过,既然linux系统自己本来就带有led子系统,那么就可以好好利用之。好处不用多说了,主要对于应用层来说,不同平台都用linux的led子系统,那么应用程序不用做任何的改变,就可以在新的平台上运行,可移植性好。
linux的led子系统的源码路径:
- include/Linux/leds.h //头文件
- drivers/leds //led子系统相关源码以及API
首先看一下led子系统中的主要文件:
- # LED Core
- obj-$(CONFIG_NEW_LEDS) +=led-core.o
- obj-$(CONFIG_LEDS_CLASS) += led-class.o
- obj-$(CONFIG_LEDS_TRIGGERS) +=led-triggers.o
- # LED PlatformDrivers
- obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
- # LED Triggers
- obj-$(CONFIG_LEDS_TRIGGER_TIMER) +=ledtrig-timer.o
- obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) +=ledtrig-ide-disk.o
- obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) +=ledtrig-heartbeat.o
- obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) +=ledtrig-backlight.o
- obj-$(CONFIG_LEDS_TRIGGER_GPIO) +=ledtrig-gpio.o
- obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
例子程序是leds-gpio,接下去会主要分析这个驱动实现。
首先简单看一下主要的文件
leds.h
Led的亮度,分为三等级,关、中间、最亮。
enum led_brightness {
LED_OFF = 0, //全暗
LED_HALF = 127, //一半亮度
LED_FULL = 255, //最大亮度
};
struct led_classdev {
const char *name; //led名字
int brightness; //当前亮度
int max_brightness;//参考值,最大亮度
int flags; //标志,目前只支持 LED_SUSPENDED和LED_CORE_SUSPENDRESUME
/* Lower 16 bits reflect status */
#define LED_SUSPENDED (1 << 0)
/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME (1 << 16)
/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
//核心回调函数,当设置/sys/class/leds/下的led接口里的brightness(亮度)属性文件时,会回调该函数
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightness brightness); //亮度设置函数指针
/* Get LED brightness level */
//核心回调函数,当获得led当前亮度值时会调用
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);//获取亮度函数指针
/*
* Activate hardware accelerated blink, delays are in milliseconds
* and if both are zero then a sensible default should be chosen.
* The call should adjust the timings in that case and if it can't
* match the values specified exactly.
* Deactivate blinking again when the brightness is set to a fixed
* value via the brightness_set() callback.
*/
/* 激活硬件加速的闪烁 */
int (*blink_set)(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off);//闪烁时点亮和熄灭的时间设置
//嵌入的标准设备模型
struct device *dev;
/* 所有已经注册的led_class dev使用这个节点串联起来 */
struct list_head node; /* LED Device list */ //leds-list的node
/* 默认触发器 */
const char *default_trigger; /* Trigger to use */ //默认trigger的名字
unsigned long blink_delay_on, blink_delay_off; //闪烁的开关时间
struct timer_list blink_timer; //闪烁的定时器链表
int blink_brightness; //闪烁的亮度
//如果配置内核时使能了触发器功能,才会编译下面一段
#ifdef CONFIG_LEDS_TRIGGERS
/* Protects the trigger data below */
struct rw_semaphore trigger_lock; /* 这个读写锁保护下面的触发器数据 */
struct led_trigger *trigger; //触发器指针
struct list_head trig_list; //触发器使用的链表节点,用来连接同一触发器上的所有led_classdev
void *trigger_data; //触发器使用的私有数据
#endif
};
struct led_trigger {
const char *name; //trigger名字
void (*activate)(struct led_classdev *led_cdev);//激活led,led_classdev和触发器建立连接时会调用这个方法。
void (*deactivate)(struct led_classdev *led_cdev);//取消激活。led_classdev和触发器取消连接时会调用这个方法。
/* LEDs under control by this trigger (for simple triggers) */
/* 本触发器控制之下的led链表 */
rwlock_t leddev_list_lock; //保护链表的锁
struct list_head led_cdevs; //链表头
/* Link to next registered trigger */
struct list_head next_trig; /* 连接下一个已注册触发器的链表节点 ,所有已注册的触发器都会被加入一个全局链表*/
};
//平台设备相关的led数据结构
struct led_info {
const char *name;
const char *default_trigger;
int flags;
};
struct led_platform_data {
int num_leds;
struct led_info *leds;
};
/* For the leds-gpio driver */
//平台设备相关的gpio led数据结构
struct gpio_led {
const char *name; //led的名字
const char *default_trigger; //默认的trigger
unsigned gpio; //gpio口
unsigned active_low : 1;
unsigned retain_state_suspended : 1;
unsigned default_state : 2;
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
};
#define LEDS_GPIO_DEFSTATE_OFF 0
#define LEDS_GPIO_DEFSTATE_ON 1
#define LEDS_GPIO_DEFSTATE_KEEP 2
struct gpio_led_platform_data {
int num_leds; //led的个数
const struct gpio_led *leds; //平台设备相关的gpio led数据结构
#define GPIO_LED_NO_BLINK_LOW 0 /* No blink GPIO state low */
#define GPIO_LED_NO_BLINK_HIGH 1 /* No blink GPIO state high */
#define GPIO_LED_BLINK 2 /* Please, blink */
int (*gpio_blink_set)(unsigned gpio, int state,
unsigned long *delay_on,
unsigned long *delay_off);
};
led-core.c
//主要声明led的链表和锁
22 DECLARE_RWSEM(leds_list_lock);
23 EXPORT_SYMBOL_GPL(leds_list_lock);
25 LIST_HEAD(leds_list);
26 EXPORT_SYMBOL_GPL(leds_list);
led-class.c
1、 leds_init
主要是创建leds_class,赋值suspend和resume以及dev_attrs。
led_class_attrs
84 static struct device_attribute led_class_attrs[] = {
85 __ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
86 __ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
87 #ifdef CONFIG_LEDS_TRIGGERS
88 __ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
89 #endif
90 __ATTR_NULL,
91 };
2、led_classdev_register
创建classdev设备,也即Leds_class类中实例化一个对象,类似于c++的new一个对象,leds有很多种,而这里是注册一个特定的led,内核中的面向对象思想也极其丰富。
加到leds_list链表中,初始化blinktimer,指定blink_timer的function和data,设置trigger,然后一个新的led设备就注册好了,就可以使用了。
led-triggers.c
1、led_trigger_register
扫描trigger链表中是否有同名的trigger,接着把当前trigger加入到链表中,如果led_classdev中有默认的trigger,那么就设置这个默认的。
好了,简单看了下led子系统中比较重要的结构体和函数,那么接下去就可以通过leds-gpio这个驱动来进一步了解led子系统了。