前言

在linux设备驱动模型中,总线可以看作是linux设备模型的核心,系统中的其他设备以及驱动都是以总线为核心围绕。不过驱动程序员在系统中创建一条总线的机会并不多。驱动模型中的总线可以是真是存在的物理总线(USB总线,I2C总线,PCI总线),也可以是为了驱动模型架构设计出的虚拟总线(Platform总线)。为此linux设备驱动模型都将围绕"总线--设备--驱动"来展开,因为符合linux设备驱动模型的设备与驱动都是必须挂载在一个总线上的,无论是实际存在的或者虚拟的。

数据结构

在详细说明bus开始的时候,先需要说明其数据结构,因为一个好的数据结构,就可以很大程度上了解其功能,内核中使用bus_type代表一个总线。
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;
};

.name:            总线的名称,比如i2c, spi

.dev_name:    通常用户枚举设备的名称,比如常见的i2c-0, i2c-1....
.dev_root:       该bus默认的父设备。
.dev_attrs:      此bus设备上默认的属性。
.bus_groups,  dev_groups,  drv_groups:  分别是总线, 设备,驱动的属性。
.match:          当一个设备或者驱动添加到此总线上的时候,bus就会调用match对设备和驱动一一匹配的。
.uevent:         当该bus下的任何设备,驱动发生增加,删除的操作时,就会调用到uevnet函数。
.probe,remove:           当一个driver添加到系统的时候,是先会调用bus的probe的,如果bus的probe都没有初始化,则bus下的任何驱动或者设备都无法使用。但是不是所有的bus都会实现probe函数的,虚拟的bus是不存在初始化的。
.supend,resume:  bus电源管理回调函数。
.pm:              一组电源管理的回调函数。
p:                 一个用来管理总线上设备与驱动的数据结构。
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;
};

.subsys:            用来表示bus所在的子系统,系统中所有注册的bus都将指向bus_kset。

.devices_kset:   代表该bus下所有设备的集合。
.drivers_kset:     代表该bus下所有驱动的集合。
.interfaces:       用于保存该bus下所有的interface。
.klist_devices:  代表该bus下所有设备的链表。
.klist_drivers:    代表该bus下所有驱动的链表。
.bus_notifier:    该bus创建的通知链。
.drivers_autoprobe:  代表在该bus下注册某一个驱动或者设备的时候,是否需要自动匹配
.bus:                指向所属的bus。
.class:              指向所属的class。

BUS相关的函数

  • buses_init(创建bus/system集合)
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;
}

通过上述的操作将在sysfs下创建了一个名字为"bus"的目录,同时也会在/sys/devices/下创建一个名字为"system"的目录。

在前面kset学习中知道,当这个名为bus的kset下有状态变化的时候,就会发送uevent消息的,在发送过程中就会调用到bus_uevent_ops中实现的函数。
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{struct kobj_type *ktype = get_ktype(kobj);if (ktype == &bus_ktype)return 1;return 0;
}static const struct kset_uevent_ops bus_uevent_ops = {.filter = bus_uevent_filter,
};

在bus_uevent_ops中只实现了filter函数。此函数只要是判断发生状态的kobj对象类型是不是总线类型,不是就不会上报该事件的。

  • bus_register(注册一个总线到系统中)
int bus_register(struct bus_type *bus)
{int retval;struct subsys_private *priv;struct lock_class_key *key = &bus->lock_key;priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);         //分配subsys_private结构体if (!priv)return -ENOMEM;priv->bus = bus;                                        //设置bus指针bus->p = priv;                                          //设置bus->p指针BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);       //初始化bus通知链retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);  //设置该bus的名字,体现在/sys/bus/下if (retval)goto out;priv->subsys.kobj.kset = bus_kset;         //设置该bus所属的kset, 以及ktypepriv->subsys.kobj.ktype = &bus_ktype;priv->drivers_autoprobe = 1;retval = kset_register(&priv->subsys);     //注册该kset到系统中,表现在/sys/bus下if (retval)goto out;retval = bus_create_file(bus, &bus_attr_uevent); //创建该bus的uevent属性if (retval)goto bus_uevent_fail;priv->devices_kset = kset_create_and_add("devices", NULL,        &priv->subsys.kobj);  //在该bus下创建devices目录if (!priv->devices_kset) {retval = -ENOMEM;goto bus_devices_fail;}priv->drivers_kset = kset_create_and_add("drivers", NULL,&priv->subsys.kobj);  //在该bus下创建drivers目录if (!priv->drivers_kset) {retval = -ENOMEM;goto bus_drivers_fail;}INIT_LIST_HEAD(&priv->interfaces);          //初始化interface, mutex, klist_devices, klist_drivers__mutex_init(&priv->mutex, "subsys mutex", key);klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);klist_init(&priv->klist_drivers, NULL, NULL);retval = add_probe_files(bus);      //创建bus的probe属性if (retval)goto bus_probe_files_fail;retval = bus_add_groups(bus, bus->bus_groups);  //创建bus的属性if (retval)goto bus_groups_fail;pr_debug("bus: '%s': registered\n", bus->name);return 0;
}

