目录

一、概述

二 、BUS

2.1 说明

2.2 数据抽象

2.3 接口

2.4 实现

三、device

3.1 说明

3.2 数据抽象

3.3 接口

3.4 实现

四、driver

4.1 说明

4.2 数据抽象

4.3 接口

4.4 实现

五、class

5.1 说明

5.2 数据抽象

5.3 接口

5.4 实现

六、小结


一、概述

驱动模型描述的是bus,driver,device三者的关系。

二 、BUS

2.1 说明

 * A bus is a channel between the processor and one or more devices. For the* purposes of the device model, all devices are connected via a bus, even if* it is an internal, virtual, "platform" bus. Buses can plug into each other.* A USB controller is usually a PCI device, for example. The device model* represents the actual connections between buses and the devices they control.* A bus is represented by the bus_type structure. It contains the name, the* default attributes, the bus' methods, PM operations, and the driver core's* private data.*/

2.2 数据抽象

[linux/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);int (*num_vf)(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;
};

2.3 接口

  • int bus_register(struct bus_type *bus);

2.4 实现

通过分析看一下bus注册后怎么将bus信息通过sysfs构建起来

[drivers/base/bus.c]

int __init buses_init(void)
{bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);if (!bus_kset)return -ENOMEM;system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);if (!system_kset)return -ENOMEM;return 0;
}

根据在Linux设备驱动——驱动模型之kobject/kset中的分析,上面两次调用是顶层的kset,对“bus”来说参数指定为NULL,说明其在/sys的根下,而“system”位于devices_kset下的kset,后面会讲到devices_kset是device顶级kset

[root@localhost ~]# ls /sys/
block  bus  class  dev  devices  firmware  fs  hypervisor  kernel  module  power

[root@localhost ~]# ls /sys/devices/
breakpoint  cpu  LNXSYSTM:00  msr  pci0000:00  platform  pnp0  power  software  system  tracepoint  virtual

接下来分析bus注册及过程中的sys文件系统的变化,假设新注册的总线名称为“new_bus”

int bus_register(struct bus_type *bus)
{retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);if (retval)goto out;priv->subsys.kobj.kset = bus_kset;priv->subsys.kobj.ktype = &bus_ktype;priv->drivers_autoprobe = 1;retval = kset_register(&priv->subsys);
}

第一个片段,为该bus创建一个kset,所有总线都属于顶级总线kset

另外,在调用kset_registger时,指定了name,ktype,kset(其实就是parent了),这时候,在sys中出现:

  • /sys/bus/new_bus
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)goto bus_uevent_fail;priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);
if (!priv->devices_kset) {retval = -ENOMEM;goto bus_devices_fail;
}priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);
if (!priv->drivers_kset) {retval = -ENOMEM;goto bus_drivers_fail;
}

接下来通过bus_create_file创建event文件

  • /sys/bus/new_bus/event

然后通过kset_create_and_add在当前new_bus下创建devices,和drivers的kset

  • /sys/bus/new_bus/devices
  • /sys/bus/new_bus/drivers
 retval = add_probe_files(bus);if (retval)goto bus_probe_files_fail;retval = bus_add_groups(bus, bus->bus_groups);if (retval)goto bus_groups_fail;

最后通过add_probe_files创建probe和autoprobe

  • /sys/bus/new_bus/drivers_probe
  • /sys/bus/new_bus/drivers_autoprobe

bus_add_groups创建文件属性

以i2c为例,看下bus注册后的结构:

[root@localhost ~]# tree /sys/bus/i2c/
/sys/bus/i2c/
├── devices
├── drivers
│   └── dummy
│       ├── bind
│       ├── module -> ../../../../module/i2c_core
│       ├── uevent
│       └── unbind
├── drivers_autoprobe
├── drivers_probe
└── uevent

kset的情况画了个图:

相同颜色代表属于一个kset。

三、device

3.1 说明

* At the lowest level, every device in a Linux system is represented by an* instance of struct device. The device structure contains the information* that the device model core needs to model the system. Most subsystems,* however, track additional information about the devices they host. As a* result, it is rare for devices to be represented by bare device structures;* instead, that structure, like kobject structures, is usually embedded within* a higher-level representation of the device.*/

