博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
platform_device与platform_driver
阅读量:4703 次
发布时间:2019-06-09

本文共 6449 字,大约阅读时间需要 21 分钟。

 通常编写linux字符设备常接触到的file_operations以及miscdevice,然后申请设备号,注册字符设备,没有涉及到设备驱动模型,而驱动模型里,device_driver根本没有涉及到设备操作的函数、file_operations等,只有一些电源管理,热插拔相关的函数。platform_device里也主要是resource的管理,所以感觉两者根本就没关系,也很奇怪为什么要弄两套东西来实现,而且两者也对应不起来。通过查阅业内长辈原文博客针对platform_device和platform_driver做了些摘录批注。

platform_device与platform_driver一直分不清关系。在网上搜了下,做个总结。两者的工作顺序是先定义platform_device -> 注册 platform_device->,再定义 platform_driver-> 注册 platform_driver。

1)定义platform_device

2)注册platform_device

3)定义platform_driver

4)注册platform_driver

 (

函数编写分步:

1)resource 

2)platform_device

3)static struct platform_device *smdk6410_devices[] __initdata = {...... &s3c_device_usbgadget, &s3c_device_usb, //jeff add.......}

  platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));

4)platform_driver

 

 

1◐ resource

platform_device设备的注册过程必须在相应设备驱动加载之前被调用,因为驱动注册时需要匹配内核中所以已注册的设备名。platform_device 是在系统启动时在init.c 里的s3c_arch_init() 函数里进行注册的。这个函数申明为arch_initcall(s3c_arch_init); 会在系统初始化阶段被调用arch_initcall 的优先级高于module_init,所以会在Platform 驱动注册之前调用。现在内核中不是采用arch_initcall(s3c_arch_init) 注册platform_device 结构体而是通过.init_machine成员将其保存在arch_initcall(customize_machine)等待调用(在mach-smdk6410.c中定义的MACHINE_START到MACHINE_END);其实质是一样的均放在.initcall3.init等待调用。之后再定义结构体struct platform_driver,在驱动初始化函数中调用函数platform_driver_register() 注册 platform_driver。详细过程描述如下:

      Linux从2.6版本开始引入了platform这个概念,在开发底层驱动程序时,首先要确认的就是设备的资源信息,在2.6内核中将每个设备的资源用结构platform_device来描述,该结构体定义在kernel/include/linux/platform_device.h中,

struct platform_device    {        const char * name;        u32  id;        struct device dev;        u32  num_resources;        struct resource * resource;  };

该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel/include/linux/ioport.h中,

比如:

struct resource     {         const char *name;         unsigned long start, end;         unsigned long flags;         struct resource *parent, *sibling, *child;  };   

实例如:

static struct resource s3c_usb_resource[] = {       信息流向① [0] = {         .start = S3C_PA_USBHOST,         .end   = S3C_PA_USBHOST + S3C_SZ_USBHOST - 1,         .flags = IORESOURCE_MEM,       },   [1] = {         .start = IRQ_UHOST,         .end   = IRQ_UHOST,         .flags = IORESOURCE_IRQ,       }  };  //编号有无上限,那么上限是多少?
 
 
2◐定义platform_device
 以上是6410的USB  HOST分配的资源信息。第1组描述了这个usb host设备所占用的总线地址范围,起始地址和大小由硬件决定,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息;第2组描述了这个usb host设备的中断号,也由硬件设定,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。

      有了resource信息(前提),就可以定义platform_device了:

struct platform_device s3c_device_usb = {           .name    = "s3c2410-ohci",  //s3c6410-usb      信息流向②         .id    = -1,           .num_resources   = ARRAY_SIZE(s3c_usb_resource),           .resource   = s3c_usb_resource,            .dev              = {                   .dma_mask = &s3c_device_usb_dmamask,                   .coherent_dma_mask = 0xffffffffUL               }  };

 

 3◐注册platform_device

有了platform_device就可以调用函数platform_add_devices向系统中添加该设备了。系统中的设备资源都可以采用这种方式列举在一起,然后成一个指针数组,如:

static struct platform_device *smdk6410_devices[] __initdata = {...... &s3c_device_usbgadget, &s3c_device_usb,  //jeff add.     信息桥梁②~③......}

然后在6410的初始化函数smdk6410_machine_init()中执行:

platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));将所有的device添加进系统。platform_add_devices的好处在于它是一次性的执行多个platform_device_register。

(2) 至于驱动程序需要实现结构体struct platform_driver,也定义在kernel/include/linux/platform_device.h中:

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 pm_ext_ops *pm;        struct device_driver driver;  };

