精华内容
下载资源
问答
  • Linux设备总线和驱动之间的关系

    千次阅读 2017-04-04 17:05:48
    本文通过梳理Linux内核源码中的驱动、总线以及设备来帮助理解,Linux驱动与设备之间是通过怎样的方式联系在一起,系统在检测到设备之后怎么找到对应的驱动,驱动在挂载之后如何对应到具体的设备

    (一)、驱动、总线和设备的主要数据结构

    include/linux/device.h)


    /driver/base/base.h

     

    (include/device.h)





    总线中的那两条链表是怎么形成的。内核要求每次出现一个设备就要向总线汇报,或者说注册,每次出现一个驱动,也要向总线汇报,或者说注册。比如系统初始化的时候,会扫描连接了哪些设备,并为每一个设备建立起一个structdevice 的变量,每一次有一个驱动程序,就要准备一个tructdevice_driver 结构的变量。把这些变量统统加入相应的链表,device 插入devices 链表,driver 插入drivers 链表。这样通过总线就能找到每一个设备,每一个驱动。

    设备和驱动又是如何联系?

    原来是把每一个要用的设备在计算机启动之前就已经插好了,插放在它应该在的位置上,然后计算机启动,然后操作系统开始初始化,总线开始扫描设备,每找到一个设备,就为其申请一个structdevice 结构,并且挂入总线中的devices 链表中来,然后每一个驱动程序开始初始化,开始注册其struct device_driver 结构,然后它去总线的devices 链表中去寻找(遍历),去寻找每一个还没有绑定驱动的设备,structdevice 中的structdevice_driver 指针仍为空的设备,然后它会去观察这种设备的特征,看是否是他所支持的设备,如果是,那么调用一个叫做device_bind_driver 的函数,然后他们就结为了秦晋之好。换句话说,把structdevice 中的structdevice_driverdriver 指向这个驱动,而struct device_driver driver 把struct device 加入他的那structklist klist_devices链表中来。就这样,bus、device 和driver,这三者之间或者说他们中的两两之间,就给联系上了。知道其中之一,就能找到另外两个。

    但现在情况变了,出现了一种新的名词,叫热插拔。设备可以在计算机启动以后在插入或者拔出计算机了。设备可以在任何时刻出现,而驱动也可以在任何时刻被加载,所以,出现的情况就是,每当一个structdevice 诞生,它就会去bus 的drivers链表中寻找自己的另一半,反之,每当一个struct device_driver 诞生,它就去bus的devices 链表中寻找它的那些设备。如果找到了合适的,那么OK,和之前那种情况一下,调device_bind_driver 绑定好。如果找不到,没有关系,等待吧!

    (二)、 驱动注册的代码游走








    其中 ptr是指向正被使用的某类型变量指针;type是包含ptr指向的变量类型的结构类型;membertype结构体中的成员,类型与ptr指向的变量类型一样。功能是计算返回包含ptr指向的变量所在的type类型结构变量的指针。(比较拗口)该宏的实现思路:计算type结构体成员member在结构体中的偏移量,然后ptr的地址减去这个偏移量,就得出type结构变量的首地址。该宏的实现方法:1、通过typeof关键字定义一个与type结构体的member成员相同的类型的变量__mptr且将ptr值赋给它。            

    2、用宏offsetof(type,member),获取member成员在type结构中的偏移量(原型:offsetof(TYPE,MEMBER)((size_t)&(TYPE *)0)->MEMBER). 定义在stddef.h.            

    3、最后将__mptr值减去这个偏移量,就得到这个结构变量的地址了(亦指针)。



    (三)、设备注册代码

    以USB检测为例





    中间省略了一部分代码:




    中间省略N行






    (四)、探测函数probe的寻根

    很多驱动都会有probe函数,probe函数不像read和write函数那样是经过调用才执行,probe函数会在驱动安装或设备被发现的时候执行,这就会设计到两方面的内容,probe函数到底是怎样实现的呢?

    经过之前的代码游走之后,可以发现,在驱动安装过程中会调用driver_attach函数,通过该函数去检测对应总线上是否有该驱动所对应的dev设备,如果有就执行驱动中的probe函数。

    对于设备,当设备被发现之后最终也会执行一个叫device_attach函数,通过该函数去寻找对应总线中的设备驱动,当找到对应的设备驱动之后就去执行驱动中的probe函数。


    4.1驱动driver端


    4.2设备device端





    展开全文
  • 总线设备,驱动之间关系

    千次阅读 2018-03-30 20:53:27
    总线设备,驱动之间关系 再看I2C驱动迷茫时候看到《 从需求的角度去理解Linux系列:总线设备和驱动 》,感觉豁然开朗,根据博客内容简单总结一下。 1、总线 总线代表同类设备需要共同遵守的时序,不同总线...

    总线,设备,驱动之间关系

    再看I2C驱动迷茫时候看到《 从需求的角度去理解Linux系列:总线、设备和驱动 》,感觉豁然开朗,根据博客内容简单总结一下。

    1、总线

    总线代表同类设备需要共同遵守的时序,不同总线硬件的通信时序也是不同的,如I2c总线、USB总线、PCI总线……

    2、设备

    设备代表真实存在的物理器件,每个器件有自己不同的通信时序,I2C、USB这些都代表不同的时序,这就与总线挂钩了

    3、驱动

    驱动代表操作设备的方式和流程,以应用来说,在程序open设备时,接着read这个这个设备,驱动就是实现应用访问的具体过程。驱动就是一个通信官和翻译官,一是通过对soc的控制寄存器编程,按总线要求输出相应时序的命令,与设备交互,一是对得到数据进行处理,给上层提供特定格式数据。

    • 不同总线的设备驱动过程不同
    • 同一总线的不同类型设备驱动不同,光感和加速度
    • 同一总线的同类设备驱动不同,多点触控和单点触控

    驱动重点关注操作的流程和方法,如果在驱动中直接针对具体的引脚来编程,其驱动的平台可移植性就比较差,为了提高驱动的可移植性,Linux把驱动要用到的GPIO和中断等资源剥离给设备去管理。即在设备里面包含其自己的设备属性,还包括了其连接到SOC所用到的资源。

    1.1、总线-2

    软件层面总线职责,负责管理设备和驱动

    • 设备和驱动要让系统感知自己,需要向总线注册自己,并且明确自己所属的总线
    • 总线上的设备和驱动匹配,总线充当红娘角色,设备提自己对驱动条件(名字),驱动告知总线自己支持设备条件(型号ID、名字),那设备在注册的时候,总线就会遍历注册在它上面的驱动,找到最适合这个设备的驱动,然后填入设备的结构成员中;驱动注册的时候,总线也会遍历注册在其之上的设备,找到其支持的设备(可以是多个,驱动和设备的关系是1:N),并将设备填入驱动的支持列表中。即总线中match干的事情。
    • 总线匹配后还不知道设备是否正常,这个时候驱动就需要探测一下,即驱动里probe干的事情,他负责与具体设备交互`if(match(device, driver) == OK); driver->probe();

    1.2、驱动-2

    在设备探测成功后,驱动就开始工作了

    • 探测成功,这个时候就可供应用层使用,但是设备文件,so……在probe成功之后,正好可以创建设备文件。通过sysfs文件系统、uevent事件通知机制和后台应用服务mdev程序配合能够成功地在/dev目录创建对应的设备文件
    • 建立VFS和设备文件之间联系,需要open、read、write、ioctl,即要建立和驱动file_operation的结构体关系,这就是cdev_add要干的事情,这样就建立起应用层操作->虚拟文件系统操作->具体文件系统操作->具体设备驱动的操作
      • 在驱动的构体中probe要完成工作就很明朗了,Driver->probe()
        1. 探测设备是否正常
        2. cdev_add(struct file_operations)注册操作接口
        3. device_create()创建设备文件

    4、再来理解IIC设备

    总线上先添加好IIC驱动,i2c.c遍历i2c_boardinfo链表,依次建立i2c_client,并对每一个i2c_client与所有在在这个线上的驱动匹配match,匹配上就调用该驱动的probe函数完成设备文件创建,操作接口注册。

    展开全文
  • 总线和设备

    千次阅读 2014-01-26 22:15:11
    总线是处理器与设备之间的通道。在linux设备模型中,所有的设备都通过总线相连,总线可能是实际的总线,比如usb总线,pci总线,也可能是虚拟的总线。 1.1 数据结构 linux使用bus_type来表示总线。其定义如下: ...

    一、总线

    总线是处理器与设备之间的通道。在linux设备模型中,所有的设备都通过总线相连,总线可能是实际的总线,比如usb总线,pci总线,也可能是虚拟的总线。

    1.1 数据结构

    linux使用bus_type来表示总线。其定义如下:
    struct bus_type {
    	const char		*name;
    	const char		*dev_name;
    	struct device		*dev_root;
    	struct bus_attribute	*bus_attrs;
    	struct device_attribute	*dev_attrs;
    	struct driver_attribute	*drv_attrs;
    	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 (*suspend)(struct device *dev, pm_message_t state);
    	int (*resume)(struct device *dev);
    	const struct dev_pm_ops *pm;
    	struct iommu_ops *iommu_ops;
    	struct subsys_private *p;
    };
    • name:总线的名称
    • dev_name:用于枚举总线下的设备时的总线名称的名称。示例代码dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id)
    • bus_attrs:总线自己的一系列属性。struct bus_attribute是一个自定义的sysfs属性,其中包括了struct attribute以及该属性的show和store方法。
    • dev_attrs:总线下设备的一系列属性。类似于总线自己的属性,这也是一个自定义的sysfs属性,包括了struct attribute以及该属性的show和store方法。在bus_add_device->device_add_attrs中被加入dev目录下。
    • drv_attrs:总线下驱动的一系列属性。类似于总线自己的属性和总线下的设备属性。在bus_add_driver->driver_add_attrs中被加入driver目录下。
    • match:用于检测指定的设备是否可以被指定的驱动处理。在往该总线添加新的驱动或者设备时会调用该函数。如果指定的设备能够被指定的驱动处理,则应该返回一个非0值,否则返回0。
    • uevent:用于处理uevent事件,典型的比如设备的热插拔。函数用于总线对uevent的环境变量添加,但在总线下设备的dev_uevent处理函数也有对它的调用。
    • probe:当新的设备或者驱动被添加到一个总线,并且match返回成功时会调用该函数对新的设备进行初始化,实际上这是一个初始化函数。bus->probe和drv->probe中只会有一个生效,如果两个都存在,则使用bus->probe。
    • remove:当从总线卸载设备或者驱动时,会调用该函数。类似于probe,如果bus->remove和drv->remove都存在,则使用bus->remove。
    • shutdown:在所有设备都关闭时会被调用。类似于remove和probe,如果bus->shutdown和drv->shutdown都存在,则使用bus->shutdown。
    • suspend:当总线上一个设备想要休眠时调用该函数。
    • resume:用于唤醒总线上的一个休眠的设备。
    • pm:电源管理的函数
    • iommu_ops:特定于该总想的和IOMMU 操作函数,用于将IOMMU驱动的实现和总线关联起来,从而允许驱动在做IOMMU时可以进行特定于总线的操作。
    • p:只能由驱动核心使用的私有数据。
    subsys_private定义如下:
    struct subsys_private {
    	struct kset subsys;
    	struct kset *devices_kset;
    	struct list_head interfaces;
    	struct mutex mutex;
    	struct kset *drivers_kset;
    	struct klist klist_devices;
    	struct klist klist_drivers;
    	struct blocking_notifier_head bus_notifier;
    	unsigned int drivers_autoprobe:1;
    	struct bus_type *bus;
    	struct kset glue_dirs;
    	struct class *class;
    };
    该结构将总线同设备、驱动、class以及sysfs关联了起来。其各个域的含义:
    • subsys:该总线在sysfs中的表示。
    • devices_kset:该总线目录下的devices子目录。
    • interfaces:该总线关联的subsysfs_interface接口的链表头。subsysfs_interface提供了一种向设备添加接口的方式,这种接口往往代表一种特定的功能,它不控制设备。其定义也很简单,其中包括了一个add_dev的函数指针和一个remove_dev的函数指针,具体的可以参看实际的代码。
    • mutex:用于保护interfaces链表的互斥锁
    • drivers_kset:该总线目录下的drivers子目录。
    • klist_devices:总线上的设备链表。
    • klist_drivers:总线上的驱动链表。
    • bus_notifier:用于发送总线变化通告的通知链。
    • driver_autoprobe:是否允许device和driver自动匹配,如果允许会在device或者driver注册时就进行匹配工作。
    • bus:指向总线的bus_type类型。
    • class:指向该结构关联的class。

    1.2 添加、删除总线

    可以调用bus_register来添加一个总线到系统中,如果要从系统中删除一个总线,需要调用bus_unregister。为了可以在sysfs中识别出来总线,在添加总线时要给它指定一个名字,添加总线可能失败,所以需要检查返回值。在注册总线时,bus_register会完成:
    1. 创建并初始化总线的subsys_private
    2. 将总线的kset设置为bus_kset(即p->subsys.kobj.kset域)
    3. 将总线的kobj_type设置为bus_ktype(即p->subsys.kobj.ktype域)
    4. 默认使能驱动的自动探测(即设置p->drivers_autoprobe为1)
    5. 初始化总线的kset
    6. 在sysfs中为总线创建bus_attr_uevent文件,该属性文件用于发送uevent事件。
    7. 创建并初始化总线下的设备的kset和驱动的kset
    8. 初始化总线下的设备链表和驱动链表
    9. 如果支持热插拔,则在sysfs中为总线创建bus_attr_drivers_probe和bus_attr_drivers_autoprobe属性文件,其中bus_attr_drivers_probe这个文件用于在热插拔时为指定名字的设备查找并加载驱动,实际上即是加载设备(写该文件执行函数store_drivers_probe,它会试图找到指定名字的设备并找到其驱动)。而bus_attr_drivers_autoprobe用于修改总线的私有数据中的drivers_autoprobe,即是否允许自动加载设备。
    10. 在sysfs中为总线结构中的其它总线属性创建属性文件
    删除总线基本就是该过程的一个逆过程
    总线的kset为全局定义的bus_kset,它在总线系统初始化时(buses_init)被创建和初始化。buses_init完成的工作包括:
    1. 在sys下创建一个名为bus的目录,其kset_uevent_ops被设置为bus_uevent_ops。bus_uevent_ops就提供了一个filter函数。
    2. 在sys下创建一个名为system的目录,其kset_uevent_ops被设置为NULL,并且设置其父kobject为devices_kset的kobject。
    总线的kobj_type是全局定义的一个bus_ktype,它的show和store函数分别设置为bus_attr_show和bus_attr_store。这两个函数的处理很简单:
    1. 首先获得bus_attribute
    2. 检测是否有show(store)函数,如果有就调用。

    1.3 总线的sysfs属性

    总线属性定义如下
    struct bus_attribute {
    	struct attribute	attr;
    	ssize_t (*show)(struct bus_type *bus, char *buf);
    	ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
    };
    该结构很简单,没什么特别之处。总线系统提供了宏BUS_ATTR用于顶一个总线属性,其原型如下:
    #define BUS_ATTR(_name, _mode, _show, _store) struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
    linux设备模型的各个部分都对在sysfs下添加属性文件做了封装,总线系统提供的api如下:
    int __must_check bus_create_file(struct bus_type *, struct bus_attribute *);
    void bus_remove_file(struct bus_type *, struct bus_attribute *);
    它们分别被用于向sysfs中添加属性文件和删除属性文件,分别会调用sysfs框架的sysfs_create_file和sysfs_remove_file函数。

    1.4 总线的其它操作

    1.4.1 遍历设备或者驱动

    为了方便使用,总线提供了两个宏,分别用于枚举总线下的设备和驱动,其原型如下:
    int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *))
    int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *))

    1.4.2 查找设备

    总线框架也提供了在总线下查找设备的功能,其原型如下:
    struct device *bus_find_device(struct bus_type *bus, struct device *start, void *data, int (*match)(struct device *dev, void *data))

    1.4.3 总线引用计数

    总线的引用计数维护在总线的私有数据结构的kobject中,最终使用的是其私有数据结构中的kset的kobject中的引用计数。相关API如下:
    struct bus_type *bus_get(struct bus_type *bus)
    void bus_put(struct bus_type *bus)
    引用计数是linux内核中广泛使用的一项计数,并且实现都是类似的,这也使得我们只要了解其中一个引用计数的实现就可以了解其它引用计数的实现。这里的原理也是相同的,不过需要说明的一点是,在为总线指定kobj_type时,bus_ktype并不包括一个用于释放的函数,这可能是因为总线不支持热插拔吧:))。

    1.4.4 设备链表的保护

    总线下的设备链表和驱动链表都是一个klist类型的链表,这种链表可以添加get和put的保护机制,总线为其中的设备链表提供了保护函数。这些保护函数在对链表进行初始化时提供给了klist,然后在使用klist的api进行遍历操作的时候,klist的代码会自动使用相关的get和put函数。总线为设备链表提供的保护函数分别为:
    void klist_devices_get(struct klist_node *n)
    void klist_devices_put(struct klist_node *n)

    二、设备

    在linux驱动模型中,struct device用于表示所有的设备,设备可能是真实的物理设备,也可能是虚拟的设备。

    2.1 数据结构

    struct device的定义如下:
    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 this
    					   device */
    	void		*platform_data;	/* Platform specific data, device
    					   core doesn't touch it */
    	struct dev_pm_info	power;
    	struct dev_pm_domain	*pm_domain;
    #ifdef CONFIG_NUMA
    	int		numa_node;	/* NUMA node this device is close to */
    #endif
    	u64		*dma_mask;	/* dma mask (if dma'able device) */
    	u64		coherent_dma_mask;/* Like dma_mask, but for
    					     alloc_coherent mappings as
    					     not all hardware supports
    					     64 bit addresses for consistent
    					     allocations such descriptors. */
    	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 mem override */
    #ifdef CONFIG_CMA
    	struct cma *cma_area;		/* contiguous memory area for dma allocations */
    #endif
    	/* arch specific additions */
    	struct dev_archdata	archdata;
    	struct device_node	*of_node; /* associated device tree 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;
    	const struct attribute_group **groups;	/* optional groups */
    	void	(*release)(struct device *dev);
    };
    其中的关键成员及其含义如下:
    • parent:该设备的父设备,即该设备所附着到的那个设备。通常是某种总线或者控制器。如果某个设备的父设备被设置为NULL,则它为一个顶层设备。
    • p: 该设备的私有数据
    • kobj:代表该设备的kobject,用于将该设备添加到kobject层次结构中,通过它就将一个设备添加到了sysfs中。通常,device->kobj->parent和device->parent->kobj是相同的。
    • init_name: 设备的初始化名字
    • type: 设备的类型标识。类似于kobject和kobject_type的关系,它保存了某些类型的设备所共有的信息,这样就不必为每个设备指定这些共有信息,而只需要指定一份,然后将设备的type域指向相应的type即可。
    • mutex:用于互斥该设备驱动的使用。
    • bus:该设备位于那种总线上
    • driver:管理该设备的驱动
    • platform_data: 特定于平台的数据
    • power:用于电源管理的数据结构
    • pm_domain:用于电源管理的函数集
    • numa_node:该设备使用的UMBA节点
    • of_node: 该设备关联的设备树节点
    • devt: 设备的devt编号,用于在sysfs中创建dev
    • id: 设备的实例编号
    • devres_lock: 用于保护该设备所使用资源链表的自旋锁
    • devres_head: 该设备的资源链表
    • knode_class: 用于将设备添加到它所属的class的链表的节点
    • class: 该设备所属的class
    • groups: 设备的可选属性组,包括了设备的可选属性。具体的结构定义可以查看device.h
    • release:当设备的引用计数为0时,也就是不再需要改设备时用于释放该设备的函数。设备的relase函数会被device_release调用,而device_release是device_ktype的release函数。在设备模型中,每个设备在初始化时都会将device_ktype设置为自己的kobj_type,因而设备模型的引用计数也可以使用struct device中的kobject进行,在kobject进行relase时会最终调用到设备自己的release函数。
    struct device_private定义如下:
    struct device_private {
    	struct klist klist_children;
    	struct klist_node knode_parent;
    	struct klist_node knode_driver;
    	struct klist_node knode_bus;
    	struct list_head deferred_probe;
    	void *driver_data;
    	struct device *device;
    };
    • klist_children:该设备的所有子设备所在的链表
    • knode_parent:用于将该设备链接到它的父设备的子链表(即父设备的klist_children)中
    • knode_driver:用于将该设备链接到它所使用的驱动的设备链表中
    • knode_bus:用于将该设备链接到它所在总线的设备链表中
    • deferred_probe:由于缺乏资源而没有加载驱动的设备链表
    • driver_data:用于设备驱动的私有数据指针
    • device:指向该设备的device结构
    struct attribute_group定义如下
    struct attribute_group {
    	const char		*name;
    	umode_t			(*is_visible)(struct kobject *, struct attribute *, int);
    	struct attribute	**attrs;
    };
    • name:属性组的名字
    • is_visible:用于控制attrs中的某个属性是否可见,即是否为其在sysfs中创建一个文件,如果可见,它就返回创建的文件的模式,因而它控制了属性文件的可见性以及访问模式。
    • attrs:属性数组
    struct device_type {  
        const char *name;  
        const struct attribute_group **groups;  
        int (*uevent)(struct device *dev, struct kobj_uevent_env *env);  
        char *(*devnode)(struct device *dev, mode_t *mode);  
        void (*release)(struct device *dev);    
        const struct dev_pm_ops *pm;  
    };  
    • name:类型名字
    • goups:类型属性数组,会在device_add时被添加到sysfs中
    • uevent:该类型的uevent函数,用于发送uevent时准备环境变量
    • devnode:获得设备的devnode
    • release:该类型的释放函数
    • pm:该类型设备的电源管理函数集

    2.2 添加、删除设备

    可以调用device_register来添加一个总线到系统中,如果要从系统中删除一个总线,需要调用device_unregister。device_register需要一个struct device的指针作为参数,在调用它之前,应该保证除了显式设置的域外其它域都为0.其完成的工作包括:
    • 调用device_initialize完成
      • 将设备的kobj.kset设置为devices_kset
      • 将设备的kobj的kobj_type设置为device_ktype,不同于总线子系统的是这里的device_ktype不仅包括了属性的读写函数还包括了包括了用于kobject释放的relase函数。
      • 初始化设备的其它域
    • 调用device_add完成
      • 获取设备的引用计数,如果失败就返回,否则
      • 如果设备的私有数据结构还没有初始化,就调用device_private_init初始化
      • 设置设备的名字(可能来自于初始化名字,也可能来自于总线的dev_name,初始化名字的优先级更高)。如果没有设置初始化名字,就会尝试根据设备所在的总线为设备生成名字。如果设置名字失败就会失败返回。
      • 初始化设备和其父设备的关系
      • 调用kobject_add将设备添加到sysfs中
      • 在sysfs中为设备创建属性文件uevent_attr
      • 如果主设备号不为0,则在sysfs中为其创建属性文件devt_attr
      • 在sysfs中创建相关的符号链接
      • 调用device_add_attrs在sysfs中为设备添加属性文件
      • 调用bus_add_device将设备添加到总线中
      • 调用dpm_sysfs_add在sysfs中未设备创建电源管理相关的属性文件
      • 调用device_pm_add初始化设备的电源管理信息
      • 如果设备是依附于总线的,就调用blocking_notifier_call_chain发送总线变化通告
      • 调用kobject_uevent发送KOBJ_ADD通知给用户空间
      • 调用bus_probe_device为该设备探测驱动
      • 如果该设备有父设备,就将该设备添加到其父设备的子设备链表中
      • 如果设备属于某个class,则将设备添加到该class的设备链表中,并调用该class中的所有接口的add_dev函数。
      • 释放设备的引用计数

    如果主设备号不为0,则除了创建devt_attr属性文件外,还会做一些其它的工作:

    • 调用device_create_sys_dev_entry以在/sys/dev下为设备创建相应的sysfs连接文件,该函数会首先获得该设备的kobject,然后再创建。设备真正的sysfs存在于/sys/devices下
    • 调用devtmpfs_create_node在/dev目录下为设备创建设备文件。这是通过向devtmpfsd内核线程发送一个添加请求实现的。devtmpfsd是一个内核线程,用于维护基于tmpfs的/dev的内容。

    删除设备基本上是添加设备的逆过程。

    devices_kset在设备子系统初始化时(devices_init)创建并初始化,devices_init完成:
    • 创建并初始化kset devices_kset,其kset_uevent_ops被设置为device_uevent_ops,这会在/sys下创建一个devices目录。device_uevent_ops包含了对相关的kobject进行uevent处理的相关函数。device_uevent_ops中的uevent事件处理函数被设置为dev_uevent,该函数会检查并调用设备所属的总线、所属的类以及设备自身的uevent处理函数。
    • 创建并初始化kobject dev_kobj,这会在/sys下创建一个dev目录。
    • 创建并初始化kobject sysfs_dev_block_kobj,其父目录指定为dev_kobj,这会在/sys/dev下创建一个block目录
    • 创建并初始化kobject sysfs_dev_char_kobj,其父目录指定为dev_kobj,这会在/sys/dev下创建一个char目录
    device_ktype指定了kobject的释放函数以及属性的读写函数。其定义如下:
    static struct kobj_type device_ktype = {
    	.release	= device_release,
    	.sysfs_ops	= &dev_sysfs_ops,
    	.namespace	= device_namespace,
    };
    在device_release中,它会依次检查设备的release成员,设备的type->release成员,设备的class->dev_release并使用第一个找到的来完成释放前的清理工作。最后再释放数据结构自身。

    属性的读写函数也很简单,就是查找属性自己的读写函数,然后调用即可。namespace用于获取设备的命名空间,这是通过调用设备所属的class的命名空间函数完成的。

    除了上述API之外,设备子系统还提供了一个简单的API来创建设备文件,其定义如下:

    struct device *device_create(struct class *class, struct device *parent,  dev_t devt, void *drvdata, const char *fmt, ...);

    该函数会完成设备数据结构的创建并初始化该设备,同时将设备添加到sysfs中,这个是最简单的API了。

    2.3 设备的sysfs属性

    设备属性结构定义如下:
    struct device_attribute {
    	struct attribute	attr;
    	ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
    	ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
    };
    该结构很简单,没有什么特别之处。设备系统提供了宏DEVICE_ATTR用来定义一个设备属性,其原型如下:
    #define DEVICE_ATTR(_name, _mode, _show, _store) struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
    linux设备模型的各个部分都对在sysfs下添加属性文件做了封装,设备系统提供的api如下:
    int device_create_file(struct device *device, const struct device_attribute *entry);
    void device_remove_file(struct device *dev, const struct device_attribute *attr);
    int __must_check device_create_bin_file(struct device *dev,	const struct bin_attribute *attr);
    void device_remove_bin_file(struct device *dev, const struct bin_attribute *attr);
    它们分别被用于向sysfs中添加(二进制)属性文件和删除(二进制)属性文件,分别会调用sysfs框架的sysfs_create(_bin)_file和sysfs_remove(_bin)_file函数。在驱动模型之中,只有设备提供了二进制属性的封装,也就是说只有设备子系统支持二进制属性,这是因为二进制属性往往是用于固件升级的目地,否则很少有需求产生一些不可读的信息,而固件是和设备紧密相关联的,因而采用了这种设计。

    2.4 设备的其它操作

    2.4.1 遍历子设备或者查找子设备

    为了方便使用,设备提供了查询其子设备以及遍历子设备链表的API。其原型如下:
    int device_for_each_child(struct device *dev, void *data, int (*fn)(struct device *dev, void *data));
    extern struct device *device_find_child(struct device *dev, void *data, int (*match)(struct device *dev, void *data));

    2.4.2 设备引用计数

    设备的引用计数维护在设备的kobject中,最终使用的是其kobject中的引用计数。相关API如下:
    struct device *get_device(struct device *dev)
    void put_device(struct device *dev)
    这就是一个常规的引用计数,没什么特别之处。

    2.4.3 子设备链表的保护

    设备私有数据结构中的子设备链表是一个klist类型的链表,这种链表可以添加get和put的保护机制,设备系统为该链表提供了保护函数。这些保护函数在对链表进行初始化时提供给了klist,然后在使用klist的api进行遍历操作的时候,klist的代码会自动使用相关的get和put函数。相关保护函数为:
    void klist_children_get(struct klist_node *n)
    void klist_children_put(struct klist_node *n)

    2.4.4 重命名

    如果要修改一个设备的名字,可以使用如下API:
    int device_rename(struct device *dev, const char *new_name);

    展开全文
  • 总线设备和驱动的关系

    千次阅读 2017-05-16 14:21:01
    这里主要简要介绍总线、设备驱动三者之间的关系。 1 一般性概念 Linux设备模型中三个很重要的概念就是总线、设备驱动...总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。

    这里主要简要介绍总线、设备和驱动三者之间的关系。

    一般性概念

    Linux设备模型中三个很重要的概念就是总线、设备和驱动,即busdevicedriver。它们分别对应的数据结构分别为struct bus_typestruct devicestruct device_driver

    总线是处理器与一个或多个设备之间的通道,在设备模型中,所有的设备都通过总线相连。在最底层,Linux系统中的每一个设备都用device结构的一个实例来表示。而驱动则是使总线上的设备能够完成它应该完成的功能。

    在系统中有多种总线,如PCI总线、SCSI总线等。系统中的多个设备和驱动是通过总线让它们联系起来的。在bus_type中两个很重要的成员就是struct kset driversstruct kset devices。它分别代表了连接在这个总线上的两个链,一个是设备链表,另一个则是设备驱动链表。也就是说,通过一个总线描述符,就可以找到挂载到这条总线上的设备,以及支持该总线的不同的设备驱动程序。

    blob.png

    总线、设备和驱动的关系

    2 PCIe/PCI设备与驱动

    PCIe/PCI设备通常由一组参数唯一地标识,它们被vendor IDdevice IDclass nodes所标识,即设备厂商,型号等,这些参数保存在pci_device_id结构中。每个PCIe/PCI设备都会被分配一个pci_dev变量,内核就用这个数据结构来表示一个PCIe /PCI设备。

    所有的PCIe/PCI驱动程序都必须定义一个pci_driver结构变量,在该变量中包含了这个PCIe/PCI驱动程序所提供的不同功能的函数,同时,在这个结构中也包含了一个device_driver结构,这个结构定义了PCIe/PCI子系统与PCIe/PCI设备之间的接口。在注册PCIe/PCI驱动程序时,这个结构将被初始化,同时这个pci_driver变量会被链接到pci_bus_type中的驱动链上去。

    pci_driver中有一个成员struct pci_device_id *id_table,它列出了这个设备驱动程序所能够处理的所有PCIe/PCI设备的ID值。

    总线、设备和驱动的绑定

    在系统启动时,它会对每种类型的总线创建一个描述符,并将使用该总线的设备链接到该总线描述符的devices链上来。也即是说在系统初始化时,它会扫描连接了哪些设备,并且为每个设备建立一个struct device变量,然后将该变量链接到这个设备所连接的总线的描述符上去。另一方面,每当加载了一个设备驱动,则系统也会准备一个struct device_driver结构的变量,然后再将这个变量也链接到它所在总线的描述符的drivers链上去。

    对于设备来说,在结构体struct device中有两个重要的成员,一个是struct bus_type *bus,另一个是struct device_driver *driverbus成员就表示该设备是链接到哪一个总线上的,而driver成员就表示当前设备是由哪个驱动程序所驱动的。对于驱动程序来说,在结构体struct device_driver中也有两个成员,struct bus_type *busstruct list_head devices,这里的bus成员也是指向这个驱动是链接到哪个总线上的,而devices这个链表则是表示当前这个驱动程序可以去进行驱动的那些设备。一个驱动程序可以支持一个或多个设备,而一个设备则只会绑定给一个驱动程序。

    对于devicedevice_driver之间建立联系的方式,主要有两种方式。第一种,在计算机启动的时候,总线开始扫描连接在其上的设备,为每个设备建立一个struct device变量并链接到该总线的devices链上,然后开始初始化不同的驱动程序,驱动程序到它所在的总线的devices链上去遍历每一个还没有被绑定给某个驱动的设备,然后再查看是否能够支持这种设备,如果它能够支持这种设备,则将这个设备与这个驱动联系起来。即,将这个设备的device变量加到驱动的devices链上,同时让struct device中的device_driver指向当前这个驱动。第二种则是热插拔。也即是在系统运行时插入了设备,此时内核会去查找在该bus链上注册了的device_driver,然后再将设备与驱动联系起来。设备与驱动根据什么规则联系起来,它们是如何被联系起来的代码我们将在后面的章节进行详细的描述。

    4 PCIe/PCI设备与驱动的绑定过程

    a)在系统启动的时候,PCIe/PCI总线会去扫描连接到这个总线上的设备,同时为每一个设备建立一个pci_dev结构,在这个结构中有一个device成员,并将这些pci_dev结构链接到PCI总线描述符上的devices链。如下图所示:

    blob.png

    图2 设备与驱动的绑定过程1

    b)当PCIe/PC驱动被加载时,pci_driver结构体将被初始化,这一过程在函数pci_register_driver中:

    drv->driver.bus = &pci_bus_type;

    drv->driver.probe = pci_device_probe;

    最后会调用driver_register(&drv->driver)将这个PCIe/PC驱动挂载到总线描述符的驱动链上。同时在注册的过程中,会根据pci_driver中的id_table中的ID值去查看该驱动支持哪些设备,将这些设备挂载到pci_driver中的devices链中来。如下图所示:

    blob.png

    图3 设备与驱动的绑定过程2

    c)对于不同的设备,可能驱动程序也不一样,因此,对于上图中的Dev3,可能就需要另外一个驱动程序来对其进行驱动。所以当加载了Dev3的驱动程序时,其示意图如下图所示:

    blob.png

    图4 设备与驱动的绑定过程3

    展开全文
  • 总线设备驱动模型其实现主要是基于Kobjectsysfs等机制,对于驱动模型程序开发主要是理解三个元素:总线、设备、驱动的关系。三者之间因为一定的联系性实现对设备的控制。
  • 总线设备驱动例码

    2015-09-21 00:09:41
    总线是处理器和设备之间的通道,在设备模型中,所有的设备都通过总线相连,以总线来管理设备驱动函数
  • USBCAN总线转换设备作为USB总线和CAN总线之间的连接设备,其运行状况软件设计是否能达到设计的需求,一般的方法很难进行验证,本文通过在Stateflow工具箱下对一种USBCAN总线转换设备进行建模,并且在Matlab/Simulink...
  • 总线设备驱动模型其实现主要是基于Kobjectsysfs等机制,对于驱动模型程序开发主要是理解三个元素:总线、设备、驱动的关系。三者之间因为一定的联系性实现对设备的控制。 首先是总线,总线是三者联系起来的基础,...
  • linux设备模型之总线 设备 驱动

    千次阅读 2012-04-28 22:43:24
    设备模型的上层建筑由总线(bus) 、设备(device)、 驱动(device_driver)这3个数据结构构成,设备模型表示了它们之间的连接关系。 在设备模型中,所有的设备都通过总线连接。总线可以是物理存在的,也可以是虚拟的。...
  • 微信公众号:嵌入式企鹅圈重磅原创文章。这是一篇有关如何学习嵌入式Linux系统的方法论文章,也是从需求的角度去理解Linux系统软件的开篇,相信...本文重点阐述从需求的角度去理解linux总线设备和驱动的关系分工。
  • 总线设备和驱动 网址:http://blog.chinaunix.net/uid-25014876-id-109733.html xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • 设备模型: 随着技术的不断进步,系统的拓扑结构也越来越复杂,对智能电源管理,热插拔的支持要求也越来越高, 2.4内核已经难以满足这些要求,为了适应这种...设备 之间的 通道 ,在设备模型中,所有的设备都通过总线
  • 要在计算机上实现与机载设备的ARINC429总线数据通信,必须实现429总线与计算机总线之间的数据传输。本文设计了基于USB总线的便携式ARINC429总线通信设备,并通过实际运行测试,对该设备的可靠性稳定性进行了验证。
  • 总线总线是处理器一个或多个设备之间的通道,在设备模型中, 所有的设备都通过总线相连, 甚至是内部的虚拟"platform"总线总线可以相互插入。设备模型展示了总线和它们所控制的设备之间的实际连接。在 Linux 设备...
  • 电信设备-信息处理装置之间的异构多总线数据传输方法.zip
  • 在工业数据通信领域,总线是指由导线组成的传输线束,连接多个传感器执行器,实现各部件之间传送信息的公共通信干线。 然后再来看国际电工技术委员会(IEC)在IEC 61158中对现场总线的标准定义: 现场总线是安装在...
  • 总线设备、驱动,也就是bus、device、driver,在内核里都有对应的结构体,在include/linux/device.h 里定义。 Device.h (linux-3.4.2\include\linux) 1、总线设备、驱动三者的关系 2、结构体 (1)总线...
  • 本文设计了基于USB总线的便携式ARINC429总线通信设备,并通过实际运行测试,对该设备的可靠性稳定性进行了验证。  1系统总体设计  1.1系统功能分析  该系统主要分为3大功能单元:中央控制单元、429数据收发...
  • 同步总线和异步总线

    千次阅读 2020-04-02 20:27:22
    同步总线:指互联的部件或设备均通过统一的时钟进行同步,即所有的互联的部件或设备都必须使用同一个时钟(同步时钟),在规定的时钟节拍内进行规定的总线操作,来完成部件或设备之间的信息交换。 异步总线:指没有...
  • 摘要:本文简要介绍了Modbus通讯协议 现场总线技术,重点阐述了综采工作面大型设备实现远程监测监控...主要应用于工业现场可以在微机集控设备之间实现双向串行多节点数字通讯。  为了更大程度地提高矿井安全、迎合未
  • 总线的定义, 并行总线和串行总线

    千次阅读 2020-09-24 08:47:17
    采用总线结构便于部件和设备的扩充,尤其制定了统一的总线标准则容易使不同设备间实现互连。  微机中总线一般有内部总线、系统总线和外部总线。内部总线是微机内部各外围芯片与处理器之间总线,用于芯片一级的...
  • 从需求的角度去理解Linux系列:总线设备和驱动

    千次阅读 多人点赞 2015-09-23 21:08:52
    操作系统是为了给应用层提供良好的接口而进行总线设备驱动管理、内存管理、文件管理、进程管理等等。总线设备驱动管理就是我们今天要谈的主题。Linux平台有各种子系统、各种总线、各种驱动,Linux系统对它们的管理...
  • 汽车中的电子部件越来越多,光是ECU就有几十个,这么多的电子...目前,有四种主流的车用总线:CAN总线、LIN总线、FlexRay总线和MOST总线。 用一张表格来说明各种总线的区别 LIN总线 LIN(Local Interconnec...
  • 基于DeviceNet总线的泵/阀控制器从设备通信适配器是用于智能泵/阀控制器与DeviceNet网络之间的通信协议设备,用于在智能泵/阀控制器DeviceNet网络主设备之间交换I/O数据其它设定数据。
  • Linux总线设备、驱动模型

    千次阅读 2017-03-04 21:52:41
    一、Linux总线设备驱动模型框架 从Linux2.6开始Linux加入了一套驱动管理注册机制—platform平台总线驱动模型。platform平台总线是一条虚拟总线,platform_device为相应的设备,platform_driver为相应的驱动。与...
  • linux设备驱动——总线设备、驱动

    千次阅读 2012-03-27 11:05:53
    2.6 版本内核是如何管理总线,驱动,设备之间的关系的,关于bus_type、device_driver、device这三个内核结构在内核代码中可以找到。由于这三个结构的重要性,我们在这里先将它们贴出来;  1、设备结构的定义...
  • 现场总线和应用

    2015-04-23 07:31:20
    1.现场总线控制系统的结构及其特点 现场总线是一种串行的数字数据通讯链路,它沟通了生产过程领域的基本控制设备(即现场级设备之间以及更高层次自动控制领域的自动化控制设备(即车间级设备之间的联系。
  • 采用总线结构便于部件和设备的扩充,尤其制定了统一的总线标准则容易使不同设备间实现互连。  微机中总线一般有内部总线、系统总线和外部总线。  (1)、内部总线是微机内部各外围芯片与处理器之间总线,用于...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 90,008
精华内容 36,003
关键字:

总线和设备之间