3.2 数据抽象

[include/linux/device.h]

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, devicecore doesn't touch it */void       *driver_data;   /* Driver data, set and get withdev_set/get_drvdata */struct dev_links_info links;struct dev_pm_info    power;struct dev_pm_domain  *pm_domain;#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAINstruct irq_domain    *msi_domain;
#endif
#ifdef CONFIG_PINCTRLstruct dev_pin_info    *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQstruct list_head   msi_list;
#endif#ifdef CONFIG_NUMAint     numa_node;  /* NUMA node this device is close to */
#endifconst struct dma_map_ops *dma_ops;u64     *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;const struct attribute_group **groups;   /* optional groups */void   (*release)(struct device *dev);struct iommu_group   *iommu_group;struct iommu_fwspec    *iommu_fwspec;bool          offline_disabled:1;bool         offline:1;
};

3.3 接口

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

3.4 实现

[driver/base/core.c]

int __init devices_init(void)
{devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);if (!devices_kset)return -ENOMEM;dev_kobj = kobject_create_and_add("dev", NULL);if (!dev_kobj)goto dev_kobj_err;sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);if (!sysfs_dev_block_kobj)goto block_kobj_err;sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);if (!sysfs_dev_char_kobj)goto char_kobj_err;
}

在device初始化时,创建devices_kset容器,这在/sys/下建立devices文件夹,接着是dev,在dev下建立block,char文件夹

[root@localhost ~]# ls /sys/
block  bus  class  dev  devices  firmware  fs  hypervisor  kernel  module  power

[root@localhost ~]# ls /sys/dev
block  char

接下来看设备是如何注册的,假设设备名称new_device

[driver/base/core.c]

int device_register(struct device *dev)
{device_initialize(dev);return device_add(dev);
}

device是基于kobject的,我们看它是如何操作的:

void device_initialize(struct device *dev)
{dev->kobj.kset = devices_kset;  /* 注意这 */kobject_init(&dev->kobj, &device_ktype);INIT_LIST_HEAD(&dev->dma_pools);mutex_init(&dev->mutex);lockdep_set_novalidate_class(&dev->mutex);spin_lock_init(&dev->devres_lock);INIT_LIST_HEAD(&dev->devres_head);device_pm_init(dev);set_dev_node(dev, -1);INIT_LIST_HEAD(&dev->links.consumers);INIT_LIST_HEAD(&dev->links.suppliers);dev->links.status = DL_DEV_NO_DRIVER;
}

可以看出,这里指定了ktype,并且该device的parent默认为顶级device下的kset

上述片段,展示了初始化基类kobj的过程,名称遵循dev->init_name, dev->bus->dev_name的过程

  • /sys/devices/new_device

接下来是:

 error = device_create_file(dev, &dev_attr_uevent);error = device_add_class_symlinks(dev);error = device_add_attrs(dev);error = bus_add_device(dev);error = dpm_sysfs_add(dev);device_pm_add(dev);
  • bus_add_device 将device加入到对应的bus中klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

最后:

 if (MAJOR(dev->devt)) {error = device_create_file(dev, &dev_attr_dev);if (error)goto DevAttrError;error = device_create_sys_dev_entry(dev);if (error)goto SysEntryError;devtmpfs_create_node(dev);}if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);kobject_uevent(&dev->kobj, KOBJ_ADD);bus_probe_device(dev);if (parent)klist_add_tail(&dev->p->knode_parent,&parent->p->klist_children);if (dev->class) {mutex_lock(&dev->class->p->mutex);/* tie the class to the device */klist_add_tail(&dev->knode_class,&dev->class->p->klist_devices);/* notify any interfaces that the device is here */list_for_each_entry(class_intf,&dev->class->p->interfaces, node)if (class_intf->add_dev)class_intf->add_dev(dev, class_intf);mutex_unlock(&dev->class->p->mutex);
}

上面的函数我们重点关注bus_probe_device->device_initial_probe->__device_attach:

[driver/base/Dd.c]

