基于platform总线的中断(按键)字符设备驱动设计
来源:互联网 发布:js给select标签负值 编辑:程序博客网 时间:2024/04/30 22:06
基于platform总线的中断(按键)字符设备驱动设计
于platform总线的中断(按键)字符设备驱动设计2011-12-23 13:02:02
分类: LINUX
我的环境:
主机:Fedora 14,内核版本2.6.38.1
开发板:ARM9 tq2440
移植内核版本:linux-2.6.30.4
1、platform简介
为了理解platform总线设备驱动模型的实际运用,我首先分析了基于S3C2410的看门狗驱动实现过程,我本着将自己学过的知识在温习一遍的态度,完成了基于platform平台总线的外部中断(按键)的基本实现过程,分别采用了混杂字符设备和一般字符设备进行了操作,其中混杂字符设备驱动、应用程序参照了 Y-Kee http://blog.csdn.net/ayangke,QQ:843308498的基本框架。
platform总线是总线设备驱动模型中的一种,内核帮助实现了总线部分,我们在设计驱动的过程中只需要完成驱动和设备的添加即可。其中设备的添加包括添加设备名和相关的硬件资源(中断号和寄存器分布)。而驱动的添加除了添加必要的驱动模型以外,还需要得到硬件的具体资源,并对资源进行相应的操作和处理。
2、中断处理简介
中断是处理外设与处理器速度不匹配过程中常用的方法,基本的思想是,当产生中断以后,CPU必须停止当前处理的任务,转去执行中断处理程序,待中断处理程序处理完成以后再回到当前处理任务的过程。嵌入式处理器中的中断主要包括两部分CPU的内部中断(主要是异常)以及外设的中断(外部中断)。同时有的中断可以被屏蔽,而有的中断又不能被屏蔽,又可以将中断分为屏蔽和不可屏蔽中断。根据入口的跳转方法又可以将中断分为向量中断和非向量中断。向量中断通常是不同的中断号有不同的处理程序入口地址(硬件提供),而非向量中断通常是指共享型的中断,入口地址通常是由用户软件提供。
在linux内核中对中断的操作主要包括两部分:1、中断的申请和释放;2、中断处理程序设计
其中的中断的申请和释放,具体的采用函数:
- /*申请中断*/
- request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
- /*释放中断*/
- void free_irq(unsigned int irq, void *dev_id)
在request_irq中的参数:
1、unsigned int irq表示具体的中断号,一般而言,针对不同的CPU,中断号存在一定的差别。
2、irq_handler_t handler表示一个回调函数,具体的就是中断操作的具体函数,称为中断处理函数。根据
typedef irqreturn_t (*irq_handler_t)(int, void *);
irq_handler_t 是一个函数指针,也就是中断处理函数应该是一个参数分别为int和void*,返回值为irqreturn_t,是枚举型参数。
- enum irqreturn {
- IRQ_NONE,
- IRQ_HANDLED,
- IRQ_WAKE_THREAD,
- };
- typedef enum irqreturn irqreturn_t;
- #define IRQ_RETVAL(x) ((x) != IRQ_NONE)
其中中断处理函数的形式就是一个函数,采用一般的C语言就可以实现。并没有什么特别需要注意的事项,但是需要保证的是中断要尽可能的短小精悍,不要出现长时间等待以及睡眠等形式,不能出现互信息量、信号量等并发机制,只能采用自旋锁实现。
3、unsigned long flags参数表示的中断的触发方式(高低电平触发等)或者处理方式(快速、一般中断),其中的IRQF_DISABLE表示快速中断模式,IRQF_SHARED表示共享中断模式。
/*可以选择的基本触发方式和处理方式*/
- #include <linux/interrupt.h>
/*中断处理方式*/
- #ifndef IRQF_DISABLED
- #define IRQF_DISABLED SA_INTERRUPT
- #define IRQF_SAMPLE_RANDOM SA_SAMPLE_RANDOM
- #define IRQF_SHARED SA_SHIRQ
- #define IRQF_PROBE_SHARED SA_PROBEIRQ
- #define IRQF_PERCPU SA_PERCPU
- #ifdef SA_TRIGGER_MASK
- #define IRQF_TRIGGER_NONE 0
- #define IRQF_TRIGGER_LOW SA_TRIGGER_LOW
- #define IRQF_TRIGGER_HIGH SA_TRIGGER_HIGH
- #define IRQF_TRIGGER_FALLING SA_TRIGGER_FALLING
- #define IRQF_TRIGGER_RISING SA_TRIGGER_RISING
- #define IRQF_TRIGGER_MASK SA_TRIGGER_MASK
- #else /*中断触发方式*/
- #define IRQF_TRIGGER_NONE 0
- #define IRQF_TRIGGER_LOW 0
- #define IRQF_TRIGGER_HIGH 0
- #define IRQF_TRIGGER_FALLING 0
- #define IRQF_TRIGGER_RISING 0
- #define IRQF_TRIGGER_MASK 0
- #endif
- #endif
4、const char *name表示具体的设备名。
5、void *dev,这个参数比较灵活,可以选择不同的值,当中断选择为共享中断模式时,dev必须是唯一的;而当中断选择为其他处理模式时,该参数可以为NULL,也可以用来传递需要处理的变量或者资源。具体后面分析。
释放函数则相对来说简单很多,主要是释放的中断号和相关的处理数据。free_irq()针对共享设备非常有用,因为不能关闭中断disable_irq(),影响其他共享该中断号的设备。
3、功能简介
我这次完成的设计主要是采用platform设备驱动模型实现外部中断的设计。具体的操作主要是包括如下两步:
1、设备的添加(主要包括资源的添加和设备的注册)
2、驱动的设计(难点),首先要注册总线驱动,第二要完成具体驱动(外部中断或者按键)的设计(字符型驱动或者混杂设备驱动),最后实现对应的操作函数。
难点分析:
1、具体设备的结构体设计,设备包含的数据,这一步是驱动设计的重点。
2、中断函数的设计,需要处理那些数据,保证短小精悍。
3、具体操作的设计,主要包括初始化和具体函数的操作。
字符设备的驱动源码代码分析,关于混杂设备的参看引用文章。
首先是设备添加代码:
- #include<linux/module.h>
- #include<linux/init.h>
- #include<linux/kernel.h>
- #include<linux/string.h>
- #include<linux/platform_device.h>
- /*硬件相关的头文件*/
- #include<mach/regs-gpio.h>
- #include<mach/hardware.h>
- #include<linux/gpio.h>
- /*这个是硬件(CPU)密切相关的中断号*/
- #include<mach/irqs.h>
- /*硬件资源量,这是根据tq2440开发板确定的*/
- static struct resource tq2440_button_resource[]=
- {
- /*EINT0*/
- [0]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT0,
- .end = IRQ_EINT0,
- .name = "S3C24XX_EINT0",
- },
- /*EINT1*/
- [1]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT1,
- .end = IRQ_EINT1,
- .name = "S3C24xx_EINT1",
- },
- /*EINT2*/
- [2]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT2,
- .end = IRQ_EINT2,
- .name = "S3C24xx_EINT2",
- },
- /*EINT4*/
- [3]=
- {
- .flags = IORESOURCE_IRQ,
- .start = IRQ_EINT4,
- .end = IRQ_EINT4,
- .name = "S3C24xx_EINT4",
- },
- };
- static struct platform_device tq2440_button_device=
- {
- /*设备名*/
- .name = "tq2440_button",
- .id = -1,
- /*资源数*/
- .num_resources = ARRAY_SIZE(tq2440_button_resource),
- /*资源指针*/
- .resource = tq2440_button_resource,
- };
- static int __init tq2440_button_init(void)
- {
- int ret ;
- /*设备注册*/
- ret = platform_device_register(&tq2440_button_device);
- }
- static void __exit tq2440_button_exit(void)
- {
- /*设备的注销*/
- platform_device_unregister(&tq2440_button_device);
- }
- /*加载与卸载*/
- module_init(tq2440_button_init);
- module_exit(tq2440_button_exit);
- /*LICENSE和作者信息*/
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");
然后是驱动实现代码:
- #include<linux/types.h>
- #include<linux/kernel.h>
- #include<linux/init.h>
- #include<linux/module.h>
- #include<linux/platform_device.h>
- #include<mach/irqs.h>
- #include<linux/irq.h>
- #include<mach/regs-gpio.h>
- #include<linux/device.h>
- #include<linux/string.h>
- #include<linux/cdev.h>
- #include<linux/fs.h>
- #include<linux/spinlock.h>
- #include<linux/wait.h>
- #include<linux/interrupt.h>
- #include<linux/uaccess.h>
- #include<linux/poll.h>
- #define NUM_RESOURCE 4
- /*主设备号*/
- int dev_major = -1;
- /*中断结构体定义*/
- struct irqs
- {
- int pirqs[NUM_RESOURCE];
- char *names[NUM_RESOURCE];
- }irqs;
- /*完成具体设备的结构体设计*/
- struct tq2440_button
- {
- /*添加具体的字符设备结构*/
- struct cdev cdev;
- /*用于自动创建设备*/
- struct class *myclass;
- /*引用次数统计表*/
- unsigned int count;
- /*添加并行机制*/
- spinlock_t lock;
- /*添加等待队列*/
- wait_queue_head_t read_wait_queue;
- /*数据*/
- int bepressed;
- /*案件值*/
- int key_values;
- };
- static struct tq2440_button tq2440_button;
- static irqreturn_t tq2440_button_interrupt_handler(int irq,void*dev_id)
- {
- /*得到传递过来的参数*/
- struct tq2440_button * dev = dev_id;
- int i;
- /*根据得到的irq值确定具体的按键值*/
- for (i= 0; i < NUM_RESOURCE; ++ i)
- {
- /*判断条件*/
- if(irq== irqs.pirqs[i])
- {
- /*对关键数据添加并行机制*/
- spin_lock(&(dev->lock));
- /*确定被按下的值*/
- dev->key_values= i ;
- /*表示有数据可以读*/
- dev->bepressed= 1;
- spin_unlock(&(dev->lock));
- /*唤醒等待的队列*/
- wake_up_interruptible(&(dev->read_wait_queue));
- }
- }
- /*返回值*/
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- static int tq2440_button_open(struct inode*inode,struct file*filp)
- {
- int i = 0,ret= 0;
-
- /*中断申请*/
- /*这句话主要是实现间接控制,但是还是可以直接控制*/
- filp->private_data= &tq2440_button;
-
- /*修改被打开的次数值*/
- spin_lock(&(tq2440_button.lock));
- tq2440_button.count ++ ;
- spin_unlock(&(tq2440_button.lock));
- /*如果是第一次打开则需要申请中断,这是比较推荐的方法,也可以在probe函数中申请中断*/
- if(1==tq2440_button.count)
- {
- for(i= 0;i < NUM_RESOURCE; ++ i)
- {
- /*request_irq操作*/
- ret = request_irq(irqs.pirqs[i],
- tq2440_button_interrupt_handler,
- IRQ_TYPE_EDGE_BOTH,irqs.names[i],(void*)&tq2440_button);
-
- if(ret)
- {
- break;
- }
- }
- if(ret)/*错误处理机制*/
- {
- i --;
- for(; i>=0;--i)
- {
- /*禁止中断*/
-
- disable_irq(irqs.pirqs[i]);
-
- free_irq(irqs.pirqs[i],(void*)&tq2440_button);
- }
- return -EBUSY;
- }
- }
- return 0;
- }
- /*closed函数*/
- static int tq2440_button_close(struct inode*inode,struct file*filp)
- {
- /*确保是最后一次使用设备*/
- int i = 0;
- if(tq2440_button.count== 1)
- {
- for(i= 0; i < NUM_RESOURCE; ++ i)
- {
- free_irq(irqs.pirqs[i],(void*)&tq2440_button);
- }
- }
- /*更新设备文件引用次数*/
- spin_lock(&(tq2440_button.lock));
- tq2440_button.count = 0;
- spin_unlock(&(tq2440_button.lock));
- return 0;
- }
- static unsigned long tq2440_button_read(struct file*filp,char __user*buff,
- size_t count,loff_t offp)
- {
- /*设备操作*/
- struct tq2440_button *dev = filp->private_data;
-
- unsigned long err;
- if(!dev->bepressed)/*确保没有采用非堵塞方式读标志*/
- {
- if(filp->f_flags& O_NONBLOCK)
- return -EAGAIN;
- else
- /*添加等待队列,条件是bepressed*/
- wait_event_interruptible(dev->read_wait_queue,dev->bepressed);
- }
- /*复制数据到用户空间*/
- err = copy_to_user(buff,&(dev->key_values),min(sizeof(dev->key_values),count));
-
- /*修改标志表示没有数据可读了*/
- spin_lock(&(dev->lock));
- dev->bepressed= 0;
- spin_unlock(&(dev->lock));
-
- /*返回数据量*/
-
- return err ? -EFAULT:min(sizeof(dev->key_values),count);
- }
- static unsigned int tq2440_button_poll(struct file*filp,
- struct poll_table_struct *wait)
- {
- struct tq2440_button *dev = filp->private_data;
- unsigned int mask = 0;
-
- /*将结构体中的等待队列添加到wait_table*/
- poll_wait(filp,&(dev->read_wait_queue),wait);
- /*
- 返回掩码
- POLLIN|POLLRDNORM表示有数据可读
- */
- if(dev->bepressed)
- {
- mask |= POLLIN| POLLRDNORM;
- }
- return mask;
- }
- /*设备的具体操作函数*/
- static const struct file_operations tq2440_fops=
- {
- .owner = THIS_MODULE,
- .open = tq2440_button_open,
- .release = tq2440_button_close,
- .read = tq2440_button_read,
- .poll = tq2440_button_poll,
- };
- /*remove函数实现字符设备的注销操作*/
- static int tq2440_button_probe(struct platform_device*dev)
- {
- printk("The driver found a device can be handler on platform bus\n");
-
- /*用来存储定义好的资源,即中断号*/
-
- struct resource * irq_resource;
- struct platform_device *pdev = dev;
- int i = 0,ret= 0;
- /*接下来完成具体字符驱动结构体的初始化*/
- /*1、设备号申请*/
-
- dev_t devno;
- if(dev_major> 0)/*静态申请设备号*/
- {
- devno = MKDEV(dev_major,0);
- ret = register_chrdev_region(devno,1,"tq2440_button");
- }
- else/*动态申请设备号*/
- {
- ret = alloc_chrdev_region(&devno,0,1,"tq2440_button");
- dev_major = MAJOR(devno);
- }
- if(ret< 0)
- {
- return ret;
- }
- /*完成设备类的创建,主要实现设备文件的自动创建*/
- tq2440_button.myclass = class_create(THIS_MODULE,"tq2440_button_class");
-
- /*2、完成字符设备的加载*/
- cdev_init(&(tq2440_button.cdev),&tq2440_fops);
- tq2440_button.cdev.owner= THIS_MODULE;
- ret = cdev_add(&(tq2440_button.cdev),devno,1);
- if(ret)
- {
- printk("Add device error\n");
- return ret;
- }
-
- /*初始化自旋锁*/
- spin_lock_init(&(tq2440_button.lock));
-
- /*修改引用次数值*/
-
- spin_lock(&(tq2440_button.lock));
- /*被打开次数统计*/
- tq2440_button.count = 0;
- /*键值*/
- tq2440_button.key_values = -1;
- spin_unlock(&(tq2440_button.lock));
-
- /*初始化等待队列*/
-
- init_waitqueue_head(&(tq2440_button.read_wait_queue));
-
-
- /*设备的创建,实现设备文件自动创建*/
-
- device_create(tq2440_button.myclass,NULL,devno,NULL,"tq2440_button");
- /*3.获得资源*/
- for(; i< NUM_RESOURCE;++ i)
- {
- /*获得设备的资源*/
- irq_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i);
-
- if(NULL== irq_resource)
- {
- return -ENOENT;
- }
- irqs.pirqs[i]= irq_resource->start;
- /*实现名字的复制操作*/
- //strcpy(tq2440_irqs.name[i],irq_resource->name);
- /*这一句是将指针的地址指向一个具体的地址*/
- irqs.names[i]= irq_resource->name;
- }
- /*将设备的指针指向中断号*/
-
- return 0;
- }
- /*probe函数实现字符设备的初始化操作*/
- static int tq2440_button_remove(struct platform_device*dev)
- {
- printk("The driver found a device be removed from the platform bus\n");
-
- /*注销设备*/
- device_destroy(tq2440_button.myclass,MKDEV(dev_major,0));
- /*字符设备注销*/
- cdev_del(&(tq2440_button.cdev));
- /*注销创建的设备类*/
- class_destroy(&(tq2440_button.myclass));
- /*释放设备号*/
- unregister_chrdev_region(MKDEV(dev_major,0),1);
- return 0;
- }
- /*完成平台总线结构体的设计*/
- static const struct platform_driver tq2440_button_driver=
- {
- /*完成具体设备的初始化操作*/
- .probe = tq2440_button_probe,
- /*完成具体设备的退出操作*/
- .remove = tq2440_button_remove,
- .driver =
- {
- .owner = THIS_MODULE,
- .name = "tq2440_button",
- },
- };
- /*总线设备初始化过程*/
- static int __init platform_driver_init(void)
- {
- int ret;
-
- /*总线驱动注册*/
- ret = platform_driver_register(&tq2440_button_driver);
-
- /*错误处理*/
- if(ret)
- {
- platform_driver_unregister(&tq2440_button_driver);
- return ret;
- }
- return 0;
- }
- static void __exit platform_driver_exit(void)
- {
- /*总线驱动释放*/
- platform_driver_unregister(&tq2440_button_driver);
- }
- /*加载和卸载*/
- module_init(platform_driver_init);
- module_exit(platform_driver_exit);
- /*LICENSE和作者信息*/
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("GP-<gp19861112@yahoo.com.cn>");
应用程序
- #include<stdio.h>
- #include<stdlib.h>
- #include<unistd.h>
- #include<sys/types.h>
- #include<sys/ioctl.h>
- #include<sys/stat.h>
- #include<sys/select.h>
- #include<sys/time.h>
- #include<errno.h>
- int main()
- {
- int buttons_fd;
- int key_value = 0;
-
- /*open函数测试*/
- buttons_fd = open("/dev/tq2440_button",0);
- if(buttons_fd< 0)
- {
- perror("open device buttons\n");
- exit(1);
- }
- while(1)
- {
- fd_set rds;
- int ret;
- FD_ZERO(&rds);
- FD_SET(buttons_fd,&rds);
- /*poll函数测试*/
- ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);
- if(ret< 0)
- {
- perror("select");
- exit(1);
- }
- if(ret== 0)
- {
- printf("Timeout.\n");
- }
- else if(FD_ISSET(buttons_fd,&rds))
- {
- /*read函数测试*/
- int ret = read(buttons_fd,&key_value,sizeof key_value);
- if(ret!= sizeof key_value)
- {
- if(errno!= EAGAIN)
- perror("read buttons\n");
- continue;
- }
- else
- {
- printf("buttons_value:%d\n",key_value+1);
- }
- }
- }
- /*release函数测试*/
- close(buttons_fd);
- return 0;
- }
采用plotform总线实现中断的操作,基本中断的操作。
中断操作设备驱动集合:
之前接触到的字符设备驱动是非常单纯的Linux字符设备驱动,他不具备工程中Linux驱动中的设备与驱动分离思想和设备驱动的分层思想,不具备“总线-设备-驱动”模型的概念。接下来通过分析platform设备驱动模型的搭建过程来看看Linux的设备驱动模型究竟是怎样的?
platform驱动模型搭建:(1)platform核心层:为设备层和驱动层提供注册接口、为设备层和驱动层的匹配提供标准
①搭建总线框架:struct bus_type {
const
char
*name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int
(*match)(struct device *dev, struct device_driver *drv);
//#####
int
(*uevent)(struct device *dev, struct kobj_uevent_env *env);
int
(*probe)(struct device *dev);
int
(*remove)(struct device *dev);
void
(*shutdown)(struct device *dev);
int
(*suspend)(struct device *dev, pm_message_t state);
int
(*suspend_late)(struct device *dev, pm_message_t state);
int
(*resume_early)(struct device *dev);
int
(*resume)(struct device *dev);
struct dev_pm_ops *pm;
struct bus_type_private *p;
//看到这个private就有点C++类中的限定域关键字了,这个类的私有成员
};
总线类实例化:platform总线
1
2
3
4
5
6
7
struct bus_type platform_bus_type = {
.name =
"platform"
,
.dev_attrs = platform_dev_attrs,
.match = platform_match,
//关键成员
.uevent = platform_uevent,
.pm = PLATFORM_PM_OPS_PTR,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
platform_bus_init()
{
.....
error = bus_register(&platform_bus_type);
//注册platform总线的核心工作
.....
}
bus_register(struct bus_type *bus)
{
//创建bus的属性文件
retval = bus_create_file(bus, &bus_attr_uevent);
......
//在/sys/bus/bus->name目录下创建devices目录
priv->devices_kset = kset_create_and_add(
"devices"
, NULL,&priv->subsys.kobj);
....
//在/sys/bus/bus->name目录下创建drivers目录
priv->drivers_kset = kset_create_and_add(
"drivers"
, NULL,&priv->subsys.kobj);
//初始化总线设备\总线驱动链表
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
}
当一个驱动挂接到该总线的时候,该总线的match方法被调用。同样的,当一个设备挂接到该总线时,platform_match也会被调用。也就是说核心层只提供匹配的方法!不会帮他们去匹配,这人生大事要他们自己去完成!
这就好办了,都是挂接到总线上的时候,往后分析时肯定会遇到,先暂时放着,先看看他的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* match against the id table first */
if
(pdrv->id_table)
//看看drv的id_table中是否有现成匹配的设备记录
return
platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return
(strcmp(pdev->name, drv->name) ==
0
);
/* match成功,strcmp返回0,语句逻辑返回1 */
}
②为设备层提供注册API、提供自动匹配接口函数
设备基类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const
char
*init_name;
/* initial name of the device 这个就是传统的bus_id,具体到每一个设备之后当做默认值 */
struct device_type *type;
......
struct bus_type *bus;
/* type of bus device is on */
struct device_driver *driver;
/* which driver has allocated this device */
void
*driver_data;
/* data private to the driver */
void
*platform_data;
/* Platform specific data, device core doesn't touch it */
......
void
(*release)(struct device *dev);
};
1
2
3
4
5
6
7
8
struct platform_device {
const
char
*name;
int
id;
// 硬件设备的象征/代表
struct device dev;
// 由此继承基类
u32 num_resources;
struct resource * resource;
//这个驱动使用的资源
struct platform_device_id *id_entry;
};
platform_device_register(struct platform_device *pdev)
platform_device_add(struct platform_device *pdev)
pdev->dev.bus = &platform_bus_type;
device_add(&pdev->dev);
bus_attach_device(struct device *dev)
device_attach(dev);
bus_for_each_drv()函数的实现:bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
1
2
3
4
5
6
7
8
bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void
*data,
int
(*fn)(struct device_driver *,
void
*))
{
......
while
((drv = next_driver(&i)) && !error)
error = fn(drv, data);
......
}
首先关心他的最后一个形参(*fn),他在注册platform_device时最终被重定向到__device_attach()函数,回调函数的使用在内核源码里边屡见不鲜!因为它可以减少很多重复的代码。
现在分析的焦点转移到__device_attach函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
__device_attach(struct device_driver *drv,
void
*data)
{
struct device *dev = data;
if
(!driver_match_device(drv, dev))
return
0
;
return
driver_probe_device(drv, dev);
//match成功就执行这个函数,他最终调用really_probe()函数
}
driver_match_device(struct device_driver *drv,struct device *dev)
{
return
drv->bus->match ? drv->bus->match(dev, drv) :
1
;
//看到这一句,上面留下的疑问就解决了:原来核心层留下的匹配判断标准match接口就是在这里被调用的!!!好爽!^_^
}
really_probe(struct device *dev, struct device_driver *drv)
{
......
if
(dev->bus->probe)
//如果bus_type结构里边的probe成员有定义就优先调用他的
{
ret = dev->bus->probe(dev);
if
(ret)
goto
probe_failed;
}
else
if
(drv->probe)
//没有就调用匹配到的drv结构里边的probe成员函数
{
ret = drv->probe(dev);
if
(ret)
goto
probe_failed;
}
driver_bound(dev);
//bound是绑定的意思,即将match成功的设备加入驱动的设备链表
......
}
驱动基类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct device_driver {
const
char
*name;
struct bus_type *bus;
struct module *owner;
const
char
*mod_name;
/* used for built-in modules */
int
(*probe) (struct device *dev);
int
(*remove) (struct device *dev);
void
(*shutdown) (struct device *dev);
int
(*suspend) (struct device *dev, pm_message_t state);
int
(*resume) (struct device *dev);
struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p;
};
1
2
3
4
5
6
7
8
9
10
11
struct platform_driver {
int
(*probe)(struct platform_device *);
//通常这个函数要自己去实现
int
(*remove)(struct platform_device *);
void
(*shutdown)(struct platform_device *);
int
(*suspend)(struct platform_device *, pm_message_t state);
int
(*suspend_late)(struct platform_device *, pm_message_t state);
int
(*resume_early)(struct platform_device *);
int
(*resume)(struct platform_device *);
struct device_driver driver;
//继承基类
struct platform_device_id *id_table;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
platform_driver_register(struct platform_driver *drv)
{
/*下面进行一系列的判断,如果派生的platform_driver中没有对特有成员进行初始化,设置成默认的 */
drv->driver.bus = &platform_bus_type;
//指向这个驱动所属的bus类型:platform
if
(drv->probe)
//有重定向
drv->driver.probe = platform_drv_probe;
if
(drv->remove)
//有重定向
drv->driver.remove = platform_drv_remove;
......
return
driver_register(&drv->driver); 【进入分析】
//注册的关键材料是platform_driver->driver->bus:关键是为了注册总线的类型platform_bus_type
}
driver_register(struct device_driver *drv)
{
......
struct device_driver *other;
......
other = driver_find(drv->name, drv->bus);
//在该总线上查找是否有该设备驱动名对应的驱动
if
(other) {
//如果设备已经存在对应的驱动就:出错,驱动已经存在
put_driver(other);
printk(KERN_ERR
"Error: Driver '%s' is already registered, "
"aborting...\n"
, drv->name);
return
-EEXIST;
}
bus_add_driver(drv);
/* 在总线上添加这个驱动,成功的话最终结果:在bus/platform/drivers目录下面生成“name”对应的目录 ,并且会生成 bind module uevent unbind 四个文件*/
......
}
bus_add_driver(struct device_driver *drv)
driver_attach(drv); /* 试图将驱动和设备绑定起来 */
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);//到这里可以非常明显的发现和设备层做的事情非常相似,几乎是对称出现
/* 对总线上的每一个设备都会拿来执行__driver_attach,他在这里被用作回调函数,看看是否匹配,这个函数和__device_attach函数做的事情基本一样这里就不再累述了*/
(2)设备层:主要工作就是把核心层提供的API用起来
1.设置好platform_device结构体成员:主要是name、resource、num_resources、id、dev->release、
2.通过platform_device_register()把这个结构体链入核心层的klist_devices链表
(3)驱动层:同样是把核心层提供的接口函数用起来1.设置好platform_driver结构体成员:probe、remove、driver->name
2.通过platform_driver_register()函数把这个结构体链入核心层的klist_drivers链表
3.实现probe成员函数
4.通常最后才去完成probe函数用到的材料,一般是file_operation结构体成员,这样应用层就可以通过这个接口来操作设备
0 0
- 基于platform总线的中断(按键)字符设备驱动设计
- 基于platform总线的中断(按键)字符设备驱动设计
- 基于platform总线的中断(按键)字符设备驱动设计
- 基于platform总线的中断(按键)字符设备驱动设计
- Linux设备驱动开发基础---字符设备驱动程序开发之基于中断的按键驱动
- 按键中断的platform bus设备和驱动
- 按键中断的platform bus设备和驱动 收藏
- 基于linux驱动设备-platform按键驱动
- 基于platform总线的mini2440的led设备驱动例子
- 基于 platform 总线的设备驱动编写模式:
- 字符设备驱动--中断方式下的按键驱动
- 字符设备驱动程序开发之基于中断的按键驱动加去抖动
- 字符设备驱动之中断按键驱动
- 字符设备驱动程序之中断方式的按键驱动
- Linux字符设备驱动之中断按键
- 字符设备驱动-中断方式操控按键
- 基于platform的ok6410按键中断实验
- 基于platform的ok6410按键中断实验
- c的简单数据组存储
- iOS开发系列--C语言之预处理
- 一篇很全面的freemarker教程
- 15个值得开发人员关注的jQuery开发技巧和心得
- PostCSS一种更优雅、更简单的书写CSS方式
- 基于platform总线的中断(按键)字符设备驱动设计
- spring【7】(spring事务详解)
- Spring MVC 无XML配置入门示例
- TYVJ2018小猫下山
- 区间dp基础(石子归并,括号匹配,整数划分。。。)
- Codeforces Round #343 (Div. 2) D. Babaei and Birthday Cake(dp、BIT)
- The 6th Zhejiang Provincial Collegiate Programming Contest->ProblemF:80ers' Memory
- C++模板简单学习
- 线上使用阿里Druid连接池首次连接MySQL异常问题