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

博物馆网站建设方案报价b2b网站有哪些平台

博物馆网站建设方案报价,b2b网站有哪些平台,珠海市企业网站制作服务机构,网络推广方案要怎么做驱动的分离与分层思想 分离:硬件信息分离; 在编写硬件驱动的时候,需要操作许多硬件寄存器。比如gpio 驱动,你需要知道gpio控制器 寄存器的地址,你想要哪个gpio输出?或是输入? 这些操作最终都是靠设置寄存…

驱动的分离与分层思想

分离:硬件信息分离;
在编写硬件驱动的时候,需要操作许多硬件寄存器。比如gpio 驱动,你需要知道gpio控制器 寄存器的地址,你想要哪个gpio输出?或是输入? 这些操作最终都是靠设置寄存器完成,如果你想要操作的gpio变了、方向变了,这些硬件信息的都要修改驱动。

或者说同一个系列的soc 它们的寄存器地址不同、soc 更新换代寄存器地址改变等等,但是寄存器的设置方法相同,驱动无法兼容多个硬件。
这样,明明可以只用一份代码通,却因为硬件不同要写无数份,代码重用性很低,内核中也会多许多这种垃圾代码。
这种要不断修改、重用性低的驱动是不理想的,有没有可能做出一份通用且不需要经常修改代码的驱动呢?

可以,硬件信息分离。把控制器的寄存器地址、要输出的引脚、方向等信息从驱动分离出来另外保存,原本的驱动就变成一些纯软件代码与操作寄存器的代码,这样就不需要因为硬件的改变而不断修改代码。

分离的思想就是把硬件信息从驱动中分离出来,做出一套通用的驱动。

Linux 驱动如何实现分离:内核3.x 以前使用 .c(结构体) 文件描写硬件信息,有设备树后用设备描写硬件信息。

分层:代码分层。
使用过内核通用的gpio驱动可以知道,内核gpio驱动分为两部分:

  1. gpio核心层 (与硬件无关的软件层,负责管理下层的各种驱动,调用它们提供的硬件操作函数(下层一般会把操作函数设成回调函数)进行封装,实现各个厂家的驱动统一的操作方法)
  2. 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 doesn't 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 dma'able 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 dma'ble) */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_driver(device->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 总线和外设的特点,为了更好的描述设备信息和驱动代码,在devicedevice_driver 基础上衍生了platform_deviceplatform_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_deviceplatform_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_registerplatform_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.dinnco.com/news/68056.html

相关文章:

  • wordpress极简博客沈阳网站制作优化推广
  • 线上推广引流是做网站吗关键词检索怎么弄
  • 校园网网络设计报告重庆seo扣费
  • 青海省住房和城乡建设厅门户网站什么叫口碑营销
  • 本溪做网站 淘宝店百度一下你就知道了官网
  • 网站建设适合什么单位seo搜索引擎优化介绍
  • 装修网站建设网页制作与网站建设实战教程
  • 全国住房和城乡建设部网站百度集团官网
  • 做app模板网站有哪些南京谷歌优化
  • 关于建设学校网站的报告书seo技术平台
  • 如何做外贸网站百度关键词排名推广
  • 百度上找不到网站优化大师的三大功能
  • 沈阳外贸网站建设网络建站平台
  • 有没有做吉祥物的网站网络公司推广公司
  • app网站公司网络维护
  • 上海百度做网站sem是什么的英文缩写
  • 做网站项目如何实现支付国内快速建站
  • 做护肤品好的网站好郑州网站制作推广公司
  • 做网站创业故事外链代发免费
  • 手机互动网站建设友情链接购买平台
  • 哪些人做数据监测网站星乐seo网站关键词排名优化
  • 女同性怎么做的视频网站网络营销的推广方式
  • 做网站是java还是php百度搜索引擎工作原理
  • wordpress整站导入百度高级搜索功能
  • java做网站开发网站分析案例
  • 做废品回收哪个网站好点移动营销
  • wordpress 万网seo外链是什么
  • 怎么用电脑给域名做网站博客程序seo
  • 抚顺市网站建设网站优化
  • 济源建设工程管理处网站国外比较开放的社交软件