static int __device_attach(struct device *dev, bool allow_async)
{int ret = 0;device_lock(dev);if (dev->driver) {if (device_is_bound(dev)) {ret = 1;goto out_unlock;}ret = device_bind_driver(dev);if (ret == 0)ret = 1;else {dev->driver = NULL;ret = 0;}} else {struct device_attach_data data = {.dev = dev,.check_async = allow_async,.want_async = false,};...ret = bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver);...}
out_unlock:device_unlock(dev);return ret;
}

上面的函数是设备注册的核心——寻找并绑定对应的驱动,看下匹配driver的过程__device_attach_driver

首先进行driver_match_device,这个就是bus上的match

static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

如果driver和device成功的match,接下来就要driver_probe_device

static int really_probe(struct device *dev, struct device_driver *drv)
{
...
re_probe:dev->driver = drv;...if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}...if (dev->bus->probe) {ret = dev->bus->probe(dev);if (ret)goto probe_failed;} else if (drv->probe) {ret = drv->probe(dev);if (ret)goto probe_failed;}...driver_bound(dev);...
}
  • 关键步骤dev->driver = drv 此时device和driver就绑定在一起了!
  • 接下来按照优先级执行dev->bus->probe, drv->probe

四、driver

4.1 说明

 * The device driver-model tracks all of the drivers known to the system.* The main reason for this tracking is to enable the driver core to match* up drivers with new devices. Once drivers are known objects within the* system, however, a number of other things become possible. Device drivers* can export information and configuration variables that are independent* of any specific device.*/

4.2 数据抽象

[include/linux/device.h]

struct device_driver {const char     *name;struct bus_type       *bus;struct module      *owner;const char       *mod_name;  /* used for built-in modules */bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */enum probe_type probe_type;const struct of_device_id    *of_match_table;const struct acpi_device_id *acpi_match_table;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 attribute_group **groups;const struct dev_pm_ops *pm;struct driver_private *p;
};

4.3 接口

  • int driver_register(struct device_driver *drv)

4.4 实现

具体看一下:

int driver_register(struct device_driver *drv)
{int ret;struct device_driver *other;BUG_ON(!drv->bus->p);...other = driver_find(drv->name, drv->bus);ret = bus_add_driver(drv);ret = driver_add_groups(drv, drv->groups);kobject_uevent(&drv->p->kobj, KOBJ_ADD);return ret;
}

driver_find确定当前driver有没有注册过

int bus_add_driver(struct device_driver *drv)
{struct bus_type *bus;struct driver_private *priv;int error = 0;bus = bus_get(drv->bus);if (!bus)return -EINVAL;pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);priv = kzalloc(sizeof(*priv), GFP_KERNEL);if (!priv) {error = -ENOMEM;goto out_put_bus;}klist_init(&priv->klist_devices, NULL, NULL);priv->driver = drv;drv->p = priv;priv->kobj.kset = bus->p->drivers_kset;error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name);if (error)goto out_unregister;klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);if (drv->bus->p->drivers_autoprobe) {if (driver_allows_async_probing(drv)) {pr_debug("bus: '%s': probing driver %s asynchronously\n",drv->bus->name, drv->name);async_schedule(driver_attach_async, drv);} else {error = driver_attach(drv);if (error)goto out_unregister;}}module_add_driver(drv->owner, drv);error = driver_create_file(drv, &driver_attr_uevent);if (error) {printk(KERN_ERR "%s: uevent attr (%s) failed\n",__func__, drv->name);}error = driver_add_groups(drv, bus->drv_groups);if (error) {/* How the hell do we get out of this pickle? Give up */printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",__func__, drv->name);}if (!drv->suppress_bind_attrs) {error = add_bind_files(drv);if (error) {/* Ditto */printk(KERN_ERR "%s: add_bind_files(%s) failed\n",__func__, drv->name);}}return 0;
}

简单列一下函数做的几件事:

  1. 分配driver_private,在对应bus_type的driver下建立文件
  2. driver纳入bus_type的管理
  3. driver_attach,这些内容在device中已经说过了。

五、class

