当前位置: 首页 > news >正文

黑龙江建设局网站东莞市专注网站建设服务机构

黑龙江建设局网站,东莞市专注网站建设服务机构,深圳人才招聘网官网,贵州做团队培训的网站驱动的分离与分层思想 分离#xff1a;硬件信息分离#xff1b; 在编写硬件驱动的时候#xff0c;需要操作许多硬件寄存器。比如gpio 驱动#xff0c;你需要知道gpio控制器 寄存器的地址#xff0c;你想要哪个gpio输出#xff1f;或是输入? 这些操作最终都是靠设置寄存…驱动的分离与分层思想 分离硬件信息分离 在编写硬件驱动的时候需要操作许多硬件寄存器。比如gpio 驱动你需要知道gpio控制器 寄存器的地址你想要哪个gpio输出或是输入? 这些操作最终都是靠设置寄存器完成如果你想要操作的gpio变了、方向变了这些硬件信息的都要修改驱动。 或者说同一个系列的soc 它们的寄存器地址不同、soc 更新换代寄存器地址改变等等但是寄存器的设置方法相同驱动无法兼容多个硬件。 这样明明可以只用一份代码通却因为硬件不同要写无数份代码重用性很低内核中也会多许多这种垃圾代码。 这种要不断修改、重用性低的驱动是不理想的有没有可能做出一份通用且不需要经常修改代码的驱动呢 可以硬件信息分离。把控制器的寄存器地址、要输出的引脚、方向等信息从驱动分离出来另外保存原本的驱动就变成一些纯软件代码与操作寄存器的代码这样就不需要因为硬件的改变而不断修改代码。 分离的思想就是把硬件信息从驱动中分离出来做出一套通用的驱动。 Linux 驱动如何实现分离内核3.x 以前使用 .c结构体 文件描写硬件信息有设备树后用设备描写硬件信息。 分层代码分层。 使用过内核通用的gpio驱动可以知道内核gpio驱动分为两部分 gpio核心层 与硬件无关的软件层负责管理下层的各种驱动调用它们提供的硬件操作函数下层一般会把操作函数设成回调函数进行封装实现各个厂家的驱动统一的操作方法chip_xxx_gpio.c 某个soc 厂家为自己芯片编写的gpio硬件驱动。实现gpio功能各个厂家的操作方式可能不同所以操作函数也不同 为什么要分层呢 因为linux内核是要兼容多个平台的不同的平台可能寄存器的设计不同导致方法不同那又会衍生出很多驱动。 所以内核提出了分层思想由与硬件无关的软件层作为上层核心层来管理下层驱动而各个厂家则根据自己的硬件编写驱动代码作为下层硬件驱动层不管你硬件怎么变暴露给上层的接口必须一致按照上层的管理方法来编写。 加上硬件信息一个驱动一共就是三部分通用的驱动框架、硬件驱动和硬件信息硬件信息硬件驱动硬件驱动层。 Linux 驱动-总线-设备 模型 分离是把硬件信息和驱动代码分离开来那么在Linux内核中如何实现分离。 Linux 内核建立了设备-总线-驱动 这样的模型用struct device 来表示设备用struct device_driver 来表示驱动。(它们都定义于include\linux\device.h) struct device 和struct device_driver 只是两个基类根据不同的总线会衍生出很多派生类比如platform_device(它可以描述设备信息)、platform_driver (可以描述驱动) struct device {struct device *parent; //父设备struct device_private *p; struct kobject kobj;const char *init_name; /* initial name of the device */ //名字const struct device_type *type;struct mutex mutex; /* mutex to synchronize calls to* its driver.*/struct bus_type *bus; /* type of bus device is on */ //总线类型struct device_driver *driver; /* which driver has allocated thisdevice */void *platform_data; /* Platform specific data, device //platform 数据core doesnt touch it */void *driver_data; /* Driver data, set and get with //驱动数据一般可以在驱动代码中设置为比较常用的结构体变量dev_set/get_drvdata */struct dev_pm_info power;struct dev_pm_domain *pm_domain;#ifdef CONFIG_PINCTRLstruct dev_pin_info *pins; #endif#ifdef CONFIG_NUMAint numa_node; /* NUMA node this device is close to */ #endifu64 *dma_mask; /* dma mask (if dmaable device) */u64 coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */unsigned long dma_pfn_offset;struct device_dma_parameters *dma_parms;struct list_head dma_pools; /* dma pools (if dmable) */struct dma_coherent_mem *dma_mem; /* internal for coherent memoverride */ #ifdef CONFIG_DMA_CMAstruct cma *cma_area; /* contiguous memory area for dmaallocations */ #endif/* arch specific additions */struct dev_archdata archdata;struct device_node *of_node; /* associated device tree node */ //设备树节点struct fwnode_handle *fwnode; /* firmware device node */dev_t devt; /* dev_t, creates the sysfs dev */ //设备号u32 id; /* device instance */spinlock_t devres_lock;struct list_head devres_head;struct klist_node knode_class;struct class *class; //device 所属的类const struct attribute_group **groups; /* optional groups */ //属性文件组void (*release)(struct device *dev); //用于释放device 的回调函数struct iommu_group *iommu_group;bool offline_disabled:1;bool offline:1; };struct device_driver {const char *name; //驱动名字struct bus_type *bus; //总线类型struct module *owner; //一般设置为THIS_MODULESconst char *mod_name; /* used for built-in modules */bool suppress_bind_attrs; /* disables bind/unbind via sysfs */const struct of_device_id *of_match_table; //用于设备树匹配的id const struct acpi_device_id *acpi_match_table;int (*probe) (struct device *dev); //当匹配成功后会调用probe在probe中我们可以自由发挥做任何想做的事比如硬件的设置int (*remove) (struct device *dev); //一方想要卸载时会调用remove匹配成功的状态下void (*shutdown) (struct device *dev);int (*suspend) (struct device *dev, pm_message_t state);int (*resume) (struct device *dev);const struct attribute_group **groups; //属性文件组const struct dev_pm_ops *pm;struct driver_private *p; };现在有了设备信息、驱动代码但是要把它们合在一起使用才能算是完整可用的驱动代码内核里有那么多个驱动就会产生无数个device 和device_driver如何把它们匹配在一起 这时候总线就出来了它能把相互对应的device 和device_driver 匹配起来形成完整的驱动。此总线并非硬件总线而是Linux内核中虚拟出来的用来匹配设备信息和驱动代码 总线用一个struct bus_type 类型的数据结构来描述 include\linux\device.h struct bus_type {const char *name; //总线名const char *dev_name;struct device *dev_root;struct device_attribute *dev_attrs; /* use dev_groups instead */const struct attribute_group **bus_groups;const struct attribute_group **dev_groups;const struct attribute_group **drv_groups;int (*match)(struct device *dev, struct device_driver *drv); //负责判断设备与驱动之间是否匹配的函数至关重要!!!int (*uevent)(struct device *dev, struct kobj_uevent_env *env);int (*probe)(struct device *dev);int (*remove)(struct device *dev);void (*shutdown)(struct device *dev);int (*online)(struct device *dev);int (*offline)(struct device *dev);int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);const struct dev_pm_ops *pm;const struct iommu_ops *iommu_ops;struct subsys_private *p;struct lock_class_key lock_key; };查看device 与 device_driver 匹配过程 原理分析 注册device 时会将它添加到bus-p-klist_devices链表然后获取bus-p-klist_drivers 链表将device 与链表中的所有device_driver 一一比较如果匹配成功就会调用device_driver-probe。 同样注册注册device_driver 时也会将它添加到bus-p-klist_drivers链表然后获取bus-p-klist_devices 链表将device_driver 与链表中的所有device 一一比较如果匹配成功就会调用device_driver-probe。 struct device 使用device_register() 向内核注册device_driver 使用driver_register() 向内核注册。 device_register device_add -bus_add_device bus_add_device 中将dev-p-knode_bus(struct klist_node) 添加到bus-p-klist_devices(struct klist) 链表中 bus根据设备所属的总线来定可能是platform、i2c、spi… 任何总线。 device_add -bus_probe_device -device_attach (attach 表示附加的意思此函数将device 附加到某个device_driver 上) device_attach 中调用bus_for_each_drv 遍历bus 下的所有device_driver。 klist_iter_init_node 初始化了一个klist_iter (i) i-i_klist k; //被设置为了bus-p-klist_drivers它就是device_driver 的klist 链表 i-i_cur n; //i_cur 是struct klist_node 类型的传入的start为NULL所以i_cur NULL 循环的调用next_driver(i) 来遍历bus-p-klist_drivers 所有节点获取与节点对应的device_driver (依靠刚刚构建的klist_iter 来找到下一个链表节点由于klist_iter-i_cur 为空所以可能是从头开始获取节点之类的) 拿到了device_driver就可以与新注册device 进行比对了调用fn 也就是传入的_device_attach 进行比较如果匹配上就会绑定device 和device_driverdevice-driver driver并且调用device_driver-probe。 device_add -bus_probe_device -device_attach -bus_for_each_drv -__device_attach //判断device 与device_driver 是否匹配 比较的方法由具体的总线提供直接调用device_driver-bus-match 回调函数。 假如总线是platform 的话那么bus-match 就是platform_match。 platform_match 中有三种匹配方式在有设备树的时我们一般使用设备树的匹配方式比较compatible 的值没有设备树则使用方法3 和4。 of_driver_match_device 是通过platform_driver-driver-of_match_table-compatible 与platform_device-device-of_node 中读取compatible 属性值来比较。 of_driver_match_device -of_match_device platform_match_id 是通过比较platform_device 的名字和platform_driver-id_table-name 来判断是否匹配。 如果匹配则调用 driver_probe_device 绑定device 与device_driver 的关系并且调用bus-probe 或device_driver-probe driver_probe_device -really_probe 优先调用device-bus-probe如果没提供则调用deivce_driver-probe。 driver_register driver_register 调用bus_add_driver 向总线添加device_driver bus_add_driver 将device_driver-p-knode_bus (klist_node)节点添加到bus-p-klist_drivers (klist)链表。 然后调用driver_attach将新注册的device_driver 与bus 下的所有device 进行比对。 driver_attach 调用bus_for_each_dev 遍历总线下的所有device调用_driver_attach 将新注册的device_driver 与device 一一比对。 bus_for_each_dev 循环遍历bus 下的所有device调用__driver_attach 进行device 与device_driver比较 __driver_attach 调用driver_match_device 来判断device 和device_driver 是否匹配这一步和__device_attach 中是一样的调用bus-match 来对比 如果两者匹配的话调用driver_probe_device-really_probe 绑定device 和device_driver的关系然后调用bus-probe 或device_driver-probe (这一步和__device_attach 中也是一模一样的) 根据不同外设的特点内核定义了多条常用的总线比如platform总线、i2c 总线、spi总线… 等等。 定义如下 //drivers/base/platform.c //drivers/i2c/i2c-core.c //driver/spi/spi.c 可以在命令行查看内核具体定义了哪些总线如 为什么要定义这么多总线类型以i2c 和platform 来举例 i2c 总线 挂在i2c总线(硬件)下的从设备比如加密芯片、rtc芯片、触摸屏芯片等等这些从设备它们也需要驱动它们自然也要按照分离思想来设计。 那么内核中的i2c 总线就是用来帮助i2c从设备的设备信息 和驱动互相匹配的。 platform 总线 像i2c、spi这样硬件有实体总线的从设备驱动可以用总线来管理。 那么没有总线的硬件外设怎么办比如gpio、uart、i2c控制器、spi 控制器…等等这些通通用platform 总线来管理。 如何注册一个总线常用的总线内核中已经注册好了通常情况下无需自己创建一条总线 可以用bus_register 函数注册一条总线 int bus_register(struct bus_type *bus) bus_unregister 注销一条总线 void bus_unregister(struct bus_type *bus) 如platform 总线的注册代码如下 //drivers\base\platform.c platform 总线 根据platform 总线和外设的特点为了更好的描述设备信息和驱动代码在device 和device_driver 基础上衍生了platform_device 和platform_driver 两个派生类。 //include/linux/platform_device.h struct platform_device {const char *name; //设备名int id;bool id_auto;struct device dev; //派生类仍然包含基础的device因为里面有不可或缺的重要信息u32 num_resources; //资源数量struct resource *resource; //描述硬件设备的资源资源包括寄存器地址范围、irq 等等。const struct platform_device_id *id_entry; //用于匹配的idchar *driver_override; /* Driver name to force a match *//* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata archdata; };在kernel v3.x 版本以上内核引入设备树所以硬件信息往往在设备树中描述。内核代码会解析设备树把它转化为一个platform_device。 例如这是一个描述gpio控制器的设备树节点 reg 第一个字段表示gpio控制器寄存器的起始地址第二个字段表示长度。物理地址 interrupts 表示中断号。共享中断 中断号 中断触发类型 //include/linux/platform_device.h struct platform_driver {//当platform_driver与platform_device 匹配时就会调用probe在probe函数中可以自由发挥写任何东西比如驱动代码int (*probe)(struct platform_device *); //当platform_driver 或是platform_device 一方卸载时就会调用remove (匹配完成的情况下)//在remove中通常做与probe 相反的操作比如probe 中申请了内存那么remove 就要释放内存int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);struct device_driver driver; //派生类任然包含基类这是很重要的device_driver 中包含决定双方是否匹配的重要信息const struct platform_device_id *id_table; //用于匹配的idbool prevent_deferred_probe; };在platform_device 中有一个resource 成员它对于描述硬件信息是非常重要的。 它用来描述一个资源资源包括寄存器地址、中断等等。可描述的资源很多参考宏定义 //include/linux/ioport.h /* 如果所描述的资源是寄存器地址IORESOURCE_MEM 类型那么 start 保存着reg 的起始地址end 保存着reg的末尾地址。start-end 就描述出了一段地址范围。 如果描述的是irq那么start 保存着irq 的值。 */ struct resource {resource_size_t start;resource_size_t end;const char *name; //资源的名字unsigned long flags;struct resource *parent, *sibling, *child; };//include/linux/ioport.h #define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ #define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */ //io内存地址资源类型 #define IORESOURCE_MEM 0x00000200 //mem内存地址资源类型 #define IORESOURCE_REG 0x00000300 /* Register offsets */ #define IORESOURCE_IRQ 0x00000400 //irq 资源类型 #define IORESOURCE_DMA 0x00000800 #define IORESOURCE_BUS 0x00001000 ......那么platform_device 和platform_driver 这两个结构体究竟该如何使用它呢以imx6ull 的gpio驱动为例 首先硬件信息会用设备树节点描述由内核解析成一个platform_device 注册到内核中。 没有设备树的版本驱动需要自己创建一个platform_device 结构体设置里面的内容然后向内核注册。使用platform_device_register 函数注册 int platform_device_register(struct platform_device *); 在驱动中需要构建一个platform_driver初始化内容并调用platform_driver_register 向内核注册。 细看platform_driver 中设置的内容 device_driver-name 是驱动的名字它也可以用来判断platform_device 与platform_driver 是否匹配。非设备树情况下 device_driver-of_match_table 设备树特有的匹配方式。在dtb节点 和of_match_table 中都有一个compatible 属性它用来判断驱动与设备是否兼容如果compatible的值是相同的那么就就判断他两是匹配的。data 是与硬件平台相关的数据。 id_table不使用设备树情况下的一种匹配方式通过对比id_table-name 与platform_device-name 来判断两者是否兼容。 probe当匹配完成之后就会自动调用probe 函数了在probe函数中我们可以做任何事情比如硬件的设置等等。 参考mxc_gpio_probe 来看看它做了什么 传入的参数就是由设备树节点转化而来的platform_device 除了这些在probe 中还可以做任何事注册irq、设置driver_data 等等。 这个函数可以设置platform_device-dev.drver_data可以把它设置成任何驱动中常用的变量地址。 platform_device-dev.of_node 指向的就是与驱动所匹配的设备树节点可以用它来获取设备树节点。 platform_driver 与platform_device 注册流程 总结前面的内容为了实现硬件信息的分离Linux 内核用一个platform_device 来描述硬件信息用platform_driver 来描述驱动代码由platform 总线来管理帮助它们相互匹配。 使用platform总线管理的设备都要调用platform_device_register 注册platform_device驱动要调用platform_driver_regster 注册platform_driver。每当platform_device 和platform_driver 匹配就会调用platform_driver.probe 来执行驱动代码。 #define platform_driver_register(drv) \ __platform_driver_register(drv, THIS_MODULE) extern int __platform_driver_register(struct platform_driver *, struct module *); int platform_device_register(struct platform_device *); 那么双方究竟是如何完成匹配的又是如何调用到probe 函数的这就要看看platform_device_register 和platform_driver_regster 是如何注册的。 代码解析 调用platform_device_register 注册platform_device platform_device_register-platform_device_add将platform_device-dev 的父设备设为platform_bus (在注册platform 总线时创建的) platform_device-dev.bus 设为platform_bus_type。 调用device_add 添加platform_device-dev (struct device)。 从device_add 开始就是device 的注册过程参考前面的device与device_driver 匹配过程 调用platform_driver_register 注册platform_driver #define platform_driver_register(drv) \__platform_driver_register(drv, THIS_MODULE)__platform_driver_register 中将platform_driver-driver.probe 设置为platform_drv_probe 那么在device 和device_driver 匹配成功后就会调用platform_drv_probe 。 调用driver_register 注册platform_driver-driver (参考前面的的device_driver 注册过程device 与device_driver 匹配过程) 在device 与device_driver 匹配成功后最终将调用device_driver-probe 对于platfrom 总线来说它就是platform_drv_probe。 在platform_drv_probe 中会调用platform_driver-probe也就是各自驱动中实现的真正的驱动代码了)
http://www.dnsts.com.cn/news/83495.html

