1、首先回顾下之前写的驱动和数据在一起的 led 驱动代码,代码如下:
- #include
- #include
- #include
- #include //ioremap和iounmap的头文件 writel等
- /**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表**********************************/
- #include
- #include
- #define GPJ0CON S5PV210_GPJ0CON //这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,
- //以此类推。
- #define GPJ0DAT S5PV210_GPJ0DAT
- static struct led_classdev myled1;
- static struct led_classdev myled2;
- static struct led_classdev myled3;
- //这个函数绑定到了struct led_classdev这个结构体类型的myled变量中的brightness_set成员,当我们用户写这个led硬件的时候,因为是用led驱动框架
- //写的led驱动,所以我们是通过led驱动框架中的brightness这个文件,去写或这个读led硬件的,应为这个文件在驱动框架中具有了可读可写的属性,是在
- //struct //device_attribute结构中拥有的,并且最终绑定了到了这个设备类中。所以最后我们用户去通过brightness这个属性文件去写led硬件的时候,会去执行led_br//ightness_store这个方法,这个方法中,又
- //通过调用led_set_brightness这个函数,这个函数中通过led_cdev->brightness_set(led_cdev, value);这条语句,最终对应到了struct //led_classdev结构体中的brightness_set这个函数指针变量,而这个函数指针变量在我们填充那个结构体
- //struct led_classdev中的brightness_set函数指针时,就已经绑定好了我们写的那个写操作硬件的函数了。所以这一条线就打通了.value就是用户要写的值
- //value是枚举,是enum led_brightness类型的枚举,这个枚举有三个值,分别是LED_OFF = 0 LED_XXX = 122 LED_FULL = 255,
- static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value)
- {
- //printk(KERN_INFO "whyx210_led_setn");
- writel(0x11111111, GPJ0CON);
- if (value == LED_OFF) { //用户输入0时灭 对应用户输入的是echo 0 > brightness
- writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT);
- }else if (value == LED_FULL){ //用户输入255时亮 对应用户输入的是echo 255 > brightness
- writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT); //这样操作保持其他位值不变
- }
- }
- static void whyx210_led2_set(struct led_classdev *led_cdev, enum led_brightness value)
- {
- //printk(KERN_INFO "whyx210_led_setn");
- writel(0x11111111, GPJ0CON);
- if (value == LED_OFF) { //用户输入0时灭 对应用户输入的是echo 0 > brightness
- writel(readl(GPJ0DAT) | (1 << 4), GPJ0DAT);
- }else if (value == LED_FULL){ //用户输入255时亮 对应用户输入的是echo 255 > brightness
- writel(readl(GPJ0DAT) & ~(1 << 4), GPJ0DAT); //这样操作保持其他位值不变
- }
- }
- static void whyx210_led3_set(struct led_classdev *led_cdev, enum led_brightness value)
- {
- //printk(KERN_INFO "whyx210_led_setn");
- writel(0x11111111, GPJ0CON);
- if (value == LED_OFF) { //用户输入0时灭 对应用户输入的是echo 0 > brightness
- writel(readl(GPJ0DAT) | (1 << 5), GPJ0DAT);
- }else if (value == LED_FULL){ //用户输入255时亮 对应用户输入的是echo 255 > brightness
- writel(readl(GPJ0DAT) & ~(1 << 5), GPJ0DAT); //这样操作保持其他位值不变
- }
- }
- static int __init whyx210_led_init(void)
- {
- int ret = -1;
- //led1
- //填充我们要注册的struct led_classdev类型的结构体
- myled1.name = "led1"; //将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
- myled1.brightness = 255;
- myled1.brightness_set = whyx210_led1_set;
- //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
- //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- ret = led_classdev_register(NULL, &myled1);
- if (ret < 0) {
- printk(KERN_ERR "led_classdev_register errron");
- return ret;
- }
- //printk(KERN_INFO "led_classdev_register success %sn", myled.name);
- /*********************************************************************************************/
- //led2
- //填充我们要注册的struct led_classdev类型的结构体
- myled2.name = "led2"; //将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
- myled2.brightness = 255;
- myled2.brightness_set = whyx210_led2_set;
- //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
- //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- ret = led_classdev_register(NULL, &myled2);
- if (ret < 0) {
- printk(KERN_ERR "led_classdev_register errron");
- return ret;
- }
- //printk(KERN_INFO "led_classdev_register success %sn", myled.name);
- /*********************************************************************************************/
- //led3
- //填充我们要注册的struct led_classdev类型的结构体
- myled3.name = "led3"; //将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
- myled3.brightness = 255;
- myled3.brightness_set = whyx210_led3_set;
- //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
- //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- ret = led_classdev_register(NULL, &myled3);
- if (ret < 0) {
- printk(KERN_ERR "led_classdev_register errron");
- return ret;
- }
- //printk(KERN_INFO "led_classdev_register success %sn", myled.name);
- return 0;
- }
- static void __init whyx210_led_exit(void)
- {
- led_classdev_unregister(&myled1);
- led_classdev_unregister(&myled2);
- led_classdev_unregister(&myled3);
- //printk(KERN_INFO "led_classdev_unregister success.n");
- }
- module_init(whyx210_led_init);
- module_exit(whyx210_led_exit);
- MODULE_AUTHOR("why <[email protected]>");
- MODULE_DESCRIPTION("whyx210 LED driver");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("whyx210_led");
现在先将 led2 和 led3 的代码去掉,只留下 led1,并且我们要知道我们怎么去用 platform 平台总线的方法去实现 led 的驱动,我们要有 probe 函数,和 remove 函数,分别对应的是驱动和设备匹配上后执行的 probe 函数,以及设备和驱动分离时的 remove 函数也就是卸载驱动的函数,我们要将数据部分的代码写入到 platform 的 platform_data 中,在驱动代码的 probe 函数中,用参数的方式,将设备的数据部分得到,实现驱动和设备数据的代码的分离逻辑框架。
上面的代码只留下 led1 的驱动代码后,代码如下:
- #include
- #include
- #include
- #include //ioremap和iounmap的头文件 writel等
- /**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表**********************************/
- #include
- #include
- #define GPJ0CON S5PV210_GPJ0CON //这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,
- //以此类推。
- #define GPJ0DAT S5PV210_GPJ0DAT
- static struct led_classdev myled1;
- //这个函数绑定到了struct led_classdev这个结构体类型的myled变量中的brightness_set成员,当我们用户写这个led硬件的时候,因为是用led驱动框架
- //写的led驱动,所以我们是通过led驱动框架中的brightness这个文件,去写或这个读led硬件的,应为这个文件在驱动框架中具有了可读可写的属性,是在
- //struct //device_attribute结构中拥有的,并且最终绑定了到了这个设备类中。所以最后我们用户去通过brightness这个属性文件去写led硬件的时候,会去执行led_br//ightness_store这个方法,这个方法中,又
- //通过调用led_set_brightness这个函数,这个函数中通过led_cdev->brightness_set(led_cdev, value);这条语句,最终对应到了struct //led_classdev结构体中的brightness_set这个函数指针变量,而这个函数指针变量在我们填充那个结构体
- //struct led_classdev中的brightness_set函数指针时,就已经绑定好了我们写的那个写操作硬件的函数了。所以这一条线就打通了.value就是用户要写的值
- //value是枚举,是enum led_brightness类型的枚举,这个枚举有三个值,分别是LED_OFF = 0 LED_XXX = 122 LED_FULL = 255,
- static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value)
- {
- //printk(KERN_INFO "whyx210_led_setn");
- writel(0x11111111, GPJ0CON);
- if (value == LED_OFF) { //用户输入0时灭 对应用户输入的是echo 0 > brightness
- writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT);
- }else if (value == LED_FULL){ //用户输入255时亮 对应用户输入的是echo 255 > brightness
- writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT); //这样操作保持其他位值不变
- }
- }
- static int __init whyx210_led_init(void)
- {
- int ret = -1;
- //led1
- //填充我们要注册的struct led_classdev类型的结构体
- myled1.name = "led1"; //将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
- myled1.brightness = 255;
- myled1.brightness_set = whyx210_led1_set;
- //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
- //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- ret = led_classdev_register(NULL, &myled1);
- if (ret < 0) {
- printk(KERN_ERR "led_classdev_register errron");
- return ret;
- }
- //printk(KERN_INFO "led_classdev_register success %sn", myled.name);
- return 0;
- }
- static void __init whyx210_led_exit(void)
- {
- led_classdev_unregister(&myled1);
- }
- module_init(whyx210_led_init);
- module_exit(whyx210_led_exit);
- MODULE_AUTHOR("why <[email protected]>");
- MODULE_DESCRIPTION("whyx210 LED driver");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("whyx210_led");
makefile 的代码如下:
- #ubuntu中的内核源码树目录
- #KERN_VER = $(shell uname -r)
- #KERN_DIR = /lib/modules/$(KERN_VER)/build
- #我编译的九鼎内核的源码树目录
- KERN_DIR = /root/xin_x210/kernel
- obj-m += leds-why210.o
- all:
- make -C $(KERN_DIR) M=`pwd` modules
- #arm-linux-gcc app.c -o app
- cp:
- cp *.ko /root/rootfs/rootfs/driver_test/ -f
- #cp app /root/rootfs/rootfs/driver_test/ -f
- .PHONY: clean
- clean:
- make -C $(KERN_DIR) M=`pwd` modules clean
- # rm app -f
makefile 的代码不用动
之后我们在 led 的驱动代码中,修改并添加 probe 函数和 led 驱动需要的结构体。加入 probe 函数和 remove 函数,并且加入并填充 led 驱动需要的结构体,并将 init 函数也就是 ismod 加载驱动模块时执行的函数,和 exit 函数也就是卸载驱动模块时执行的函数,将这两个函数中的内容分别改为用 platform 提供的注册函数进行注册驱动(led 驱动的结构体)和用 platform 提供的注销驱动(led 驱动结构体)的方法。改动后的代码如下:
- #include
- #include
- #include
- #include //ioremap和iounmap的头文件 writel等
- /**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表**********************************/
- #include
- #include
- #include
- #define GPJ0CON S5PV210_GPJ0CON //这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,
- //以此类推。
- #define GPJ0DAT S5PV210_GPJ0DAT
- static struct led_classdev myled1;
- static void whyx210_led1_set(struct led_classdev *led_cdev, enum led_brightness value)
- {
- //printk(KERN_INFO "whyx210_led_setn");
- writel(0x11111111, GPJ0CON);
- if (value == LED_OFF) { //用户输入0时灭 对应用户输入的是echo 0 > brightness
- writel(readl(GPJ0DAT) | (1 << 3), GPJ0DAT);
- }else if (value == LED_FULL){ //用户输入255时亮 对应用户输入的是echo 255 > brightness
- writel(readl(GPJ0DAT) & ~(1 << 3), GPJ0DAT); //这样操作保持其他位值不变
- }
- }
- static int why_led_remove(struct platform_device *dev)
- {
- led_classdev_unregister(&myled1);
- return 0;
- }
- static int why_led_probe(struct platform_device *dev)
- {
- int ret = -1;
- //led1
- //填充我们要注册的struct led_classdev类型的结构体
- myled1.name = "led1"; //将来sys/class/leds/目录下的那个led文件的名字。leds这个目录在led-class.c中内核帮我们实现好了、
- myled1.brightness = 255;
- myled1.brightness_set = whyx210_led1_set;
- //去调用led驱动框架中为我们提供的led注册函数led_classdev_register去注册驱动
- //在led-class.c中int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
- ret = led_classdev_register(NULL, &myled1);
- if (ret < 0) {
- printk(KERN_ERR "led_classdev_register errron");
- return ret;
- }
- return 0;
- }
- static struct platform_driver why_led_driver = {
- .probe = why_led_probe,
- .remove = why_led_remove,
- .driver = {
- .name = "why_led",
- .owner = THIS_MODULE,
- },
- };
- static int __init whyx210_led_init(void)
- {
- return platform_driver_register(&why_led_driver);
- }
- static void __init whyx210_led_exit(void)
- {
- platform_driver_unregister(&why_led_driver);
- }
- module_init(whyx210_led_init);
- module_exit(whyx210_led_exit);
- MODULE_AUTHOR("why <[email protected]>");
- MODULE_DESCRIPTION("whyx210 LED driver");
- MODULE_LICENSE("GPL");
- MODULE_ALIAS("whyx210_led");
分析,此时 probe 和 remove 函数的内容还不是正确的,用 makefile 编译能编译通过,生成一个 leds-why210.ko 驱动模块,我们将这个驱动模块拷贝到根文件系统中,执行 insmod 的时候,对应的 whyx210_led_init 函数会被执行,会将 led 的驱动结构体数据结构注册到内核中,此时我们在根文件系统的 / sys/bus/platform/driver / 目录下面会看到有一个 why_led 的文件,说明我们的驱动注册成功了,但是是用不了的,因为我们没有提供 platform 总线下需要的 platform_device,probe 函数和 remove 也是不对的,如果正确的话,我们提供 platform_device 后,将其进行注册后,当有 led 设备的时候,驱动也被加载的时候,那么 match 函数会将 led 的驱动和设备进行匹配,驱动的 probe 函数会得以执行,led 的数据部分是在 platform_device 中的,在驱动的 probe 函数中将 platform_device 中的数据部分得到,最后在 probe 函数中会进行一些注册,这个时候,在根文件系统的 / sys/bus/platform/driver/why_led 目录下就会有其他东西,这个时候我们的 led 驱动就是可以使用的了。
来源: http://www.bubuko.com/infodetail-2098446.html