平台总线驱动设计
2015-08-30 15:13
405 查看
Linux系统既支持实际的总线如usb总线,pci总线,也支持虚拟总线。平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。
总线设备一个很关键的函数是匹配函数,平台总线的匹配函数为platform_match(不同版本的Linux系统函数实现有差别)
若驱动程序带有ip_table,则根据ip_table进行匹配;但一般驱动程序都不带ip_table,此时根据设备名称与驱动名称进行匹配。
①定义平台设备
②注册平台设备
③定义平台驱动
④注册平台驱动
由于一个设备可以具备多个设备资源,如基地址、中断号等,所以利用该结构体定义设备时一般使用结构数组表示该成员。
struct resource结构体如下:
注册平台设备:int platform_device_register(struct platform_device *pdev)
注销平台设备:void platform_device_unregister(struct platform_device *pdev)
在平台总线上挂载按键设备:
probe为函数指针,当驱动与设备匹配成功时总线就调用probe指针指向的函数;
remove为函数指针,当与该驱动匹配的设备从总线上移除时总线就调用remove指针指向的函数;
driver结构成员中含有该平台驱动的名称,初始化时需要与设备的名字一致。
注册平台驱动:int platform_driver_register(struct platform_driver *)
注销平台驱动:void platform_driver_unregister(struct platform_driver *)
在编写驱动程序时,还会用到函数platform_get_resource(),使用该函数可以得到设备的信息。
平台总线驱动模型的按键驱动程序:
总线设备一个很关键的函数是匹配函数,平台总线的匹配函数为platform_match(不同版本的Linux系统函数实现有差别)
static int 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); /* try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0); }
若驱动程序带有ip_table,则根据ip_table进行匹配;但一般驱动程序都不带ip_table,此时根据设备名称与驱动名称进行匹配。
开发流程
通过平台总线开发设备驱动流程为:①定义平台设备
②注册平台设备
③定义平台驱动
④注册平台驱动
平台设备
平台设备使用struct platform_device来描述:struct platform_device { const char *name; /*设备名*/ int id; /*设备编号,配合设备名使用*/ struct device dev; u32 num_resources;/*设备资源的数量*/ struct resource *resource; /*设备资源*/ }
由于一个设备可以具备多个设备资源,如基地址、中断号等,所以利用该结构体定义设备时一般使用结构数组表示该成员。
struct resource结构体如下:
struct resource { resource_size_t start;/*资源开头*/ resource_size_t end;/*资源结尾*/ const char *name; unsigned long flags; /*资源的类型,表明该资源是中断号,基地址或其它类型*/ struct resource *parent, *sibling, *child; };
注册平台设备:int platform_device_register(struct platform_device *pdev)
注销平台设备:void platform_device_unregister(struct platform_device *pdev)
在平台总线上挂载按键设备:
#include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/platform_device.h> MODULE_LICENSE("GPL"); #define GPFCON 0x56000050 static struct resource key_resource[] = { [0] = { .start = GPFCON, .end = GPFCON + 8, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_EINT0, .end = IRQ_EINT2, .flags = IORESOURCE_IRQ, }, }; struct platform_device key_device = { .name = "my-key", .id = 0, .num_resources = ARRAY_SIZE(key_resource), .resource = key_resource, }; static int button_init() { platform_device_register(&key_device); return 0; } static void button_exit() { platform_device_unregister(&key_device); } module_init(button_init); module_exit(button_exit);
平台驱动
平台驱动使用struct platform_driver 描述:struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); struct device_driver driver; … }
probe为函数指针,当驱动与设备匹配成功时总线就调用probe指针指向的函数;
remove为函数指针,当与该驱动匹配的设备从总线上移除时总线就调用remove指针指向的函数;
driver结构成员中含有该平台驱动的名称,初始化时需要与设备的名字一致。
注册平台驱动:int platform_driver_register(struct platform_driver *)
注销平台驱动:void platform_driver_unregister(struct platform_driver *)
在编写驱动程序时,还会用到函数platform_get_resource(),使用该函数可以得到设备的信息。
struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num) { int i; for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; if (type == resource_type(r) && num-- == 0) return r; } return NULL; }
平台总线驱动模型的按键驱动程序:
#include <linux/module.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/platform_device.h> MODULE_LICENSE("GPL"); struct work_struct *work; struct timer_list buttons_timer; unsigned int key_num = 0; wait_queue_head_t key_q; struct resource *res; struct resource *res_irq; unsigned int *key_base; void work_func(struct work_struct *work) { mod_timer(&buttons_timer, jiffies + (HZ /10)); } void buttons_timer_function(unsigned long data) { unsigned int key_val; key_val = readw(key_base+1)&0x1; if (key_val == 0) key_num = 4; key_val = readw(key_base+1)&0x4; if (key_val == 0) key_num = 3; wake_up(&key_q); } irqreturn_t key_int(int irq, void *dev_id) { //1. 检测是否发生了按键中断 //2. 清除已经发生的按键中断 //3. 提交下半部 schedule_work(work); //return 0; return IRQ_HANDLED; } void key_hw_init() { unsigned short data; data = readw(key_base); data &= ~0b110011; data |= 0b100010; writew(data,key_base); } int key_open(struct inode *node,struct file *filp) { return 0; } ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) { wait_event(key_q,key_num); copy_to_user(buf, &key_num, 4); key_num = 0; return 4; } struct file_operations key_fops = { .open = key_open, .read = key_read, }; struct miscdevice key_miscdev = { .minor = 200, .name = "key", .fops = &key_fops, }; int key_probe(struct platform_device *pdev) { int ret,size; ret = misc_register(&key_miscdev); if (ret !=0) printk("register fail!\n"); //注册中断处理程序 res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4); request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3); //按键初始化 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); size = (res->end - res->start) + 1; key_base = ioremap(res->start, size); key_hw_init(); //. 创建工作 work = kmalloc(sizeof(struct work_struct),GFP_KERNEL); INIT_WORK(work, work_func); /* 初始化定时器 */ init_timer(&buttons_timer); buttons_timer.function = buttons_timer_function; /* 向内核注册一个定时器 */ add_timer(&buttons_timer); /*初始化等待队列*/ init_waitqueue_head(&key_q); return 0; } int key_remove(struct platform_device *dev) { free_irq(res_irq->start, (void *)4); free_irq(res_irq->end, (void *)3); iounmap(key_base); misc_deregister(&key_miscdev); return 0; } static struct platform_driver key_driver = { .probe = key_probe, .remove = key_remove, .driver = { .owner = THIS_MODULE, .name = "my-key", }, }; static int button_init() { return platform_driver_register(&key_driver); } static void button_exit() { platform_driver_unregister(&key_driver); } module_init(button_init); module_exit(button_exit);
相关文章推荐
- 在Ubuntu中安装Eclipse与创建桌面快捷方式
- 关于windows7开机黑屏的解决办法
- Android Studio 安装后无法打开的问题
- Set up Caffe on Ubuntu14.04 64bit+NVIDIA GTX970M+CUDA7.0
- 图的割点
- Generate Parentheses
- HDU 5423 Rikka with Tree(水题)
- Tomcat简介、安装以及启动
- STL源码剖析 [容器](八)[priority_queue]
- #1005 Number Sequence
- 计算机组成原理四:存储器
- TreeSet
- XML的优势及应用领域
- Android网络编程之使用post方式提交数据
- 算法:并查集
- leetcode 232: Implement Queue using Stacks
- Android-手势识别(普通手势识别:上 下 左 右 ;自定义手势识别:对号,错号 等)
- django 1.8 官方文档翻译: 3-2-3 TemplateResponse 和 SimpleTemplateResponse
- win10系统经常打开程序无响应该怎么办?
- 单例模式