17.平台总线程序设计
平台总线程序设计
一、平台总线概述
平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备与驱动进行了管理,这总线的模型对设备与驱动进行了管理,这样提高了程序的可移植性。
通过平台总线机制开发设备驱动的流程如图:
平台总线的结构:platform_bus_type:
1 struct bus_type platform_bus_type = { 2 .name = "platform", 3 .dev_attrs = platform_dev_attrs, 4 .match = platform_match, 5 .uevent = platform_uevent, 6 .pm = &platform_dev_pm_ops, 7 };
该结构中,最重要的是我们的匹配函数platform_match:
1 static int platform_match(struct device *dev, struct device_driver *drv) 2 { 3 struct platform_device *pdev = to_platform_device(dev); 4 struct platform_driver *pdrv = to_platform_driver(drv); 5 6 /* Attempt an OF style match first */ 7 if (of_driver_match_device(dev, drv)) 8 return 1; 9 10 /* Then try to match against the id table */ 11 if (pdrv->id_table) 12 return platform_match_id(pdrv->id_table, pdev) != NULL; 13 14 /* fall-back to driver name match */ 15 return (strcmp(pdev->name, drv->name) == 0); 16 }
在匹配函数里,有我们熟悉的代码:总线设备驱动程序学习中使用过的
二、平台设备
平台设备使用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。设备的资源,例如中断号,寄存器.....都是资源。这些就放到resource这个结构里:
其中struct resource *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 };
1.1注册和注销平台设备
注册平台设备,使用函数:
int platform_device_register(struct platform_device *pdev)
注销平台设备,使用函数:
int platform_device_unregister(struct platform_device *pdev)
使用实例:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/interrupt.h> 5 #include <linux/of_platform.h> 6 7 MODULE_LICENSE("GPL"); 8 9 #define GPNCON 0x7f008830 10 11 //定义资源 12 struct resource key_resource[] ={ 13 [0] = { //第一项资源 14 .start = GPNCON, 15 .end = GPNCON+8, 16 .flags = IORESOURCE_MEM, 17 }, 18 [1] = { //第二项资源,中断号 19 .start = IRQ_EINT(0), 20 .end = IRQ_EINT(1), 21 .flags = IORESOURCE_IRQ, 22 }, 23 }; 24 25 //定义平台设备结构 26 struct platform_device key_device = { 27 .name = "my-key", 28 .id = 0, 29 .num_resources = 2, 30 .resource = key_resource, 31 }; 32 33 int keydev_init(void) 34 { 35 int ret; 36 //平台设备的注册 37 ret = platform_device_register(&key_device); 38 return ret; 39 } 40 41 void keydev_exit(void) 42 { 43 platform_device_unregister(&key_device); 44 } 45 46 module_init(keydev_init); 47 module_exit(keydev_exit);
执行结果如下:
三、平台驱动
平台驱动使用struct platform_driver 描述:
1 struct platform_driver { 2 int (*probe)(struct platform_device *); 3 int (*remove)(struct platform_device *); 4 void (*shutdown)(struct platform_device *); 5 int (*suspend)(struct platform_device *, pm_message_t state); 6 int (*resume)(struct platform_device *); 7 struct device_driver driver; 8 const struct platform_device_id *id_table; 9 };
其中probe函数是当驱动找到对应的设备后要执行的函数,remove函数是当设备移除是要调用的函数。
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 <linux/slab.h> 8 #include <linux/uaccess.h> 9 #include <linux/platform_device.h> 10 #include <linux/sched.h> 11 12 MODULE_LICENSE("GPL"); 13 14 struct work_struct *work1; 15 struct timer_list key_timer; 16 17 unsigned int *key_base; 18 struct resource *res_irq; 19 struct resource *res_mem; 20 21 unsigned int key_num = 0; 22 wait_queue_head_t key_q; 23 24 void work1_func(struct work_struct *work) 25 { 26 mod_timer(&key_timer,jiffies + (HZ/10)); //启动定时器 27 //100毫秒超时=HZ/10,HZ=1秒。jiffies是系统当前时间 28 } 29 30 void key_timer_func(unsigned long data) //定时器超时函数 31 { 32 unsigned int key_val; 33 //超时的时候,就要读取data 34 key_val = readw(key_base+1) &0x01; 35 if(key_val==0) 36 key_num = 1; 37 38 key_val = readw(key_base+1) &0x02; 39 if(key_val==0) 40 key_num = 2; 41 42 //当有数据的时候,需要唤醒 43 wake_up(&key_q); 44 } 45 46 irqreturn_t key_int(int irp,void *dev_id) 47 { 48 //1.检测是否发生了按键中断 49 //2.清除已经发生的按键中断 50 //前面的都是硬件相关的工作,必须在中断里面执行 51 //下面是硬件无关的工作,我们把它放到到中断以外的work1_func函数去处理。 52 //3.提交下半部 53 schedule_work(work1); 54 return IRQ_HANDLED; 55 } 56 57 void key_hw_init(void) //硬件初始化 58 { 59 unsigned short data; 60 61 data = readw(key_base); 62 data &= ~0xfff; 63 data |= 0xaaa; 64 writew(data,key_base); 65 } 66 67 int key_open(struct inode *node,struct file *filp) 68 { 69 return 0; 70 } 71 72 ssize_t key_read (struct file *filp, char __user *buf, size_t size, loff_t *pos) 73 { 74 wait_event(key_q,key_num); //进入睡眠 75 printk(KERN_EMERG"key num is%d\n",key_num); 76 copy_to_user(buf,&key_num,4); //返回内核的给用户 77 key_num = 0; 78 return 4; 79 } 80 81 struct file_operations key_fops = 82 { 83 .open = key_open, 84 .read = key_read, 85 }; 86 87 struct miscdevice key_miscdev = 88 { 89 .minor = 200, //次设备号 90 .name = "key", 91 .fops = &key_fops, 92 }; 93 94 static int key_probe(struct platform_device *pdev) 95 { 96 int ret; 97 int size; 98 ret = misc_register(&key_miscdev); //1注册混杂设备 99 if(ret!=0) 100 printk(KERN_EMERG"register fail!\n"); 101 102 //获取资源信息 103 res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0); 104 105 request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //注册中断处理程序 106 request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //注册中断处理程序 107 108 res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0); 109 size = res_mem->end - res_mem->start+1; 110 key_base = ioremap(res_mem->start,size); 111 112 //按键硬件初始化 113 key_hw_init(); /*这个函数里面会用到上面获取到的资源信息所以必须放在获取资源之后执行*/ 114 115 //创建工作1 116 work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL); 117 INIT_WORK(work1,work1_func); 118 119 //初始化内核定时器 120 init_timer(&key_timer); 121 key_timer.function = key_timer_func; 122 123 //注册定时器 124 add_timer(&key_timer); 125 126 //初始化等待队列 127 init_waitqueue_head(&key_q); 128 return ret; 129 } 130 131 static int key_remove(struct platform_device *pdev) 132 { 133 return misc_deregister(&key_miscdev); //注销混杂设备 134 } 135 136 struct platform_driver key_driver ={ 137 .probe = key_probe, 138 .remove = key_remove, 139 .driver = { 140 .name = "my-key", 141 }, 142 }; 143 144 //驱动程序初始化 145 static int button_init(void) 146 { 147 return platform_driver_register(&key_driver);; 148 } 149 150 static void button_exit(void) 151 { 152 platform_driver_unregister(&key_driver); 153 } 154 155 module_init(button_init); 156 module_exit(button_exit);
运行结果如下:
这里面遇到最纠结的问题就是:刚开始的时候将硬件初始化放在了平台总线的前面结果只要想运行设备驱动成和设备程序就会出现段错误,之所以这样是因为没有考虑硬件初始化的时候会用到获取资源信息里面的内容。一下是正确的初始化过程
1 //获取资源信息 2 res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0); 3 4 request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //注册中断处理程序 5 request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"6410key",0); //注册中断处理程序 6 7 res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0); 8 size = res_mem->end - res_mem->start+1; 9 key_base = ioremap(res_mem->start,size); 10 11 //按键硬件初始化 12 key_hw_init(); /*这个函数里面会用到上面获取到的资源信息所以必须放在获取资源之后执行*/ 13