相关文章:

  • 扬州哪里做网站好90设计网首页
  • 婚礼婚庆网站建设深圳建设招标网站首页
  • 贵阳网站商城建设营销型网站的特点
  • seo网站策划书网站m3u8链接视频怎么做的
  • 网站的二维码怎么做小学校园门户网站建设方案
  • 网站建设否定关键词长沙黄花机场公众号
  • 企业网站的布局个人网页设计与实现ppt
  • 太原网站公司网站分析的优劣势
  • 淘宝购物返利网站开发wordpress文件上传管理
  • 免费制作网站和网页潍坊做网站的企业
  • 电白建设局网站docker wordpress多个
  • 可信赖的企业网站建设科技广告公司网站建设
  • 门户网站域名是什么意思深圳市空间设计有限公司
  • 做外贸需要有自己的网站吗江西建设监督网站
  • 九冶建设有限公司官方网站wordpress 支持
  • 万盛建设局官方网站织梦cms 网站计数
  • h5哪个网站可以做个人网站建设的目的
  • 安云自助建站系统源码在线看国内永久免费crm
  • 怎么做网站优化有哪些做设计交易网站有哪些内容
  • 有创意的婚纱网站模板建筑网课回放
  • 如何让各大搜索引擎识别新建网站iis5建设网站
  • 建设部网站安全考核证书查询微网站无锡
  • 长沙wap网站建设长沙网站建设流程
  • WordPress迁移网站打不开福步外贸app下载
  • 北京做环评备案的网站财务软件费用计入什么科目
  • 中国最大的建材网站手机网站生成小程序
  • 做淘客网站多少钱个人网站建设基础与实例
  • 南京医院网站建设平台app制作哪家好
  • 龙岗营销网站建设公司哪家好英文建站平台有哪些
  • 宿州高端网站建设网店设计实训报告