十八、platform模型
一、概念
platform是一个驱动管理模型,它不是从设计驱动的角度考虑,而是从管理驱动的角度考虑
- platform device:描述硬件信息的部分(硬件不通,信息或资源不同)。
- platform driver:描述软件信息的部分(对硬件资源的操作,保持相对稳定,较少修改)。
- platform bus:是platform driver和platform device的管理者(管理着两条链表)。
二、platform模型的优点
- 方便驱动的移植(争取做到移植时,只修改device,而driver部分基本不变)。
- 内核中绝大部分设备驱动都是基于platform驱动模型的,,方便分析内核驱动源码,更便于理解驱动程序的框架。
- 当platform driver和platform device匹配时,调用driver->probe函数
- platform driver和platform device一般在不同的源文件中。
三、platform dirver实现步骤
1、platform driver的结构体
1 2 3 4 5 6 7 8 9 | struct platform_driver { int (*probe)( struct platform_device *);-----在platform device 和platform driver 的driver的name匹配时会调用该函数 int (* remove )( struct platform_device *);----是probe的反函数 void (*shutdown)( struct platform_device *); int (*suspend)( struct platform_device *, pm_message_t state); int (*resume)( struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; }; |
2、platform driver的注册
1 | int platform_driver_register( struct platform_driver *drv) |
3、platform driver的注销
1 | void platform_driver_unregister( struct platform_driver *drv) |
4、资源的获取
1 2 | struct resource *platform_get_resource( struct platform_device *dev, unsigned int type, unsigned int num) |
功能:获取platform device的资源,
最常用的资源类型有下面几种:
1 2 3 4 | #define IORESOURCE_IO 0x00000100 //GPIO资源 #define IORESOURCE_MEM 0x00000200 //IO内存资源 #define IORESOURCE_IRQ 0x00000400 //中断号资源 #define IORESOURCE_DMA 0x00000800 //DMA资源 |
返回值:成功返回有效的资源结构体地址,失败返回NULL
四、platform模型驱动实例
以内核源码看门狗驱动为例
1、plafrom device
(1)注册platform device
大部分的platform device都会在nxp_cpu_devs_register中使用platform_device_register进行注册
1 2 3 4 | #if defined(CONFIG_NXP_WDT) printk( "mach: add device watchdog\n" ); platform_device_register(&nxp_device_wdt); #endif |
(2) platform结构体
1 2 3 4 5 6 | struct platform_device nxp_device_wdt = { .name = DEV_NAME_WDT, //设备名,资源通过设备名和platform driver匹配,匹配成功后会调用驱动中的probe进行初始化 .id = -1, .num_resources = ARRAY_SIZE(nxp_wdt_resource), .resource = nxp_wdt_resource, //看门狗的资源 }; |
(3) resource
1 2 3 4 | static struct resource nxp_wdt_resource[] = { [0] = DEFINE_RES_MEM(PHY_BASEADDR_WDT, SZ_1K), //内存资源 [1] = DEFINE_RES_IRQ(IRQ_PHY_WDT), //中断资源 }; |
(4)platform device的注销
1 | void platform_device_unregister( struct platform_device *pdev) |
2、platform driver
nxp_wdt.c (drivers\watchdog)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | static struct platform_driver nxp_wdt_driver = {-------platform driver .probe = nxp_wdt_probe,-----------------基于某个驱动模型的初始化函数 . remove = __devexit_p(nxp_wdt_remove),----相当于probe函数的反函数 .shutdown = nxp_wdt_shutdown, .suspend = nxp_wdt_suspend, .resume = nxp_wdt_resume, .driver = { .owner = THIS_MODULE, .name = DEV_NAME_WDT,-----------------用于注册时跟device的name比较 .of_match_table = nxp_wdt_match, }, }; static int __init watchdog_init( void ) { pr_info( "NXP Watchdog Timer, (c) 2014 SLsiAP\n" ); return platform_driver_register(&nxp_wdt_driver);----------注册platform driver } static void __exit watchdog_exit( void ) { platform_driver_unregister(&nxp_wdt_driver);-------注销platform driver } static int __devinit nxp_wdt_probe( struct platform_device *pdev) { 1.获取platform device 的资源(IO内存资源、中断号资源) 2.申请IO内存 3.获取/使能时钟 4.具体硬件相关的操作,停止看门狗、设置定时时间 5.注册看门狗中断 6.其它的资源操作的代码 } |
五、platform实例
使用platform模型实现蜂鸣器打开和关闭。
1、beep_dev.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/ioctl.h> #include <mach/platform.h> #include <mach/soc.h> #include <mach/devices.h> static void beep_release( struct device *dev) { printk(KERN_INFO "beep_release\n" ); } //platform device拥有的资源 //定义了两种资源类型,可以使用IO口,也可以使用io内存 static struct resource beep_res[] = { { .start =0xC001C000, .end = 0xC001C000+0x64+3, //加3是因为最后一个寄存器还有四位 .name = "GPIOC_IOMEM" , .flags = IORESOURCE_MEM, }, { .start =PAD_GPIO_C+14, .end = PAD_GPIO_C+14, .name = "GPIOC_IONUM" , .flags = IORESOURCE_IO, }, }; static struct platform_device beep_pdev = { .name = "beep_pdev_pdrv" , //和驱动匹配的设备名 .id = -1, .num_resources = ARRAY_SIZE(beep_res), //得到的是数组元素的个数 .resource = beep_res, .dev = { .release = beep_release, }, }; static int __init beep_dev_init( void ) { printk(KERN_INFO "beep_dev_init\n" ); //注册平台设备 platform_device_register(&beep_pdev); return 0; } static void __exit beep_dev_exit( void ) { //销毁平台设备 printk(KERN_INFO "beep_dev_exit\n" ); platform_device_unregister(&beep_pdev); } module_init(beep_dev_init); module_exit(beep_dev_exit); MODULE_AUTHOR( "yqf" ); MODULE_DESCRIPTION( "beep device program" ); MODULE_LICENSE( "GPL" ); |
2、beep_drv.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/ioport.h> #include <linux/io.h> #include <linux/uaccess.h> #include <linux/types.h> #include <linux/ioctl.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> static struct resource *beep_mem_res = NULL; static void __iomem *GPIOCBASE = NULL; static void __iomem *GPIOCOUT = NULL; static void __iomem *GPIOCOUTENB= NULL; static void __iomem *GPIOCALTFN0 = NULL; static void __iomem *GPIOCALTFN1 = NULL; #define BEEP_ON _IO('B',0) #define BEEP_OFF _IO('B',1) static int beep_open( struct inode* inode, struct file *filp) { printk(KERN_INFO "beep_open\n" ); return 0; } static int beep_close( struct inode* inode, struct file *filp) { printk(KERN_INFO "beep_close\n" ); return 0; } static ssize_t beep_write( struct file *filp, const char __user *user, size_t size, loff_t *oft) { printk(KERN_INFO "beep_write\n" ); return size; } static long beep_ioctl ( struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { case BEEP_ON: writel(readl(GPIOCOUT)|((0x01<<14)),GPIOCOUT); break ; case BEEP_OFF: writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT); default : return -ENOIOCTLCMD; } return 0; } struct file_operations beep_fops= { .open = beep_open, .release = beep_close, .write = beep_write, .unlocked_ioctl = beep_ioctl, }; static struct miscdevice beep_misc= { .minor = MISC_DYNAMIC_MINOR, .name = "beep_misc" , .fops = &beep_fops, }; static int beep_probe( struct platform_device *pdev) { int ret; printk(KERN_INFO "beep_probe\n" ); misc_register(&beep_misc); if (ret < 0) { printk(KERN_INFO "beep misc register fail\n" ); goto misc_register_err; } //获取IO内存资源 beep_mem_res = platform_get_resource(pdev,IORESOURCE_MEM,0); if (beep_mem_res==NULL) { printk(KERN_INFO "platform_get_resource failed\n" ); ret = -ENODEV; goto platform_get_resource_err; } //映射到虚拟地址 GPIOCBASE = ioremap(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1); if (GPIOCBASE==NULL) { printk(KERN_INFO "ioremap failed" ); ret = -EBUSY; goto ioremap_err; } GPIOCOUT = GPIOCBASE+0x00; GPIOCOUTENB = GPIOCBASE+0x04; GPIOCALTFN0 = GPIOCBASE+0x20; GPIOCALTFN1 = GPIOCBASE+0x24; //beep writel(readl(GPIOCOUTENB)|(0x01<<14),GPIOCOUTENB); writel(readl(GPIOCALTFN0) &(~(0x03<<28)),GPIOCALTFN0); writel(readl(GPIOCALTFN0) |(1<<28),GPIOCALTFN0); writel(readl(GPIOCOUT)&(~(0x01<<14)),GPIOCOUT); return 0; ioremap_err: release_mem_region(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1); platform_get_resource_err: misc_deregister(&beep_misc); misc_register_err: return 0; } static int beep_remove( struct platform_device *pdev) { printk(KERN_INFO "beep_remove\n" ); iounmap(GPIOCBASE); release_mem_region(beep_mem_res->start,beep_mem_res->end-beep_mem_res->start+1); misc_deregister(&beep_misc); return 0; } static struct platform_driver beep_drv={ .probe = beep_probe, . remove = beep_remove, .driver ={ .owner = THIS_MODULE, .name = "beep_pdev_pdrv" , }, }; static int __init beep_init( void ) { printk(KERN_INFO "beep_init\n" ); platform_driver_register(&beep_drv); return 0; } static void __exit beep_exit( void ) { printk(KERN_INFO "beep_exit\n" ); platform_driver_unregister(&beep_drv); } module_init(beep_init); module_exit(beep_exit); MODULE_AUTHOR( "yqf" ); MODULE_DESCRIPTION( "beep platform driver" ); MODULE_LICENSE( "GPL" ); |
3、main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <sys/ioctl.h> #define BEEP_ON _IO('B',0) #define BEEP_OFF _IO('B',1) int main() { int fd; char buff[2]={0}; fd = open( "/dev/beep_misc" ,O_RDWR); if (fd<0) { perror ( "open bepp misc error!" ); } while (1) { ioctl(fd,BEEP_ON); sleep(1); ioctl(fd,BEEP_OFF); sleep(1); } close(fd); } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2019-01-08 计算机中内存、cache和寄存器之间的关系及区别