linux平台设备驱动模型是什么,linux设备驱动模型之平台总线实践环节(一)

1、首先回顾下之前写的驱动和数据在一起的led驱动代码,代码如下:#include 

#include 

#include 

#include //ioremap和iounmap的头文件writel等

/**********************************静态映射虚拟地址的方法,用的是内核移植时的静态映射表**********************************/

#include 

#include 

#define GPJ0CONS5PV210_GPJ0CON//这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,

//以此类推。

#define GPJ0DATS5PV210_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_set\n");

writel(0x11111111, GPJ0CON);

if (value == LED_OFF) {//用户输入0时灭对应用户输入的是echo 0 > brightness

writel(readl(GPJ0DAT) | (1 <

}else if (value == LED_FULL){//用户输入255时亮对应用户输入的是echo 255 > brightness

writel(readl(GPJ0DAT) & ~(1 <

}

}

static void whyx210_led2_set(struct led_classdev *led_cdev, enum led_brightness value)

{

//printk(KERN_INFO "whyx210_led_set\n");

writel(0x11111111, GPJ0CON);

if (value == LED_OFF) {//用户输入0时灭对应用户输入的是echo 0 > brightness

writel(readl(GPJ0DAT) | (1 <

}else if (value == LED_FULL){//用户输入255时亮对应用户输入的是echo 255 > brightness

writel(readl(GPJ0DAT) & ~(1 <

}

}

static void whyx210_led3_set(struct led_classdev *led_cdev, enum led_brightness value)

{

//printk(KERN_INFO "whyx210_led_set\n");

writel(0x11111111, GPJ0CON);

if (value == LED_OFF) {//用户输入0时灭对应用户输入的是echo 0 > brightness

writel(readl(GPJ0DAT) | (1 <

}else if (value == LED_FULL){//用户输入255时亮对应用户输入的是echo 255 > brightness

writel(readl(GPJ0DAT) & ~(1 <

}

}

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 

printk(KERN_ERR "led_classdev_register errro\n");

return ret;

}

//printk(KERN_INFO "led_classdev_register success %s\n", 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 

printk(KERN_ERR "led_classdev_register errro\n");

return ret;

}

//printk(KERN_INFO "led_classdev_register success %s\n", 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 

printk(KERN_ERR "led_classdev_register errro\n");

return ret;

}

//printk(KERN_INFO "led_classdev_register success %s\n", 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 <417842990@qq.com>");

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 GPJ0CONS5PV210_GPJ0CON//这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,

//以此类推。

#define GPJ0DATS5PV210_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_set\n");

writel(0x11111111, GPJ0CON);

if (value == LED_OFF) {//用户输入0时灭对应用户输入的是echo 0 > brightness

writel(readl(GPJ0DAT) | (1 <

}else if (value == LED_FULL){//用户输入255时亮对应用户输入的是echo 255 > brightness

writel(readl(GPJ0DAT) & ~(1 <

}

}

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 

printk(KERN_ERR "led_classdev_register errro\n");

return ret;

}

//printk(KERN_INFO "led_classdev_register success %s\n", 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 <417842990@qq.com>");

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 GPJ0CONS5PV210_GPJ0CON//这个宏是在gpio-bank.h中定义的,是虚拟地址,这个宏还用到了regs-gpio.h中的宏,

//以此类推。

#define GPJ0DATS5PV210_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_set\n");

writel(0x11111111, GPJ0CON);

if (value == LED_OFF) {//用户输入0时灭对应用户输入的是echo 0 > brightness

writel(readl(GPJ0DAT) | (1 <

}else if (value == LED_FULL){//用户输入255时亮对应用户输入的是echo 255 > brightness

writel(readl(GPJ0DAT) & ~(1 <

}

}

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 

printk(KERN_ERR "led_classdev_register errro\n");

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 <417842990@qq.com>");

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驱动就是可以使用的了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值