1. 平台总线(Platform bus)是linux2.6内核加入的一种虚拟总线,其优势在于采用了总线的模型对设备(没有挂到真实总线的设备)与驱动进行了管理,这样提高了程序的可移植性。
2. 平台总线开发设备驱动流程
(1)定义平台设备
(2)注册平台设备
(3)定义平台驱动
(4)注册平台驱动
3. 平台设备使用struct platform_device来描述
struct platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; };
① name:设备名
② id:设备ID:配合设备名使用,以区分不同设备
③ dev:
④ num_resources:硬件资源个数
⑤ resource:硬件资源
struct resource { resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child; };
(1) 注册平台设备:int platform_device_register(struct platform_device *pdev);
(2) 注销平台设备:void platform_device_unregister(struct platform_device *pdev);
4. 平台驱动使用struct platform_driver描述
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; };
(1)平台驱动注册: int platform_driver_register(struct platform_driver *)
(2)平台驱动注销:void platform_driver_unregister(struct platform_driver *)
5. 简单示例
① PlatformKeyDevice.c
#include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/platform_device.h> MODULE_LICENSE("GPL"); #define GPGCON 0x56000060 void key_release(struct device *dev) { printk("dev->init_name = %s release! ", dev->init_name); } static struct resource key_resource[] = { [0] = { .start = GPGCON, .end = GPGCON + 8, .flags = IORESOURCE_MEM, }, [1] = { .start = IRQ_EINT8, .end = IRQ_EINT11, .flags = IORESOURCE_IRQ, }, [2] = { .start = IRQ_EINT13, .end = IRQ_EINT14, .flags = IORESOURCE_IRQ, }, [3] = { .start = IRQ_EINT15, .end = IRQ_EINT19, .flags = IORESOURCE_IRQ, }, }; struct platform_device key_device = { .name = "my-key", .id = 0, .dev = { .init_name = "init_key", .release = key_release, }, .num_resources = ARRAY_SIZE(key_resource), .resource = key_resource, }; static int button_init(void) { platform_device_register(&key_device); return 0; } static void button_exit(void) { platform_device_unregister(&key_device); } module_init(button_init); module_exit(button_exit);
② PlatformKeyDriver.c
#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 timer_list buttons_timer; unsigned int key_num = 0; wait_queue_head_t key_wait_queue; unsigned int *key_base = NULL; void buttons_timer_function(unsigned long data) { printk("keys_timer_function "); key_num = 0; printk("data = %lx ", data); switch(data) { case IRQ_EINT8: key_num = 1; break; case IRQ_EINT11: key_num = 2; break; case IRQ_EINT13: key_num = 3; break; case IRQ_EINT14: key_num = 4; break; case IRQ_EINT15: key_num = 5; break; case IRQ_EINT19: key_num = 6; break; default: break; } printk("key_num = %d ", key_num); wake_up(&key_wait_queue); } irqreturn_t key_int(int irq, void *dev_id) { //1. 检测是否发生了按键中断 //2. 清除已经发生的按键中断 //3. 提交下半部 buttons_timer.data = irq; mod_timer(&buttons_timer, jiffies + (HZ /10)); //return 0; return IRQ_HANDLED; } void key_hw_init(void) { unsigned int config_data; if(key_base != NULL) { config_data = readl(key_base); config_data &= 0b001111110000001100111100; config_data |= 0b100000001010100010000010; writel(config_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_wait_queue, 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; struct resource *resources; struct resource *resources_irq; ret = misc_register(&key_miscdev); if (ret != 0) printk("register fail! "); //注册中断处理程序 resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); request_irq(resources_irq->start, key_int, IRQF_TRIGGER_LOW, "key1", 0); request_irq(resources_irq->end, key_int, IRQF_TRIGGER_LOW, "key2", 0); resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1); request_irq(resources_irq->start, key_int, IRQF_TRIGGER_LOW, "key3", 0); request_irq(resources_irq->end, key_int, IRQF_TRIGGER_LOW, "key4", 0); resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 2); request_irq(resources_irq->start, key_int,IRQF_TRIGGER_LOW, "key5", 0); request_irq(resources_irq->end, key_int,IRQF_TRIGGER_LOW, "key6", 0); //按键初始化 resources = platform_get_resource(pdev, IORESOURCE_MEM, 0); size = (resources->end - resources->start) + 1; key_base = ioremap(resources->start, size); key_hw_init(); /* 初始化定时器 */ init_timer(&buttons_timer); buttons_timer.function = buttons_timer_function; /* 向内核注册一个定时器 */ add_timer(&buttons_timer); /*初始化等待队列*/ init_waitqueue_head(&key_wait_queue); return 0; } int key_remove(struct platform_device *pdev) { struct resource *resources_irq; int i = 0; printk("PlatformKeyDriver: key_remove! "); for(i = 0; i <= 2; i++) { resources_irq = platform_get_resource(pdev, IORESOURCE_IRQ, i); free_irq(resources_irq->start, 0); free_irq(resources_irq->end, 0); } 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(void) { return platform_driver_register(&key_driver); } static void button_exit(void) { platform_driver_unregister(&key_driver); } module_init(button_init); module_exit(button_exit);