版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、总线设备驱动模型主要包含总线、设备、驱动三个部分,总线可以是一条真实存在的总 线,例如USB、I2C等典型的设备。但是对于一些设备(内部的设备)可能没有现成的总线。 Linux 2.6内核中引入了总线设备驱动模型。总线设备驱动模型与之前的三类驱动(字符、块 设备、网络设备)没有必然的联系。设备只是搭载到了总线中。在linux内核中假设存在一 条虚拟总线,称之为platform总线。platform总线相比与常规的总线模型其优势主要是 platform总线是由内核实现的,而不用自己定义总线类型,总线设备来加载总线。platform 总线是内核已经实现好的。只需要添加相应的platform dev
2、ice和platform driver。具体的实 现过程主要包括如下的过程:两者的工作顺序是先定义platform_device -注册platform_device-,再定义 platform_driver- 注册 platform_driver。整体而言只需要完成两个步骤,也就是设备的实现和驱动的实现,每一个实现都包括相 关结构体的定义和注册。platform_device 注册需要注意的是platform_device实质上是经过处理过的设备,在platform_device结构体 中存在一个设备结构体,与之前的设备存在差别的是引入了设备资源。这些设备资源就能实 现对设备寄存器,中断等资
3、源的访问。平台设备的基本结构体如下: struct platform_device /*设备名*/const char * name;/*设备ID号*/int id;/*结构体包含一个具体的device结构体*/struct device dev;/*资源的数量*/u32num_resources;/*资源结构体,用来保存硬件的资源*/struct resource * resource;/*平台设备的ID*/struct platform_device_id *id_entry;其中struct device和struct resource是重要的结构体。struct device在总线设备
4、驱动模型中 已经提到了。这次讨论一下struct resource0 struct resource /*资源的起始值,如果是地址,那么是物理地址,不是虚拟地址*/resource_size_t start;/*资源的结束值,如果是地址,那么是物理地址,不是虚拟地址7resource_size_t end;/*资源名*/const char *name;/*资源的标示,用来识别不同的资源*/unsigned long flags;/*资源指针,可以构成链表*/struct resource *parent, *sibling, *child; platform_device的注册很简单,只需要
5、在设备的初始化函数中首先定义相应的设备,通常 采用函数 platform_device *platform_device_alloc(const char *name, int id)动态申请,通常 name就是需要申请的设备名,而id为-1。然后采用int platform_device_add(struct platform_device *pdev)或者int platform_device_register(struct platform_device *pdev)注册定义好的设备即可。同样在退出函数中释放注册好的设备即可,可以采用函数:void platform_device_unr
6、egister(struct platform_device *pdev。然后一个平台设备就完成了,不需要像自己实现模型时定义相关的文件属性等。设备资源可以通过相关函数得到:struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)中断资源也可以通过:int platform_get_irq(struct platform_device *dev, unsigned int num)资源的使用主要是驱动实现过程中需要使用到的,但是后期的使用一般需要
7、在驱动的probe 函数中实现申请中断或者IO内存才能使用,而不能直接使用。特别是资源中的地址通常是 物理地址,需要通过申请IO内存和映射完成物理到虚拟地址的转换,便于进程的访问。platform_driver 注册平台驱动结构体platform_driver实现如下:struct platform_driver /*平台驱动需要实现的相关函数操作,其中的前4个函数与最后一个函数与device_driver中的函数是相同的本质是实现对device_driver中相关函数的赋值。*/int (*probe)(struct platform_device *);int (*remove)(stru
8、ct 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
9、 device_driver driver;/*平台设备 ID,这与 platform_device 中的 struct platform_device_id *id_entry 是相同的主要是完成总线的匹配操作,platform总线的匹配操作第一匹配要素就是该元素。而 不再是简单的name选项。*/struct platform_device_id *id_table;通常驱动的入口函数:int (*probe)(struct platform_device *);当总线完成了设备的match操作以后就会进入驱动中该函数的运行。总线函数的匹配操作如下:static int platform_
10、match(struct device *dev, struct device_driver *drv)/*得到平台设备的指针*/struct platform_device *pdev = to_platform_device(dev);/*得到平台驱动指针*/struct platform_driver *pdrv = to_platform_driver(drv);/* match against the id table first */*从定义上分析,id_table是首先匹配的对象,然后才是name的匹配,当ID匹配完成 时就说明匹配好了*/if (pdrv-id_table)re
11、turn platform_match_id(pdrv-id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev-name, drv-name) = 0);从上面的定义可以知道platform总线的匹配函数手下是比较id_table是匹配的首选项。probe函数称之为探针函数,用于检测总线上有该驱动能够处理的设备,而remove函数则 是为了说明总线上该驱动能够处理的设备被移除。因此这两个函数是在平台设备中一定要被实现的函数。其他的函数则不一样要求实现。平台驱动的设计主要是完成平台驱动结构体的
12、填充和注册。通常的平台驱动结构体实现如下:static struct platform_driver my_driver =/*平台驱动的probe函数实现*/.probe = my_probe,/*平台驱动的remove函数实现*/.remove = my_remove,/*实现设备驱动的name和owner变量*/.driver =/*该参数主要实现总线中的匹配函数调用*/.name = my_dev,/*该函数表示模块的拥有者*/.owner = THIS_MODULE,;其中的 my_probe和 my_remove是自己定义的probe和remove函数。最主要的是内嵌设备驱动结构体
13、的填充,主要的填充包括name和owner两个,当然也可以 包括其他的。由于没有填充id_table,那么name就是总线匹配操作的第一选择。因此如果没 有填充好id_table,那么name元素是一定要实现的,不然不能完成相应的设备驱动匹配操作。完成platform_driver结构体的填充过后就是完成驱动的在初始化阶段的注册以及退出阶段 的释放操作,基本的实现函数为:注册函数,通常在驱动初始化函数中调用:int platform_driver_register(struct platform_driver *drv)释放函数,通常在驱动退出函数调用:void platform_driver
14、_unregister(struct platform_driver *drv)完成相关的注册以后总线、设备、驱动的大概框架就完成啦。但是这只是常用的框架,还不能在应用程序中使用。基于平台驱动的设备驱动都是基于总线架构的,基本的实现过程与之前的简单字符设备存在 较大的差别,主要的区别在驱动的初始化不在是平台设备驱动的初始化函数中实现,而是在 probe函数中实现。而驱动的卸载函数则是在remove函数中实现。probe函数是平台总线实 现匹配以后首先被调用的函数,因此在其中实现字符设备、块设备、网络设备驱动的初始化 是有意义的,这样的设备驱动就是基于平台总线的设备驱动,便于维护。平台总线驱动的
15、注册过程分析:int platform_driver_register(struct platform_driver *drv)/*第一步,仍然是完成结构体的填充操作*/*驱动的总线类型*/drv-driver.bus = &platform_bus_type;/*将自己定义的probe函数赋值给平台驱动中设备驱动的probe函数,其他函数类似*/if (drv-probe)drv-be = platform_drv_probe;if (drv-remove)drv-driver.remove = platform_drv_remove;if (drv-shutdown)drv-driver.
16、shutdown = platform_drv_shutdown;if (drv-suspend)drv-driver.suspend = platform_drv_suspend;if (drv-resume)drv-driver.resume = platform_drv_resume;/*第二步,仍然是完成一般设备驱动的注册操作*/*然手就是一般驱动的注册,这样就完成了设备的注册*/return driver_register(&drv-driver);/*设备驱动的probe函数的赋值过程*/static int platform_drv_probe(struct device *_d
17、ev)/*得到设备对应的平台驱动*/struct platform_driver *drv = to_platform_driver(_dev-driver);/*得到设备的平台设备*/struct platform_device *dev = to_platform_device(_dev);/*下面的probe是自己实现的probe函数。具体的实现思路:根据一般设备找对应的平台设备,同时根据设备的驱动找到平台驱动。然后返回平台驱动的probe函数(自己实现通常是初始化操作)地址。*/return drv-probe(dev);实现的总线平台驱动模型的最简单源码:平台设备的实现:device
18、.c#include#include#include#include#include#include/*平台模型驱动的平台设备对象*/static struct platform_device *my_device;/*初始化函数*/static int _init my_device_init(void)int ret = 0;/*采用 platform_device_alloc 分配一个 platform_device 对象 参数分别为 platform_device 的 name,和 id。*/my_device = platform_device_alloc(my_dev,-1);/*
19、注册设备,注意不是platform_device_register,将平台设备注册到内核中*/ ret = platform_device_add(my_device);/*如果出错释放相关的内存单元*/if(ret)platform_device_put(my_device);return ret;/*卸载处理函数*/static void _exit my_device_exit(void)platform_device_unregister(my_device);module_init(my_device_init);module_exit(my_device_exit);MODULE_
20、LICENSE(GPL);MODULE_AUTHOR(GP-);平台驱动的实现:driver.c#include#include#include#include#include/*平台驱动中的probe和remove函数是必须实现的函数*/*设备驱动的探测函数,主要实现检测总线上是否有该驱动对应的设备*/static my_probe(struct device *dev)/*如果添加实际的设备到该平台总线设备驱动模型中,则可以在该函数 中实现具体的设备驱动函数的初始化操作,包括设备号的申请,设备 的初始化,添加。自动设备文件创建函数的添加等操作。或者是混杂字符设备的相关初始化操作。当然结构体
21、的相关处理仍 然采取全局变量的形式。*/printk(Driver found devices which this driver can be handlen);return 0;/*设备驱动的移除函数,主要检测该驱动支持设备的移除活动检测*/static my_remove(struct device *dev)/*如果添加实际的设备到该平台总线设备驱动模型中,则可以在该函数 中实现具体的设备的释放,包括设备的删除,设备号的注销等操作。*/printk(Driver found device unpludedn);return 0;static struct platform_driver
22、 my_driver =/*平台驱动的probe函数实现*/.probe = my_probe,/*平台驱动的remove函数实现*/.remove = my_remove,/*实现设备驱动的name和owner变量*/.driver =/*该参数主要实现总线中的匹配函数调用*/.name = my_dev,/*该函数表示模块的拥有者*/.owner = THIS_MODULE,;/*初始化函数*/static int _init my_driver_init(void)/*注册平台驱动*/return platform_driver_register(&my_driver);/*退出函数*/
23、static void _exit my_driver_exit(void)/*注销平台驱动*/return platform_driver_unregister(&my_driver);/*加载和卸载*/module_init(my_driver_init);module_exit(my_driver_exit);MODULE_LICENSE(GPL);MODULE_AUTHOR(GP-);将一般设备驱动加入到总线设备模型中的相关操作是后期总结和学习的内容。设备驱动实现 的实现原理还是之前的那些操作,但是初始化和推出函数发生了改变。总结:platform总线的驱动模型只是在一般总线模型的基础
24、上做了相关的延伸,实质上只要 弄清除总线模型的一般原理,学习platform总线也就简单不少。但是毕竟还是学习阶段。概述platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就 是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内 存,中断信号线。现在大多数处理器芯片都是soc,如s3c2440,它包括处理器内核(arm920t) 和系统的外设(lcd接口,nandflash接口等)。linux在引入了 platform机制之后,内核假 设所有的这些外设都挂载在platform虚拟总线上,以便进行统一管理。platfor
25、m 总线1.在系统中platform对应的文件drivers/base/platform.c,它不是作为一个模块注册到内 核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函 数间接调用。这里可以看出platform非常重要,要在系统其他驱动加载之前注册。下面分 析platform总线注册函数cpp view plaincopyint _init platform_bus_init(void)int error;early_platform_cleanup();error = device_register(&platform_bus);/
26、总线也是设备,所以也要进行设备的注册if (error)return error;error = bus_register(&platform_bus_type);/注册 platform_bus_type 总线到内核if (error)device_unregister(&platform_bus);return error;这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的driver_init函数调用, driver_init 函数由/init/main.c 中的 do_basic_setup 函数调用,do_basic_setup 这个函数由 kerne
27、l_init调用,所以platform总线是在内核初始化的时候就注册进了内核。2. platform_bus_type总线结构与设备结构(1)platform总线 设备结构cpp view plaincopystruct device platform_bus = .init_name = platform,;platform总线也是一种设备,这里初始化一个device结构,设备名称platform,因为没 有指定父设备,所以注册后将会在/sys/device/T出现platform目录。(2) platform总线总线结构cpp view plaincopystruct bus_type p
28、latform_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_dev_attrs设备属性platform_matchmatch函数,这个函数在当属于platform的设备或者驱动注册到内核时就会调用,完成设备与驱动的匹配工作。platform_uevent热插拔操作函数三.platform设备platform_device 结构cpp view plai
29、ncopy1.struct platform_device 2.const char * name;3.intid;4.struct device dev;5.u32num_resources;6.struct resource * resource;7.struct platform_device_id *id_entry8./* arch specific additions */9.struct pdev_archdata archdata;10.;(1)platform_device结构体中有一个struct resource结构,是设备占用系统的资源, 定义在ioport.h中,如下
30、cpp view plaincopystruct resource resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;(2) num_resources占用系统资源的数目,一般设备都占用两种资源,io内存和中断 信号线。这个为两种资源的总和。设备注册函数 platform_device_registercpp view plaincopyint platform_device_register(struc
31、t platform_device *pdev)device_initialize(&pdev-dev);return platform_device_add(pdev);这个函数首先初始化了 platform_device的device结构,然后调用platform_device_add, 这个是注册函数的关键,下面分析platform_device_add:cpp view plaincopyint platform_device_add(struct platform_device *pdev)int i, ret = 0;4.if (!pdev)return -EINVAL;7.if
32、(!pdev-dev.parent)pdev-dev.parent = &platform_bus;/可以看出,platform设备的父设备一般都是platform_bus,所以注册后的 platform 设备都出现在/sys/devices/platform_bus 下7.18.源4.35.pdev-dev.bus = &platform_bus_type;/挂到platform总线上if (pdev-id != -1)dev_set_name(&pdev-dev
33、, %s.%d, pdev-name, pdev-id);elsedev_set_name(&pdev-dev, %s, pdev-name);/设置设备名字,这个名字与/sys/devices/platform_bus下的名字对应 for (i = 0; i num_resources; i+) 下面操作设备所占用的系统资struct resource *p, *r = &pdev-resourcei;if (r-name = NULL)r-name = dev_name(&pdev-dev);p = r-parent;if (!p) if (resource_type(r) = IORES
34、OURCE_MEM) p = &iomem_resource;else if (resource_type(r) = IORESOURCE_IO) p = &ioport_resource;if (p & insert_resource(p, r) printk(KERN_ERR%s: failed to claim resource %dn, dev_name(&pdev-dev), i);9.40.ret = -EBUSY;goto failed;/上面主要是遍历设备所占用的资源,找到对应的父资源,如果没有定义,那么根据资源的类型,分别赋予 iomem_resource
35、 和 ioport_resource,然后调用 insert_resource 插入 资源。这样系统的资源就形成了一个树形的数据结构,便于系统的管理pr_debug(Registering platform device %s. Parent at %sn,dev_name(&pdev-dev), dev_name(pdev-dev.parent);44.ret = device_add(&pdev-dev);/注册到设备模型中if (ret = 0)return ret;failed:while (-i = 0) struct resource *r = &pdev-resourcei;un
36、signed long type = resource_type(r);if(type= IORESOURCE_MEM | type =IORESOURCE_IO)release_resource(r);returnret;mini2440内核注册platform设备过程因为一种soc确定之后,其外设模块就已经确定了,所以注册platform设备就由板级初 始化代码来完成,在mini2440中是mach-mini2440.c的mini2440_machine_init函数中调 用 platform_add_devices(mini2440_devices, ARRAY_SIZE(mini244
37、0_devices)来完成注 册。这个函数完成mini2440的所有platform设备的注册:(1)platform_add_devices 函数是 platform_device_register 的简单封装,它向内核 注册一组platform设备(2)mini2440_devices 是一个 platform_device 指针数组,定义如下:cpp view plaincopystatic struct platform_device *mini2440_devices _initdata = &s3c_device_usb,&s3c_device_rtc,&s3c_device_lc
38、d,&s3c_device_wdt,&s3c_device_i2c0,&s3c_device_iis,&mini2440_device_eth,&s3c24xx_uda134x,&s3c_device_nand,&s3c_device_sdi,&s3c_device_usbgadget,;这个就是mini2440的所有外设资源了,每个外设的具体定义在/arch/arm/plat-s3c24xx/devs.c,下面以 s3c_device_lcd 为例说明,其他的类似。 s3c_device_lcd 在 devs.c 中它定义为:cpp view plaincopystruct platform
39、_device s3c_device_lcd = .name= s3c2410-lcd,.id = -1,.num_resources= ARRAY_SIZE(s3c_lcd_resource),.resource= s3c_lcd_resource, TOC o 1-5 h z .dev= .dma_mask = &s3c_device_lcd_dmamask,.coherent_dma_mask = 0 xffffffffUL;可以看出,它占用的资源s3c_lcd_resource,定义如下:cpp view plaincopystatic struct resource s3c_lcd_
40、resource = 0 = .start=S3C24XX_PA_LCD,.end=S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,.flags=IORESOURCE_MEM, TOC o 1-5 h z ,1= .start=IRQ_LCD,.end=IRQ_LCD,.flags=IORESOURCE_IRQ,;这是一个数组,有两个元素,说明lcd占用了系统两个资源,一个资源类型是IORESOURCE_MEM 代表 io 内存,起使地址 S3C24XX_PA_LCD,这个是 LCDCON1 寄 存器的地址。另外一个资源是中断信号线。四.platform设备驱动如果要将
41、所写的驱动程序注册成platform驱动,那么所做的工作就是初始化一个 platform_driver,然后调用 platform_driver_register 进行注册。基本数据机构platform_drivercpp view plaincopystruct platform_driver int (*probe)(struct platform_device *);int ( *remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platfo
42、rm_device*, pm_message_t state);int ( *resume)(struct platform_device*);struct device_driver driver;struct platform_device_id *id_table;这是platform驱动基本的数据结构,在驱动程序中我们要做的就是声明一个这样的结构 并初始化。下面是led驱动程序对它的初始化:cpp view plaincopy1.static struct platform_driver s3c2412fb_driver = 2.probe= s3c2412fb_probe,3.rem
43、ove= s3c2410fb_remove,4.suspend = s3c2410fb_suspend,5.resume= s3c2410fb_resume,6.driver= 7.name = s3c2412-lcd,8.9.10.owner = THIS_MODULE, ,;上面几个函数是我们要实现的,它将赋值给deviee_driver中的相关成员,probe函数是 用来查询特定设备是够真正存在的函数。当设备从系统删除的时候调用remove函数。注册函数 platform_driver_registercpp view plaincopyint platform_driver_register(struct platf
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年纳雍县带编教师招聘笔试备考题库及答案解析
- 2026年玛沁县医疗事业单位人员招聘考试备考试题及答案解析
- 2026年绿春县带编教师招聘笔试备考题库及答案解析
- 2026年汶上县社区工作者招聘笔试备考题库及答案解析
- 2026年通榆县社区工作者招聘笔试模拟试题及答案解析
- 2026年广宗县医疗事业单位人员招聘笔试模拟试题及答案解析
- 2026年安新县医疗事业单位人员招聘笔试备考试题及答案解析
- 2026年浮梁县医疗事业单位人员招聘笔试参考题库及答案解析
- 2026年余庆县医疗事业单位人员招聘笔试模拟试题及答案解析
- 2026年贺兰县医疗事业单位人员招聘考试备考题库及答案解析
- 2026广东珠海市拱北海关缉私局警务辅助人员招聘15人考试备考试题及答案详解
- (正式版)DB11∕T 850-2024 《建筑墙体用腻子施工及验收规程》
- 2026年中医科规培考试题库及完整答案
- 苏教版2026-2027三年级数学上册教学计划及进度
- 2026年秋季开学大学面试技巧生涯规划课件
- 1.2我们都是社会的一员 课件 2026-2027学年统编版道德与法治 八年级上册
- 新版2026年秋新版九年级上册道德与法治知识点全面梳理1合集
- 中国华电集团2026笔试题目及答案详解
- 2026青岛平度市农旅开发建设集团有限公司工作人员招聘(11人)笔试参考题库及答案详解
- 2026年心内科急诊心衰抢救护理配合流程
- 2026年秋人教PEP版新教材小学英语四年级上册教学计划及进度表
评论
0/150
提交评论