一、概念
Platform bus平台总线,是一种虚拟总线,采用总线的模型对设备与驱动进行管理,提高程序的可移植性。
Platform bus平台总线 驱动和设备匹配方法:
1. 通过id_table(芯片型号)进行匹配,但是大多数设备都没有id_table(芯片型号)
2. 通过设备->name 与 驱动->name进行匹配
二、平台设备
平台设备使用struct platform_device来描述: struct platform_device { const char *name; /*设备名*/ int id; /*设备编号,配合设备名使用*/ struct device dev; u32 num_resources; struct resource *resource; /*设备资源*/ } struct resource { 注册平台设备: int platform_device_register(struct platform_device *pdev)
扫描二维码关注公众号,回复:
151183 查看本文章
|
平台驱动使用struct platform_driver 描述: struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); …… } 注册平台驱动: |
四、程序 下载点击打开链接
key_dev.c
/********************************************* *File name :key_dev.c *Author :JerryGou *Date :2017/10/23 *Function :平台总线之按键设备 *********************************************/ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/platform_device.h> #define GPH0CON 0xE0200C00 /*使用的资源*/ struct resource key_resource[] = { [0] = { /*资源1 两个按键*/ .start = GPH0CON, .end = ( GPH0CON + 8 ), //CONFIG寄存器+DATA寄存器共8字节 .flags = IORESOURCE_MEM, /*资源类型-内存*/ }, [1] = { /*资源2 两个中断*/ .start = IRQ_EINT0, .end = IRQ_EINT1, .flags = IORESOURCE_IRQ, /*资源类型-中断*/ }, }; struct platform_device key_device = { .name = "my-key", /*与驱动相匹配的名字*/ .id = 0, /*设备编号ID,为0为空,不用可设置为0*/ .num_resources = 2, /*资源数目,与resource对应*/ .resource = key_resource, /*资源描述结构*/ }; int keydev_init(void) { int ret; /*平台注册*/ ret = platform_device_register(&key_device); return ret; } void keydev_exit(void) { /*平台注销*/ platform_device_unregister(&key_device); } module_init(keydev_init); module_exit(keydev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jerry.Gou"); MODULE_DESCRIPTION("TQ210 button driver");key_dri.c
/********************************************* *File name :key_dri.c *Author :JerryGou *Date :2017/10/24 *Function :平台总线之按键驱动 *********************************************/ #include <linux/module.h> #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/fs.h> #include <linux/io.h> #include <linux/slab.h> #include <asm/uaccess.h> #include <linux/sched.h> #include <linux/platform_device.h> #define GPH0CON 0xE0200C00 #define GPH0DAT 0xE0200C04 struct work_struct *work; unsigned int key_num; /*定时器*/ struct timer_list buttons_timer; /*等待队列*/ wait_queue_head_t key_q; struct resource *res_irq; struct resource *res_mem; unsigned int *key_base; /*open 函数*/ int key_open(struct inode *node, struct file *filp) { return 0; } /*read 函数*/ ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos) { /*进入等待队列睡眠*/ wait_event(key_q, key_num); /*当key_num为true,即当按键按下时唤醒*/ if (copy_to_user(buf, &key_num, 4)) return 4; else return 0; } struct file_operations key_fops = { .open = key_open, .read = key_read, }; struct miscdevice key_miscdev = { /*MISC_DYNAMIC_MINOR代表动态分配次设备号,即由系统自动分配*/ //.minor = 200, .minor = MISC_DYNAMIC_MINOR, /*次设备号*/ .name = "tq210key", /*设备名称*/ .fops = &key_fops, /*文件操作*/ }; static irqreturn_t key_int(int irq, void *dev_id) { //1. 检测是否发生了按键中断 //2. 清除已经发生的按键中断 //3. 挂载(提交)工作 schedule_work(work); return IRQ_HANDLED; } void buttons_timer_function(unsigned long data) { unsigned int key_val = 0; key_val = readw(key_base + 1); if (( key_val & 0x01 ) == 0) { printk("key1 down!\n"); key_num = 0; } if (( key_val & 0x02 ) == 0) { printk("key2 down!\n"); key_num = 2; } /*唤醒进程*/ wake_up(&key_q); } /*定时器初始化*/ void timer_init(void) { /*初始化定时器*/ init_timer(&buttons_timer); /*设置超时函数*/ buttons_timer.function = buttons_timer_function; /*向内核注册一个定时器*/ add_timer(&buttons_timer); } void work_func(struct work_struct *work) { /*启动定时器*/ /*延时 1s/10=100ms */ mod_timer(&buttons_timer, jiffies + HZ / 10); } /*队列初始化*/ void queue_init(void) { //创建工作 work = kmalloc(sizeof(struct work_struct), GFP_KERNEL); INIT_WORK(work, work_func); } void key_hw_init(void) { unsigned int data; data = readw(key_base); data &= ~0xFFFFF; data |= 0xFFFFF; writew(data, key_base); } /*找到设备后对设备进行初始化*/ int key_probe(struct platform_device *pdev) { int ret, size; /*混杂设备驱动注册*/ ret = misc_register(&key_miscdev); if (ret != 0) printk(KERN_WARNING"register fail\n"); /*注册中断处理程序,下降沿产生中断*/ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ret = request_irq(res_irq->start, key_int, IRQF_TRIGGER_FALLING, "tq210key", (void*)1); ret = request_irq(res_irq->end, key_int, IRQF_TRIGGER_FALLING, "tq210key", (void*)2); res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); size = res_mem->end - res_mem->start + 1; key_base = ioremap(res_mem->start, size); /*按键硬件初始化*/ key_hw_init(); /*队列初始化*/ queue_init(); /*定时器初始化*/ timer_init(); /*初始化等待队列*/ init_waitqueue_head(&key_q); return 0; } static int key_remove(struct platform_device *pdev) { /*释放中断*/ free_irq(res_irq->start, (void *)1); free_irq(res_irq->end, (void *)2); /*取消虚拟地址映射*/ iounmap(key_base); /*注销设备*/ misc_deregister(&key_miscdev); return 0; } struct platform_driver key_driver = { .probe = key_probe, //驱动、设备匹配成功后执行该函数,相当于初始化 .remove = key_remove, //驱动注销时执行 .driver = { .owner = THIS_MODULE, .name = "my-key", }, }; static int button_init(void) { int ret; //注册平台设备驱动 ret = platform_driver_register(&key_driver); return ret; } static void button_exit(void) { /*注销平台设备驱动*/ platform_driver_unregister(&key_driver); } module_init(button_init); module_exit(button_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jerry.Gou"); MODULE_DESCRIPTION("TQ210 button driver");