分享
分销 收藏 举报 申诉 / 17
播放页_导航下方通栏广告

类型如何设计平台设备和驱动.docx

  • 上传人:精***
  • 文档编号:2314427
  • 上传时间:2024-05-27
  • 格式:DOCX
  • 页数:17
  • 大小:156.93KB
  • 下载积分:8 金币
  • 播放页_非在线预览资源立即下载上方广告
    配套讲稿:

    如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。

    特殊限制:

    部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。

    关 键  词:
    如何 设计 平台 设备 驱动
    资源描述:
    ______________________________________________________________________________________________________________ 如何设计平台设备和驱动 2.6内核引入了platform机制,能够实现对设备所占用的资源进行统一管理。Platform机制抽象出了platform_device和platform_driver两个核心概念,与此相关的还有一个重要概念就是资源resource。 1.1.1 资源 1. 描述和类型 资源resource是对设备所占用的硬件信息的抽象,目前包括I/O、内存、IRQ、DMA、BUS这5类。在内核中,用resource结构来对资源进行描述。resource结构在<linux/ioport.h>文件中定义,如程序清单2.1所示。 程序清单Error! No text of specified style in document..1 resource数据结构 struct resource { resource_size_t start; /* 资源在CPU上的物理起始地址 */ resource_size_t end; /* 资源在CPU上的物理结束地址 */ const char *name; /* 资源名称 */ unsigned long flags; /* 资源的标志 */ struct resource *parent, *sibling, *child; /* 资源的父亲、兄弟和子资源 */ }; flags通常被用来表示资源的类型,可用的资源类型有IO、MEM、IRQ等,在<linux/ioport.h>中定义,各资源类型和定义如下: #define IORESOURCE_TYPE_BITS 0x00001f00 /*资源类型 */ #define IORESOURCE_IO 0x00000100 #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800 #define IORESOURCE_BUS 0x00001000 2. 资源定义 一个设备的资源定义可以同时包含所占用的多种资源。例如,对于一个既占用内存资源,又占用IRQ中断资源的设备,其资源定义可以如程序清单2.2所示。 程序清单Error! No text of specified style in document..2资源定义实例 #define EMC_CS2_BASE 0x11000000 /* 总线片选地址 */ static struct resource ecm_ax88796b_resource[] = { [0] = { /* 内存资源 */ .start = EMC_CS2_BASE, /* 起始地址 */ .end = EMC_CS2_BASE + 0xFFF, /* 结束地址 */ .flags = IORESOURCE_MEM, /* 资源类型:IORESOURCE_MEM */ }, [1] = { /* IRQ资源 */ .start = IRQ_GPIO_04, .end = IRQ_GPIO_04, .flags = IORESOURCE_IRQ, /* 资源类型:IORESOURCE_IRQ */ } }; 3. 资源获取 定义了一个设备的资源后,需通过特定函数获取才能使用,这些函数在<linux/platform_device.h>文件中定义,一共有3个函数,分别是:platform_get_resource()、platform_get_resource_byname()、platform_get_irq()和platform_get_irq_byname()。 platform_get_resource()函数用于获取指定类型的资源,函数原型如下: extern struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int); dev指向包含资源定义的platform_device结构;type表示将要获取的资源类型;num表示获取资源的数量。返回值为0表示获取失败,成功返回申请的资源地址。 platform_get_resource_byname()则是根据平台设备的设备名称获取指定类型的资源,函数原型如下: extern struct resource *platform_get_resource_byname(struct platform_device *, unsigned int, const char *); 另外,内核还单独提供了获取IRQ的接口函数platform_get_irq(),实际上就是platform_get_resource()获取IORESOURCE_IRQ的封装,方便用户使用。原型如下: int platform_get_irq(struct platform_device *dev, unsigned int num); 获取设备的私有数据,可通过宏platform_get_drvdata实现: #define platform_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev) 实际上是获取_dev->dev->p->driver_data,可参考Error! Reference source not found. device结构的定义。 platform_get_irq_byname()则可根据平台设备名称获取设备的IRQ资源,函数原型如下: extern int platform_get_irq_byname(struct platform_device *, const char *); 在驱动编写中如何实际使用这些函数,下面给出一个代码片段,如程序清单2.3所示。 程序清单Error! No text of specified style in document..3平台资源获取和使用范例 if (!mem){ res = platform_get_resource (pdev, IORESOURCE_MEM, 0); /* 获取内存资源 */ if (!res) { printk("%s: get no resource !\n", DRV_NAME); return -ENODEV; } mem = res->start; } if(!irq) irq = platform_get_irq(pdev, 0); /* 获取IRQ资源 */ if (!request_mem_region (mem, AX88796B_IO_EXTENT, "ax88796b")) { /* 申请IO内存 */ PRINTK (ERROR_MSG, PFX " request_mem_region fail !"); return -EBUSY; } addr = ioremap_nocache(mem, AX88796B_IO_EXTENT); /* 内存映射ioremap */ if (!addr) { ret = -EBUSY; goto release_region; } 该范例演示了内存资源和IRQ资源的获取和使用。特别说明一下内存资源,在定义内存资源的时候,通常使用内存的物理地址,而在驱动中须转换为虚拟地址使用,所以需要进行ioremap操作,而在ioremap之前又需要先申请IO内存,所以在代码中看到的是先使用request_mem_region()函数申请IO内存,然后再通过ioremap_nocache()函数完成内存映射。 1.1.2 平台设备 并不是任何设备都可以抽象成为platform_device。platform_device是在系统中以独立实体出现的设备,包括传统的基于端口的设备、主机到外设的总线以及大部分片内集成的控制器等。这些设备的一个共同点是CPU都可以通过总线直接对它们进行访问。在极少数情况下,一个platform_device可能会经过一小段其它总线,但是它的寄存器依然可以被CPU直接访问。 1. platform_device 用于描述平台设备的数据结构是platform_device,在<linux/platform_device.h>文件中定义,如程序清单2.4所示。 程序清单Error! No text of specified style in document..4 platform_device数据结构 struct platform_device { const char * name; /* 设备名称 */ int id; /* 设备ID */ struct device dev; /* 设备的device数据结构 */ u32 num_resources; /* 资源的个数 */ struct resource * resource; /* 设备的资源 */ const struct platform_device_id *id_entry; /* 设备ID入口 */ /*体系结构相关的附加项*/ struct pdev_archdata archdata; /* 体系结构相关的数据 */ }; name是设备的名称,用于与platform_driver进行匹配绑定,resourse用于描述设备的资源如地址、IRQ等。 2. 分配platform_device结构 注册一个platform_device之前,必须先定义或者通过platform_device_alloc()函数为设备分配一个platform_device结构,platform_device_alloc()函数原型如下: struct platform_device *platform_device_alloc(const char *name, int id); 3. 添加资源 通过platform_device_alloc()申请得到的platform_device结构,必须添加相关资源和私有数据才能进行注册。添加资源的函数是platform_device_add_resources: int platform_device_add_resources(struct platform_device *pdev, const struct resource *res, unsigned int num); 添加私有数据的函数是platform_device_add_data: int platform_device_add_data(struct platform_device *pdev, const void *data, size_t size); 4. 注册和注销platform_device 申请到platform_device结构后,可以通过platform_device_register()往系统注册,platform_device_register()函数原型如下: int platform_device_register(struct platform_device *pdev); platform_device_register()只能往系统注册一个platform_device,如果有多个platform_device,可以用platform_add_devices()一次性完成注册,platform_add_devices()函数原型如下: int platform_add_devices(struct platform_device **devs, int num); 通过platform_device_unregister()可以注销系统的platform_device,platform_device_unregister()函数原型如下: void platform_device_unregister(struct platform_device *pdev); 如果已经定义了设备的资源和私有数据,可以用platform_device_register_resndata()一次性完成数据结构申请、资源和私有数据添加以及设备注册: struct platform_device *__init_or_module platform_device_register_resndata( struct device *parent, const char *name, int id, const struct resource *res, unsigned int num, const void *data, size_t size); platform_device_register_simple()函数是platform_device_register_resndata()函数的简化版,可以一步实现分配和注册设备操作,platform_device_register_simple()函数原型如下: static inline struct platform_device *platform_device_register_simple( const char *name, int id, const struct resource *res, unsigned int num); 实际上就是:platform_device_register_resndata(NULL, name, id, res, num, NULL, 0)。 在<linux/platform_device.h>文件还提供了更多的platform_device相关的操作接口函数,在有必要的时候可以查看并使用。 5. 向系统添加平台设备的流程 向系统添加一个平台设备,可以通过两种方式完成: l 方式1:定义资源,然后定义platform_device结构并初始化;最后注册; l 方式2:定义资源,然后动态分配一个platform_device结构,接着往结构添加资源信息,最后注册。 两种方式归纳如图2.1所示。 图Error! No text of specified style in document..1添加平台设备的方式 1.1.3 平台驱动 1. platform_driver platform_driver是device_driver的封装,提供了驱动的probe和remove方法,也提供了与电源管理相关的shutdown和suspend等方法,如程序清单2.5所示。 程序清单Error! No text of specified style in document..5platform_driver数据结构 struct platform_driver { int (*probe)(struct platform_device *); /* probe方法 */ int (*remove)(struct platform_device *); /* remove方法 */ void (*shutdown)(struct platform_device *); /* shutdown方法 */ int (*suspend)(struct platform_device *, pm_message_t state); /* suspend方法 */ int (*resume)(struct platform_device *); /* resume方法 */ struct device_driver driver; /* 设备驱动 */ const struct platform_device_id *id_table; /* 设备的ID表 */ }; Platform_driver有5个方法: l probe成员指向驱动的探测代码,在probe方法中获取设备的资源信息并进行处理,如进行物理地址到虚拟地址的remap,或者申请中断等操作,与模块的初始化代码不同; l remove成员指向驱动的移除代码,进行一些资源释放和清理工作,如取消物理地址与虚拟地址的映射关系,或者释放中断号等,与模块的退出代码不同; l shutdown成员指向设备被关闭时的实现代码; l suspend成员执行设备挂起时候的处理代码; l resume成员执行设备从挂起中恢复的处理代码。 2. 注册和注销platform_driver 注册和注销platform_driver的函数分别是platform_driver_register()和platform_driver_unregister(),函数原型分别如下: int platform_driver_register(struct platform_driver *drv); void platform_driver_unregister(struct platform_driver *drv); 另外,platform_driver_probe()函数也能完成设备注册,原型如下: int platform_driver_probe(struct platform_driver *driver, int (*probe)(struct platform_device *)); 如果已经明确知道一个设备不支持热插拔,可以在__init断代码中调用platform_driver_probe()函数,以减少运行时对内存的消耗。如程序清单2.6所示代码是<drivers/net/ne.c>中的范例,可以参考。 程序清单Error! No text of specified style in document..6使用platform_driver_probe的范例 int __init init_module(void) { int retval; ne_add_devices(); retval = platform_driver_probe(&ne_driver, ne_drv_probe); if (retval) { if (io[0] == 0) printk(KERN_NOTICE "ne.c: You must supply \"io=0xNNN\"" " value(s) for ISA cards.\n"); ne_loop_rm_unreg(1); return retval; } /* Unregister unused platform_devices. */ ne_loop_rm_unreg(0); return retval; } 注意:在设备驱动模型中已经提到,bus根据驱动和设备的名称寻找匹配的设备和驱动,因此注册驱动必须保证platform_driver的driver.name字段必须和platform_device的name相同,否则无法将驱动和设备进行绑定而注册失败。 1.1.4 平台驱动与普通驱动的差异 基于platform机制编写的驱动与普通字符驱动,只是在框架上有差别,驱动的实际内容是差不多相同的,如果有必要的话,一个普通驱动很容易就可被改写为platform驱动。图2.2是普通字符驱动与平台驱动的框架对照。 图Error! No text of specified style in document..2普通驱动与平台驱动对比 可以看到,将一个普通字符驱动改写为平台驱动,驱动各方法方法的实现以及fops定义都是一样的,不同之处是框架结构发生了变化,资源的申请和释放等代码的位置发生了变化: l 资源申请、设备注册等从普通字符驱动的模块初始化部分移到了平台驱动的probe方法,对于特殊情况,也可以继续放在模块初始化代码中; l 设备注销、资源释放等从普通字符驱动的模块退出代码移到了平台驱动的remove方法。 平台驱动还增加了资源定义和初始化、平台设备和驱动的定义和初始化,以及驱动必要方法的实现等。 平台驱动的模块初始化代码可以很简单,几乎只需简单的调用平台设备注册和注销的接口函数。 1.1.5 平台驱动范例 前面已经提到过,采用platform方式编程,能够很好的将资源与驱动分开,便于程序移植和驱动复用。本节继续以LED为例,用platform方式重新实现LED驱动,实现与第Error! Reference source not found.小节驱动相同的功能。 为了演示资源和驱动分离,本例将驱动分为如下两个模块: n led_platform模块:实现资源定义和platform设备注册; n led_drv模块:通过platform方式实现LED驱动。 在使用的时候,须依次插入led_platform和led_drv,才能生成设备节点。 1. led_platform模块 led_platform模块只有led_platform.c一个文件。该文件实现了LED资源定义,并向系统注册了一个led platform设备,代码如程序清单2.7所示。 程序清单Error! No text of specified style in document..7led_platform.c参考代码 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/device.h> 4 #include <linux/platform_device.h> 5 6 #define GPIO_LED_PIN_NUM 55 /* gpio 1_23 */ 7 8 /* 定义LED资源 */ 9 static struct resource led_resources[] = { 10 [0] = { 11 .start = GPIO_LED_PIN_NUM, 12 .end = GPIO_LED_PIN_NUM, 13 .flags = IORESOURCE_IO, 14 }, 15 }; 16 17 static void led_platform_release(struct device *dev) 18 { 19 return; 20 } 21 22 /* 定义平台设备 */ 23 static struct platform_device led_platform_device = { 24 .name = "led", /* platform_driver中,.name必须与该名字相同 */ 25 .id = -1, 26 .num_resources = ARRAY_SIZE(led_resources), 27 .resource = led_resources, 28 .dev = { 29 /* Device 'led' does not have a release() function, it is broken and must be fixed. */ 30 .release = led_platform_release, 31 .platform_data = NULL, 32 }, 33 }; 34 35 static int __init led_platform_init(void) 36 { 37 int ret; 38 39 ret = platform_device_register(&led_platform_device); 40 if (ret < 0) { 41 platform_device_put(&led_platform_device); 42 return ret; 43 } 44 45 return 0; 46 } 47 48 static void __exit led_platform_exit(void) 49 { 50 platform_device_unregister(&led_platform_device); 51 } 52 53 module_init(led_platform_init); 54 module_exit(led_platform_exit); 55 56 MODULE_LICENSE("GPL"); 57 MODULE_AUTHOR("Chenxibing, linux@"); 2. led_drv模块 led_drv模块由led_drv.c和led_drv.h两个文件组成,其中led_drv.h与第Error! Reference source not found.小节的头文件完全相同,参考Error! Reference source not found.。 led_drv.c的代码如程序清单2.8所示,实现了led的platform驱动,与Error! Reference source not found.相比,设备的fops定义、open、release 和ioctl等方法的定义和实现都相同,仅仅在模块初始化和退出代码的实现有差别,同时增加了platform_driver定义、probe和remove方法。 第128~136行是platform_driver定义和初始化,注意其中的.driver.name必须与platform_device的.name相同,否则无法进行匹配。 第77~117行是驱动probe方法的实现代码,实现Error! Reference source not found.驱动初始化部分的几乎全部功能。在peobe中,通过platform_get_resource()函数从资源中获取需要的IO端口,保存在全局变量led_io中,供驱动的open、release和ioctl等方法使用。 第119~126是驱动remove方法的实现代码,实现Error! Reference source not found.驱动退出部分的几乎全部功能。 程序清单Error! No text of specified style in document..8led_drv.c参考代码 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/version.h> 4 #include <linux/fs.h> 5 #include <linux/cdev.h> 6 #include <linux/device.h> 7 #include <linux/platform_device.h> 8 #include <asm/gpio.h> 9 10 #include "led_drv.h" 11 12 static int major; 13 static int minor; 14 struct cdev *led; /* cdev数据结构 */ 15 static dev_t devno; /* 设备编号 */ 16 static struct class *led_class; 17 static int led_io; /* 用于保存GPIO编号 */ 18 19 #define DEVICE_NAME "led" 20 21 static int led_open(struct inode *inode, struct file *file ) 22 { 23 try_module_get(THIS_MODULE); 24 gpio_direction_output(led_io, 1); 25 return 0; 26 } 27 28 static int led_release(struct inode *inode, struct file *file ) 29 { 30 module_put(THIS_MODULE); 31 gpio_direction_output(led_io, 1); 32 return 0; 33 } 34 35 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) 36 int led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 37 #else 38 static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) 39 #endif 40 { 41 if (_IOC_TYPE(cmd) != LED_IOC_MAGIC) { 42 return -ENOTTY; 43 } 44 45 if (_IOC_NR(cmd) > LED_IOCTL_MAXNR) { 46 return -ENOTTY; 47 } 48 49 switch(cmd) { 50 case LED_ON: 51 gpio_set_value(led_io, 0); 52 break; 53 54 case LED_OFF: 55 gpio_set_value(led_io, 1); 56 break; 57 58 default: 59 gpio_set_value(led_io, 0); 60 break; 61 } 62 63 return 0; 64 } 65 66 struct file_operations led_fops = { 67 .owner = THIS_MODULE, 68 .open = led_open, 69 .release = led_release, 70 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) 71 .unlocked_ioctl = led_ioctl 72 #else 73 .ioctl = led_ioctl 74 #endif 75 }; 76 77 static int __devinitled_probe(struct platform_device *pdev) 78 { 79 int ret; 80 struct resource *res_io; 81 82 res_io = platform_get_resource(pdev, IORESOURCE_IO, 0); /*从设备资源获取IO引脚 */ 83 led_io = res_io->start; 84 85 ret = alloc_chrdev_region(&devno, minor, 1, DEVICE_NAME); /* 从系统获取主
    展开阅读全文
    提示  咨信网温馨提示:
    1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
    2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
    3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
    4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
    5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
    6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

    开通VIP折扣优惠下载文档

    自信AI创作助手
    关于本文
    本文标题:如何设计平台设备和驱动.docx
    链接地址:https://www.zixin.com.cn/doc/2314427.html
    页脚通栏广告

    Copyright ©2010-2026   All Rights Reserved  宁波自信网络信息技术有限公司 版权所有   |  客服电话:0574-28810668    微信客服:咨信网客服    投诉电话:18658249818   

    违法和不良信息举报邮箱:help@zixin.com.cn    文档合作和网站合作邮箱:fuwu@zixin.com.cn    意见反馈和侵权处理邮箱:1219186828@qq.com   | 证照中心

    12321jubao.png12321网络举报中心 电话:010-12321  jubao.png中国互联网举报中心 电话:12377   gongan.png浙公网安备33021202000488号  icp.png浙ICP备2021020529号-1 浙B2-20240490   


    关注我们 :微信公众号  抖音  微博  LOFTER               

    自信网络  |  ZixinNetwork