5.1 说明

 /** A class is a higher-level view of a device that abstracts out low-level* implementation details. Drivers may see a SCSI disk or an ATA disk, but,* at the class level, they are all simply disks. Classes allow user space* to work with devices based on what they do, rather than how they are* connected or how they work.*/

class是更高层次对设备的抽象。

5.2 数据抽象

[inclde/linux/device.h]

struct class {const char     *name;struct module     *owner;const struct attribute_group **class_groups;const struct attribute_group **dev_groups;struct kobject         *dev_kobj;int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, umode_t *mode);void (*class_release)(struct class *class);void (*dev_release)(struct device *dev);int (*shutdown_pre)(struct device *dev);const struct kobj_ns_type_operations *ns_type;const void *(*namespace)(struct device *dev);const struct dev_pm_ops *pm;struct subsys_private *p;
};

5.3 接口

  • class_register

5.4 实现

[driver/base/class.c]

int __init classes_init(void)
{class_kset = kset_create_and_add("class", NULL, NULL);if (!class_kset)return -ENOMEM;return 0;
}

再来看class的注册:

int __class_register(struct class *cls, struct lock_class_key *key)
{struct subsys_private *cp;int error;pr_debug("device class '%s': registering\n", cls->name);cp = kzalloc(sizeof(*cp), GFP_KERNEL);if (!cp)return -ENOMEM;klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);INIT_LIST_HEAD(&cp->interfaces);kset_init(&cp->glue_dirs);__mutex_init(&cp->mutex, "subsys mutex", key);error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);if (error) {kfree(cp);return error;}/* set the default /sys/dev directory for devices of this class */if (!cls->dev_kobj)cls->dev_kobj = sysfs_dev_char_kobj;#if defined(CONFIG_BLOCK)/* let the block class directory show up in the root of sysfs */if (!sysfs_deprecated || cls != &block_class)cp->subsys.kobj.kset = class_kset;
#elsecp->subsys.kobj.kset = class_kset;
#endifcp->subsys.kobj.ktype = &class_ktype;cp->class = cls;cls->p = cp;error = kset_register(&cp->subsys);if (error) {kfree(cp);return error;}error = class_add_groups(class_get(cls), cls->class_groups);class_put(cls);error = add_class_attrs(class_get(cls));class_put(cls);return error;
}

六、小结

上面描述了驱动模型各个部件之间的关系,实际上就是完成在特定bus_type下,device和driver的attach在一起,这是通过match行为完成的,随后执行probe的过程,这个过程发生在:

  1. 设备注册
  2. 驱动注册

具体来说,device的attach就是遍历bus下所有的driver,执行driver->bus->match匹配对应的driver。driver的attach则是遍历bus下所有的设备,同样执行driver->bus->match匹配对应的device。无论从哪个角度进行attach,一旦match成功就将device->driver指向具体的driver。此时代表二者attach完成。

attach完成,也反应在sysfs上:

static int driver_sysfs_add(struct device *dev)
{int ret;if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_BIND_DRIVER, dev);ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,kobject_name(&dev->kobj));if (ret == 0) {ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,"driver");if (ret)sysfs_remove_link(&dev->driver->p->kobj,kobject_name(&dev->kobj));}return ret;
}

可以看到,主要建立了两个软连接,这里用图进行说明:

这之后,调用probe进一步对device进行探测。

