Linux中总线设备驱动模型及平台设备驱动实例
本文将简要地介绍Linux总线设备驱动模型及其实现方式,并不会过多地涉及其在内核中的具体实现,最后,本文将会以平台总线为例介绍设备和驱动程序的实现过程。
目录:
一、总线设备驱动模型总体介绍及其实现方式
1.总线设备驱动模型总体介绍
2、总线实现方式
(1):描述
(2):注册和卸载
3、设备实现方式
4、驱动实现方式
二、平台总线设备驱动
1、平台设备的实现方式
2、平台驱动的实现方式
3、平台总线驱动与设备匹配机制
三、按键实例
1.按键设备
2.按键驱动
一、总线设备驱动模型总体介绍及其实现方式
1.总线设备驱动模型总体介绍
如果cpu和外设或者外设和外设想要进行通讯,需要将通信的双方挂载到一条总线上,这里的总线可以是具体的总线,如IIC、CAN总线等,也可以是虚拟的总线,如平台总线。随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,从Linux 2.6内核开始提供了全新的设备模型(当然也可以选择不用这样的方式实现)。其示意图如下所示:
总线设备驱动模型可分为总线、设备和驱动三个部分,当将一个设备加入到总线上时,内核会在这条总线上寻找该设备对应的驱动;当将一个驱动加入到一条总线上时,内核会在该总线上寻找与该驱动对应的设备。匹配的规则根据不同类型的总线及设备特征进行定义。
2、总线实现方式
(1):总线的描述
在 Linux 内核中, 总线由 bus_type 结构表示,定义在 <linux/device.h>中
1 struct bus_type { 2 const char *name; /*总线名称*/ 3 int (*match) (struct device *dev, struct 4 device_driver *drv); /*驱动与设备的匹配函数*/ 5 ……… 6 }
其中,match函数的定义为:
int (*match)(struct device * dev, struct device_driver * drv)
设备和驱动的匹配规则在此函数中定义,match函数的职责为:当一个新设备或者新驱动被添加到这个总线时,该函数被调用,用于判断指定的驱动程序是否能处理指定的设备。若可以,则返回非零,若不可以则返回0值。
(2):总线的注册和注销
总线的注册使用如下函数
1 bus_register(struct bus_type *bus)
若成功,新的总线将被添加进系统,并可在/sys/bus 下看到相应的目录。
总线的注销使用:
1 void bus_unregister(struct bus_type *bus)
若成功,则总线将会从系统中删除。
3、设备的实现方式
(1):设备的描述方式
在 Linux内核中, 设备由struct device结构表示。
1 struct device { 2 { 3 const char *init_name; /*设备的名字*/ 4 struct bus_type *bus; /*设备所在的总线*/ 5 ……… 6 }
其中,init_name为设备的名称,*bus指向总线的描述结构。
(2):设备的注册和注销
设备的注册使用如下函数:
1 int device_register(struct device *dev);
当一个设备成功地添加到一条总线上时可以在/sys/bus/xxx_bus/目录下看到相应的设备。
设备的注销使用如下函数:
1 void device_unregister(struct device *dev);
4.驱动实现方式
(1):驱动的描述方式
在 Linux内核中, 驱动由 device_driver结构表示:
1 struct device_driver { 2 { 3 const char *name; /*驱动名称*/ 4 struct bus_type *bus; /*驱动程序所在的总线*/ 5 int (*probe) (struct device *dev);/*设备匹配成功后调用的函数*/ 6 ……… 7 }
其中,*name为驱动的名称,可以作为一种设备和驱动匹配的条件;*bus指向所要加入的总线的描述结构;*probe函数指针指向一个操作函数,该函数会在设备和驱动匹配成功之后(即match函数返回为1时)被调用,一般用来完成对设备的初始化操作。
(2):驱动的注册和注销
驱动的注册使用如下函数
1 int driver_register(struct device_driver *drv);
驱动的注销使用:
1 void driver_unregister(struct device_driver *drv);
二、平台总线设备驱动实例
平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线(已经在Linux内核中实现,是上文介绍的总线的一个实例),其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。平台总线的开发主要包括平台设备和平台驱动两个部分的实现,其开发流程图如下所示:
1.设备的实现方式
(1):平台设备的描述方式
平台设备使用struct platform_device来描述:
1 struct platform_device { 2 const char *name; /*设备名*/ 3 int id; /*设备编号,配合设备名使用*/ 4 struct device dev; 5 u32 num_resources; 6 struct resource *resource; /*设备资源*/ 7 }
其中,*name为设备名,*resource指针指向该平台设备所占有的资源,其用struct resource 结构来描述:
1 struct resource { 2 resource_size_t start;/*资源的起始地址*/ 3 resource_size_t end;/*资源的结束地址*/
4 const char *name;
5 unsigned long flags; /*资源的类型*/
6 struct resource *parent, *sibling, *child; 7 };
(2):平台设备的注册和注销
注册平台设备,使用函数:
1 int platform_device_register(struct platform_device *pdev);
注销平台设备,使用函数:
1 int platform_device_unregister(struct platform_device *pdev);
2.平台驱动的实现方式
(1):平台驱动的描述方式
平台驱动使用struct platform_driver 描述:
1 struct platform_driver { 2 int (*probe)(struct platform_device *); 3 int (*remove)(struct platform_device *); 4 …… 5 }
其中,*probe函数指针指向设备处理函数,当平台设备和平台驱动匹配成功时被调用。
(1):平台驱动的注册和注销
平台驱动注册使用函数:
1 int platform_driver_register(struct platform_driver *);
平台驱动注销使用函数:
1 int platform_driver_unregister(struct platform_driver *);
3.平台总线驱动与设备的匹配机制
平台总线驱动和设备采用名称是否相同的方式进行匹配,在平台总线的match函数中对驱动和设备的名称进行比较,若两者名称相同则match函数返回为非0,否则返回为0。另外,平台设备一般直接载入内核进行初始化而不通过模块的方式进行加载(平台设备可以看成是对应于具体设备的资源的集合,将设备和驱动分开有利于程序的移植)。
三、按键实例
1.平台按键的定义和注册
1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/interrupt.h> 4 #include <linux/platform_device.h> 5 6 MODULE_LICENSE("GPL"); 7 8 #define GPFCON 0x56000050 9 //所用的资源描述 10 static struct resource key_resource[] = { 11 [0] = { 12 .start = GPFCON, 13 .end = GPFCON + 8, 14 .flags = IORESOURCE_MEM, 15 }, 16 [1] = { 17 .start = IRQ_EINT0, 18 .end = IRQ_EINT2, 19 .flags = IORESOURCE_IRQ, 20 }, 21 }; 22 23 struct platform_device key_device = { 24 .name = "my-key", 25 .id = 0, 26 .num_resources = ARRAY_SIZE(key_resource), 27 .resource = key_resource, 28 }; 29 30 static int button_init() 31 { 32 platform_device_register(&key_device); 33 34 return 0; 35 } 36 37 38 static void button_exit() 39 { 40 platform_device_unregister(&key_device); 41 } 42 43 44 module_init(button_init); 45 module_exit(button_exit);
2.平台驱动的定义和注册
1 #include <linux/module.h> 2 #include <linux/init.h> 3 #include <linux/miscdevice.h> 4 #include <linux/interrupt.h> 5 #include <linux/io.h> 6 #include <linux/fs.h> 7 #include <asm/uaccess.h> 8 #include <linux/platform_device.h> 9 10 MODULE_LICENSE("GPL"); 11 12 struct work_struct *work; 13 14 struct timer_list buttons_timer; 15 16 unsigned int key_num = 0; 17 18 wait_queue_head_t key_q; 19 20 struct resource *res; 21 struct resource *res_irq; 22 unsigned int *key_base; 23 24 void work_func(struct work_struct *work) 25 { 26 mod_timer(&buttons_timer, jiffies + (HZ /10)); 27 } 28 29 void buttons_timer_function(unsigned long data) 30 { 31 unsigned int key_val; 32 33 key_val = readw(key_base+1)&0x1; 34 if (key_val == 0) 35 key_num = 4; 36 37 key_val = readw(key_base+1)&0x4; 38 if (key_val == 0) 39 key_num = 3; 40 41 wake_up(&key_q); 42 } 43 44 45 irqreturn_t key_int(int irq, void *dev_id) 46 { 47 //1. 检测是否发生了按键中断 48 49 //2. 清除已经发生的按键中断 50 51 //3. 提交下半部 52 schedule_work(work); 53 54 //return 0; 55 return IRQ_HANDLED; 56 57 } 58 59 void key_hw_init() 60 { 61 unsigned short data; 62 63 data = readw(key_base); 64 data &= ~0b110011; 65 data |= 0b100010; 66 67 writew(data,key_base); 68 } 69 70 71 int key_open(struct inode *node,struct file *filp) 72 { 73 return 0; 74 } 75 76 ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) 77 { 78 wait_event(key_q,key_num); 79 80 copy_to_user(buf, &key_num, 4); 81 82 key_num = 0; 83 84 return 4; 85 } 86 87 struct file_operations key_fops = 88 { 89 .open = key_open, 90 .read = key_read, 91 }; 92 93 struct miscdevice key_miscdev = { 94 .minor = 200, 95 .name = "key", 96 .fops = &key_fops, 97 }; 98 99 int key_probe(struct platform_device *pdev) 100 { 101 int ret,size; 102 103 104 ret = misc_register(&key_miscdev); 105 106 if (ret !=0) 107 printk("register fail!\n"); 108 109 //注册中断处理程序 110 111 res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 112 113 request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4); 114 request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3); 115 116 //按键初始化 117 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 118 size = (res->end - res->start) + 1; 119 key_base = ioremap(res->start, size); 120 121 key_hw_init(); 122 123 //. 创建工作 124 work = kmalloc(sizeof(struct work_struct),GFP_KERNEL); 125 INIT_WORK(work, work_func); 126 127 /* 初始化定时器 */ 128 init_timer(&buttons_timer); 129 buttons_timer.function = buttons_timer_function; 130 131 /* 向内核注册一个定时器 */ 132 add_timer(&buttons_timer); 133 134 /*初始化等待队列*/ 135 init_waitqueue_head(&key_q); 136 137 return 0; 138 } 139 140 int key_remove(struct platform_device *dev) 141 { 142 free_irq(res_irq->start, (void *)4); 143 free_irq(res_irq->end, (void *)3); 144 145 iounmap(key_base); 146 misc_deregister(&key_miscdev); 147 return 0; 148 } 149 150 static struct platform_driver key_driver = { 151 .probe = key_probe, 152 .remove = key_remove, 153 .driver = { 154 .owner = THIS_MODULE, 155 .name = "my-key", 156 }, 157 }; 158 159 static int button_init() 160 { 161 return platform_driver_register(&key_driver); 162 } 163 164 165 static void button_exit() 166 { 167 platform_driver_unregister(&key_driver); 168 } 169 170 171 module_init(button_init); 172 module_exit(button_exit);