比如常见的通过bus_register创建一个i2c总线,就会在/sys/bus/下创建i2c目录, 以及devices目录, driver目录, 以及probe属性, uevent属性。

root@test:/sys/bus/i2c # ls -l
drwxr-xr-x root     root              2012-01-01 08:17 devices
drwxr-xr-x root     root              2012-01-01 08:17 drivers
-rw-r--r-- root     root         4096 2012-01-01 08:17 drivers_autoprobe
--w------- root     root         4096 2012-01-01 08:17 drivers_probe
--w------- root     root         4096 2012-01-01 08:17 uevent

而在devices下就是该i2c-bus下所有的设备,drivers下就是i2c-bus下所有去的驱动。

  • subsys_register(注册一个子系统)
static int subsys_register(struct bus_type *subsys,const struct attribute_group **groups,struct kobject *parent_of_root)
{struct device *dev;int err;err = bus_register(subsys);       //注册一个总线if (err < 0)return err;dev = kzalloc(sizeof(struct device), GFP_KERNEL);        //分配一个device结构,然后设置名字if (!dev) {err = -ENOMEM;goto err_dev;}err = dev_set_name(dev, "%s", subsys->name);if (err < 0)goto err_name;dev->kobj.parent = parent_of_root;                //设置该device的parent,通常都是在buses_init中的system_ksetdev->groups = groups;                             //表现在sys中就是在/sys/devices/system下新建一个subsys->name的目录dev->release = system_root_device_release;err = device_register(dev);  //注册该设备if (err < 0) goto err_dev_reg;subsys->dev_root = dev;return 0;
}
  • subsys_system_register(/sys/devices/system/下注册一个子系统)
int subsys_system_register(struct bus_type *subsys,const struct attribute_group **groups)
{return subsys_register(subsys, groups, &system_kset->kobj);
}

该函数将会调用上面的subsys_register函数。举例:

static int __init clockevents_init_sysfs(void)
{int err = subsys_system_register(&clockevents_subsys, NULL);if (!err)err = tick_init_sysfs();return err;
}
struct bus_type clockevents_subsys = {.name       = "clockevents",.dev_name       = "clockevent",
};

这样的话,就会在/sys/devices/system下创建一个名字为"clockevent"设备,在/sys/bus/下创建一个名字为“clockevents”的总线

root@test:/sys/bus/clockevents # ls
devices
drivers
drivers_autoprobe
drivers_probe
uevent
root@test:/sys/devices/system/clockevents # ls -l
drwxr-xr-x root     root              2012-01-01 08:00 broadcast
drwxr-xr-x root     root              2012-01-01 08:00 clockevent0
drwxr-xr-x root     root              2012-01-01 08:00 clockevent1
drwxr-xr-x root     root              2012-01-01 08:00 clockevent2
drwxr-xr-x root     root              2012-01-01 08:00 clockevent3
drwxr-xr-x root     root              2012-01-01 08:00 clockevent4
drwxr-xr-x root     root              2012-01-01 08:00 clockevent5
drwxr-xr-x root     root              2012-01-01 08:00 clockevent6
drwxr-xr-x root     root              2012-01-01 08:00 clockevent7
drwxr-xr-x root     root              2012-01-01 08:00 power
-rw-r--r-- root     root         4096 2012-01-01 08:00 uevent

不过根据该函数的注释“Do not use this interface for anything new, it exists for compatibility with bad ideas only.”  意思是不在建议使用该函数了。

  • subsys_virtual_register(在/sys/devices/virtual/下创建一个子系统)
int subsys_virtual_register(struct bus_type *subsys,const struct attribute_group **groups)
{struct kobject *virtual_dir;virtual_dir = virtual_device_parent(NULL);if (!virtual_dir)return -ENOMEM;return subsys_register(subsys, groups, virtual_dir);
}

中间会调用virtual_device_parent函数