Linux设备驱动——驱动模型相关推荐

  1. Linux设备与驱动学习之----什么是设备

    [ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在学习Linux设备驱动的过程中我们用到也是看到最多的就是设备和驱动了,接下 ...

  2. Linux 设备总线驱动模型(转载)

    尽管LDD3中说对多数程序员掌握设备驱动模型不是必要的,但对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要.     Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一 ...

  3. linux设备和驱动加载的先后顺序

    点击打开链接 Linux驱动先注册总线,总线上可以先挂device,也可以先挂driver,那么究竟怎么控制先后的顺序呢. Linux系统使用两种方式去加载系统中的模块:动态和静态. 静态加载:将所有 ...

  4. linux设备和驱动注册,Linux驱动第五篇-----驱动注册和生成设备节点

    加载驱动的指令是:insmod xx.ko 查看驱动的指令是: lsmod 卸载驱动的指令是:rmmod xx 在include/linux/platform_device.h这个文件中定义了平台驱动 ...

  5. 宋宝华:Linux设备与驱动的手动解绑与手动绑定

    众所周知,Linux靠设备与驱动之间的match,来完成设备与驱动的bind,从而触发驱动的probe()成员函数被执行.每个bus都有相应的match方法,完成match的总的入口函数是: stat ...

  6. linux设备和驱动匹配的方法,Linux使用设备树的i2c驱动与设备匹配方式

    Linux使用设备树的i2c驱动与设备匹配有3种方式: of_driver_match_device acpi_driver_match_device i2c_match_id 源码: static ...

  7. Linux设备与驱动学习之----什么是驱动

    [ 声明:版权所有,欢迎转载,转载请注明出处,请勿用于商业用途] [ 声明:本文属于作者个人理解,如有错误,欢迎大家指正] 在上一篇博文中我们介绍了 Linux 中的device 概念,这篇博文中我们 ...

  8. linux 设备节点 驱动,【Linux驱动】自动创建设备节点

    开始学习驱动的时候,是将驱动程序编译成模块然后用mknod命令手动建立设备节点以提供给应用程序调用.这对于刚开始调试驱动程序的时候常用的一种方法.但是,当有种需要必须在系统启动的时候就将驱动程序就绪, ...

  9. linux设备树 驱动,(9条消息)zynq linux驱动之使用设备树开发

    PC:Windows 10 虚拟机:ubuntu 16.04 vivado:2017.04 PetaLinux:2017.04 开发板:黑金AX7010 根文件系统:debian8 --------- ...

  10. 一张图说明linux 设备 节点 驱动 主设备号 和次设备号之间的关系

    Linux各种设备都以文件的形式存放在/dev目录下,称为设备文件. 应用程序可以打开.关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样.为了管理这些设备,系统为设备编了号,每个设 ...

最新文章

  1. PyTorch手把手自定义Dataloader读取数据
  2. 「斐波那契」投稿法不好使了:IJCAI 2020出台新规,隐瞒「拒稿重投」者将被一票否决...
  3. LabviewRS232串口通信数据格式问题解析
  4. 全球及中国小水电行业投资规模及运行动态分析报告2021年版
  5. 获取的官方例程后怎么开发_开发商败诉后拒不赔偿怎么办,房地产纠纷处理方式有哪些?...
  6. 逆向 EasyBase64
  7. NodeJS开发c++扩展模块
  8. 如何从DOS命令窗口进行复制粘贴
  9. 如何用java更改网页图片,java如何修改文档第一页为不同的页面
  10. Photoshop通道抠出散乱的儿童头发
  11. mysql sql语句集合
  12. 如何清除 MacBook 上的浏览​​器缓存
  13. 你的脸儿圆又圆改变MSN头像(转)
  14. iperf 安卓 灌包命令_iperf工具测速
  15. IDEA设置方法注释模板
  16. java字符串长度解答
  17. win7计算机自动关机设置在哪里设置方法,win7系统怎么设置每天自动关机|win7创建定时关机计划的方法...
  18. 利用LM317的LED恒流源电路图
  19. Ubuntu上安装QQ,无法显示图片和头像
  20. 华为鸿蒙认证测试题,你能答对几道?

热门文章

  1. 【转】c语言位域操作—_结构体内冒号:的使用
  2. bnu 29064, 期望 水题
  3. 用友CDM系统“货位间商品移库单(一步)”表体增加“货位可用数量”字段,根据表头的选择的货位自动带出数值...
  4. Caused by: org.hibernate.HibernateException: unknown Oracle major version [0]
  5. ASP.net:URL重写实现IHttpHandler接口
  6. 使用Spark core和SparkSQL的窗口函数分别实现分组取topN的操作
  7. 基于Vue.js 2.x系列 + Element UI + RBAC/AUTH权限 的响应式后台管理系统
  8. Apache2 实现https访问http服务
  9. 极限编程的12个实践原则
  10. Python web实时消息服务器后台推送技术方案---GoEasy