Linux内核发展迅速,作为一名驱动工程师,掌握驱动框架尤为重要。以前写单片机程序或者上手linux驱动程序的时候,都把点灯作为入门程序。可是linux实在做的太好了,都把点灯这么简单的东西都封装好框架。而用户只需要调用LED子系统相关的API就可以实现LED操作。
Led子系统源码路径位于driver/leds下,驱动文件位于driver/leds/leds-gpio.c,关于led子系统的核心文件是led-class.c和led-core.c
从drivers/leds/Makefile中可以看到LED子系统的主要文件有几类: LED核心:leds.h、led-core.c、led-class.c、led-
triggers.c(其中led-triggers又分为了timer、ide-disk、heartbeat、backlight、gpio、default-on等)
include/linux/leds.h
24 enum led_brightness {
25 LED_OFF = 0,
26 LED_HALF = 127,
27 LED_FULL = 255,
28 };
led_classdev 结构:
30 struct led_classdev {
31 const char *name;
32 int brightness;
33 int flags;
34
35 #define LED_SUSPENDED (1 << 0)
36
37 /* Set LED brightness level */
38 void (*brightness_set)(struct led_classdev *led_cdev,
39 enum led_brightness brightness);
40
41 struct class_device *class_dev;
42 struct list_head node; /* LED Device list */
43 char *default_trigger; /* Trigger to use */
44
45 #ifdef CONFIG_LEDS_TRIGGERS
46 /* Protects the trigger data below */
47 rwlock_t trigger_lock;
48
49 struct led_trigger *trigger;
50 struct list_head trig_list;
51 void *trigger_data;
52 #endif
53 };
LED_TRIGGER
68 struct led_trigger {
69 /* Trigger Properties */
70 const char *name;
71 void (*activate)(struct led_classdev *led_cdev);
72 void (*deactivate)(struct led_classdev *led_cdev);
73
74 /* LEDs under control by this trigger (for simple triggers) */
75 rwlock_t leddev_list_lock;
76 struct list_head led_cdevs;
77
78 /* Link to next registered trigger */
79 struct list_head next_trig;
80 };
相关文件:
(1)led-class.c和led-core.c,这两个文件加起来属于LED驱动框架的第一部分,这两个文件是内核开发者提供的,他们描述的是内核中所有厂家的不同LED硬件的相同部分的逻辑,是LED子系统的核心层
(2)leds-xxxx.c,这个文件是LED驱动框架的第二部分,是由驱动工程师编写添加完成的,驱动工程师结合自己公司的硬件的不同情况来对LED进行操作,使用第一部分提供的接口来和驱动框架进行交互,最终实现驱动的功能
以leds-s3c24xx.c为例。leds-s3c24xx.c中通过调用led_classdev_register来完成LED驱动的注册,而led_classdev_register是在drivers/leds/led-class.c中定义的。厂商的驱动工程师是调用内核开发者在驱动框架中提供的接口来实现自己的驱动的。
使用led-class中的框架写出一个led子系统的驱动程序,根据Linux驱动编写的经验,就是分配,设置。注册led_classdev
1. 分配led_classdev
2. 设置 :
led_cdev->max_brightness
led_cdev->brightness_set
led_cdev->flags
led_cdev->brightness
led_cdev->name
3. 注册 : led_classdev_register
68 static int s3c24xx_led_init()
69 {
70 int ret;
71 mydev.name="myled";
72 mydev.brightness=255;
73 mydev.brightness_set=my_led_set;
74 ret=led_classdev_register(NULL,&mydev);
75 if(ret<0)
76 {
77 printk("register led_dev fail!\n");
78 return ret;
79 }
80 gpbcon=(volatile unsigned long *)ioremap(0x56000010,16);//映射物理地址 16表示结束大小
81 //0x56000010表示gpbcon 0x56000014代表gpbdat 0x56000018代表gpbup 0x5600001c 保留
82 gpbdat=gpbcon+1;
83
84 //return platform_driver_register(&my_led_driver);
85 return 0;
86 }
41 static void my_led_set(struct led_classdev *led_cdev,enum led_brightness value)
42 {
43 printk("2440_led_set\n");
44 *gpbcon =0x00111400; // 设置GPB5 GPB6 GPB8 GPB10 为输出口, 位[9:8]=0b01
45 if(value==LED_OFF)//灭灯
46 {
47 *gpbdat&=~((1<<5)|(1<<6)|(1<<8)|(1<<10));
48 }
49 else//亮灯
50 {
51 *gpbdat|=(1<<5)|(1<<6)|(1<<8)|(1<<10);
52 }
53 }
加载模块时发现led_sys: Unknown symbol led_classdev_unregister
led_sys: Unknown symbol led_classdev_register
可能是内核没有支持LEDS_CLASS
make muconfig 看一下
确实是编译成ko文件了,make module生成相应的ko文件 把led-class.ko文件上传到开发板文件系统中去
然后insmod 注册了一个led设备
写的驱动能够工作了,被加载了,/sys/class/leds/目录下多出来了一个表示设备的文件夹。文件夹里面有相应的操控led硬件的2个属性brightness和max_brightness
echo 0 >/sys/class/leds/myled/brightness
echo 1 >/sys/class/leds/myled/brightness
就可以操作led了,led-class.c中brightness方法有一个show方法和store方法,当echo 1 > brightness时,实际就会执行led_brightness_store函数
分析echo 1 brightness
led_brightness_store
led_set_brightness(led_cdev,state)
led_cdev->brightness(led_cdev,value))
my_led_set(struct led_classdev *led_cdev,enum led_brightness value)