struct kobject *virtual_device_parent(struct device *dev)
{static struct kobject *virtual_dir = NULL;if (!virtual_dir)virtual_dir = kobject_create_and_add("virtual",&devices_kset->kobj);return virtual_dir;
}

也就是在/sys/devices/下创建一个名字为“virtual”的目录。

Linux设备驱动模型-Bus相关推荐

  1. Linux设备驱动模型1——简介和底层架构

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...

  2. linux设备驱动模型及其他,Linux设备驱动模型

    Linux设备驱动模型,主要函数分析 整个驱动模型中,最核心的三个函数分别是 __bus_register.driver_register.device_register int __bus_regi ...

  3. linux平台设备驱动模型是什么意思,Linux设备驱动模型之我理解

    点击(此处)折叠或打开 /* my_bus.c   */ #include #include #include #include #include #include "my_bus.h&qu ...

  4. Linux设备驱动模型之platform(平台)总线详解

    /********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...

  5. Linux中kobject的作用,Linux设备驱动模型-- 数据结构Kset/KObject

    前言 Kset和kobject是Linux设备驱动模型中的核心数据结构,其主要作用是将系统中的设备抽象出来,以树状结构组织,方便系统统一管理. 而这个统一管理的地方,就是sysfs,先放一张示例图,阐 ...

  6. Linux设备驱动模型三 kset

    Linux设备驱动模型三 kset 1 kset数据结构 kset的定义在前文已有描述,我们再回顾一下: [cpp] view plain copy struct kset { /*与子kobject ...

  7. Linux设备驱动模型一 sysfs

    Linux设备驱动模型一 sysfs 1 Linux设备模型 Linux 2.5的内核引入了一种新的设备模型,目的是对计算机上的所有设备进行统一的管理. 它包含以下基础结构: 类型 说明 设备Devi ...

  8. 五.linux设备驱动模型

    站在设备驱动这个角度分析,设备驱动模型是如何构建出来,起到什么作用,认识它并在写驱动的时候去利用设备驱动模型 目录 一.linux 设备驱动模型简介 1.1. 什么是设备驱动模型 1.2. 为什么需要 ...

  9. 整理--linux设备驱动模型

    知识整理–linux设备驱动模型 以kobject为底层,组织类class.总线bus.设备device.驱动driver等高级数据结构,同时实现对象引用计数.维护对象链表.对象上锁.对用户空间的表示 ...

  10. linux 内核驱动模型,linux设备驱动模型架构分析 一

    linux设备驱动模型架构分析 一 发布时间:2018-07-04 15:14, 浏览次数:584 , 标签: linux 概述 LDD3中说:"Linux内核需要一个对系统结构的一般性描述 ...

最新文章

  1. [Matlab]二维统计分析图实例
  2. 向深度学习三剑客学习四种科研精神(上)
  3. 结合项目实例 回顾传统设计模式(二)观察者模式
  4. SupeSite后台添加新闻增加【预览】功能
  5. [转]VMware虚拟机上网络连接(network type)的三种模式--bridged、host-only、NAT
  6. 关于extjs中动态添加TabPanel的tab项并以iframe显示的整理(转)
  7. 智能机浏览器版本信息获取
  8. .NET 6 新特性 System.Text.Json 中的 Writeable DOM
  9. 产品经理是否应该给 UI 设计师的设计稿提意见?
  10. thinkphp5.0架构总览
  11. Facebook 洗白?欲打造以隐私为中心的社交平台!
  12. 力扣-811 子域名访问计数
  13. WAP网站源代码--WAP新闻(文章)系统调试实战
  14. 概率统计D 07.03 正态总体均值与方差的置信区间
  15. 《庄子·胠箧》:“彼窃钩者诛,窃国者为诸侯;诸侯之门而仁义存焉。”
  16. mysql 计算农历_SQL农历转换函数
  17. go get xxx timeout
  18. linux安装程序企鹅,Linux-小企鹅输入法的安装与使用
  19. document的用法
  20. 【学习分享】0、创龙 TMS320C6748开发例程使用手册

热门文章

  1. iOS支付宝支付总结
  2. ICSharpCode.SharpZipLib实现压缩解压缩
  3. Android性能优化方法(五)
  4. MFC-CFileException类学习笔记
  5. KVO.非常简单的键值监听模式
  6. python数据包pandas_python | 数据分析(二)- Pandas数据包
  7. python按键按下改变数值_「正点原子NANO STM32开发板资料连载」第十六章电容触摸按键实验...
  8. jquery中attr() 和 prop()的区别
  9. 将博客同步至CSDN
  10. 文本处理三剑客,正则表达式等