则该处的USB HOST实现是:

 4◐定义platform_driver

static struct platform_driver ohci_hcd_s3c2410_driver = {       .probe  = ohci_hcd_s3c2410_drv_probe,       .remove  = ohci_hcd_s3c2410_drv_remove,       .shutdown = usb_hcd_platform_shutdown,       /*.suspend = ohci_hcd_s3c2410_drv_suspend, */       /*.resume = ohci_hcd_s3c2410_drv_resume, */       .driver  = {            .owner = THIS_MODULE,            .name = "s3c2410-ohci",  信息③        },  };

 

  5◐注册platform_driver

      在驱动初始化(ohci-hcd.c的1124行)函数中调用函数platform_driver_register()注册该platform_driver,需要注意的是s3c_device_usb结构中name元素和ohci_hcd_s3c2410_driver 结构中driver.name必须是相同的,这样在platform_driver_register()注册时会对所有已注册的platform_device中元素的name和当前注册的platform_driver的driver.name进行比较,只有找到具备相同名称的platform_device存在后,platform_driver才能注册成功。当注册成功时会调用platform_driver结构元素probe函数指针,这里就是ohci_hcd_s3c2410_drv_probe开始探测加载。platform driver中的函数都是以platform device作为参数进入。

(3)为什么两个name的名字必须匹配才能实现device和driver的绑定?(1)在内核初始化时kernel_init()->do_basic_setup()->driver_init()->platform_bus_init()初始化platform_bus(虚拟总线);(2)设备注册的时候platform_device_register()->platform_device_add()->(pdev->dev.bus = &platform_bus_type)把设备挂在虚拟的platform bus下;(3)驱动注册的时候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev(),对每个挂在虚拟的platform bus的设备作__driver_attach()->driver_probe_device(),判断drv->bus->match()是否存在并且是否执行成功,此时通过指针执行platform_match,比较strncmp(pdev->name, drv->name, BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行的相应设备的platform_driver->probe(platform_device),注意platform_drv_probe的_dev参数是由bus_for_each_dev的next_device获得)开始真正的探测加载,如果probe成功则绑定该设备到该驱动。

      当进入probe函数后,需要获取设备的资源信息,根据参数type所指定类型,例如IORESOURCE_MEM,来分别获取指定的资源。

struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);当然,也可以固定资源类型,如获取资源中的中断号:struct int platform_get_irq(struct platform_device *dev, unsigned int num);

      probe函数一般完成硬件设备使能,struct resource的获取以及虚拟地址的动态映射和具体类型设备的注册(因为平台设备只是一种虚拟的设备类型);remove函数完成硬件设备的关闭,struct resource以及虚拟地址的动态映射的释放和具体类型设备的注销。只要和内核本身运行依赖性不大的外围设备 ( 换句话说只要不在内核运行所需的一个最小系统之内的设备 ), 相对独立的拥有各自独自的资源 (addresses and IRQs) ,都可以用platform_driver 实现。如:lcd,usb,uart 等,都可以用platfrom_driver 写,而timer,irq等最小系统之内的设备则最好不用platfrom_driver 机制,实际上内核实现也是这样的。

 

参考原文:http://www.cnblogs.com/Ph-one/p/4671593.html 

参考原文:

参考原文:http://blog.csdn.net/zhandoushi1982/article/details/5130207

博文出自:http://www.cnblogs.com/Ph-one/p/4762710.html

 

 

 

                                                      修改:

                                                      瘋耔 

                                                      2015/08/27

                                        

转载于:https://www.cnblogs.com/Ph-one/p/4762710.html

你可能感兴趣的文章
发布时间 sql语句
查看>>
黑马程序员 ExecuteReader执行查询
查看>>
记一些从数学和程序设计中体会到的思想
查看>>
题目1462:两船载物问题
查看>>
POJ 2378 Tree Cutting(树形DP,水)
查看>>
第二冲刺阶段个人博客5
查看>>
UVA 116 Unidirectional TSP (白书dp)
查看>>
第三方测速工具
查看>>
MySQL 网络访问连接
查看>>
在aws ec2上使用root用户登录
查看>>
数据访问 投票习题
查看>>
cnblog!i'm coming!
查看>>
使用点符号代替溢出的文本
查看>>
Axios 中文说明
查看>>
fatal: remote origin already exists.
查看>>
gridview 自定义value值
查看>>
2018二月实现计划成果及其三月规划
查看>>
类名.class和getClass()区别
查看>>
12/17面试题
查看>>
LeetCode 242. Valid Anagram
查看>>