Linux 驱动开发 | 驱动世界里的宏伟建筑

哈喽,我是老吴。

是否每一个上进的人都会觉得自己还可以再努力一点?

事情到了最后,只要没达成目的,总能把失败的原因归为 "没有再努力一点"。

但是,对努力的最大错误认知就是:时间越长,过程越痛苦,代表我越努力。

想一想,是否有更合理的努力方式?

以下是正文:

一、什么是 device model?
二、device model 的 3 个核心概念
三、bus、device、driver 是如何关联的?
四、bus、device、driver 最简单示例
五、小结
六、相关参考

一、什么是 device model?

Linux 的 device model 是一个旨在统一管理所有设备驱动的模型。

它犹如一栋规模宏大的建筑:

以 kobject、kset、attribute 等作为基本的建筑材料,

构造出支撑驱动世界的 bus、device、driver 三大组件,

最后通过 sysfs 在各种基础的建筑材料之间建立彼此的互联层次关系,并向外界提供了与建筑内设施进行互动的文件接口。

点击查看大图

device model 有什么作用?

可以将 device 的硬件描述 和 driver 进行分离,提升 driver 的代码复用率;

可以对 device 进行分类;

可以遍历 device 和 driver;

可以更好地呈现设备的拓扑关系;

可以通过 sysfs 访问设备;

可以让设备支持热插拔;

...

为了控制篇幅,本文将重点放在与驱动工程师关系最紧密的 bus、device、driver 3 个 组件

二、device model 的 3 个核心概念

device model 里有 3 个核心的概念:

  • bus

  • device

  • driver

什么是 bus?

bus 代表一种总线,例如 I2C、SPI、USB 等。

bus 是 Linux 设备驱动模型这种建筑的核心框架,系统中的设备和驱动都依附在其周围。

启动系统后,可以通过 /sys/bus 可以查看系统里当前有哪些总线。

bus 由 struct bus_type 来描述:

struct bus_type {
 const char *name;
 const char *dev_name;
 struct device *dev_root;
 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);

 ...
 struct subsys_private *p;
 struct lock_class_key lock_key;
};

不需要一下子了解各个成员的作用,用到的时候再说明。

重点关注成员:

  • int (*match)(struct device *dev, struct device_driver *drv),用于判断挂在该 bus 上的设备和驱动是否匹配的回调函数;

  • int (*probe)(struct device *dev),如果 bus 具有探测设备的能力,则会提供该回调函数;

  • struct subsys_private *p,用于管理 bus 上的设备和驱动的数据结构;

注册 bus 的 api:

int bus_register(struct bus_type *bus);

什么是 device ?

device 代表了某个设备。

由 struct device 来描述:

struct device {
 struct device *parent;
 struct device_private *p;
 struct kobject kobj;
 const char *init_name;
 const struct device_type *type;
 struct mutex mutex;
 struct bus_type *bus;
 struct device_driver *driver;
 void *platform_data;
 void *driver_data;
    ...
}

重点关注成员:

  • struct kobject kobj,内核对象;

  • struct bus_type *bus,设备所在的总线;

  • struct device_driver *driver,和设备绑定在一起的驱动,如果还没绑定,则为 NULL;

注册 device 的 api:

int device_register(struct device *dev)

什么是 driver?

driver 代表了设备驱动。

由 struct device_driver 来描述:

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;
};

重点关注成员:

  • struct bus_type *bus;
  • int (*probe) (struct device *dev);

值得一提的是,总线控制器也是一种设备。

例如 I2C 总线控制器这个设备,对应的驱动为 I2C controller driver。

而挂在 I2C 总线上的设备,对应的驱动为 I2C device driver。

注册 driver 的 api:

int driver_register(struct device_driver *drv);

三、bus、device、driver 是如何关联的?

device model 最核心的工作就是维护这三类抽象的实例,以及建立它们之间的关联关系。

bus 如何管理 device 和 driver ?

在 struct bus_type 中有一个 struct subsys_private *p 指针,它负责管理挂在 bus 上的所有设备和驱动,其定义如下:

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;
};
点击查看大图

两个 klist 成员以链表的形式将该总线上所有的驱动与设备链接到一起。

struct kset *drivers_kset 和 struct kset *devices_kset 是在向系统注册当前新总线时动态生成的容纳该总线上所有驱动与设备的 kset。

在内核里,用 kobject 来表示一个对象,kset 则是 kobject set 的缩写,即内核对象集合。

内核用 kobject 和 kset 等数据结构作为原材料,以实现面向对象的方式构建了 device model 的框架。

最后,device 和 device_driver 的 bus 成员也会指向总线:

device 和 driver 的绑定

无论是通过 device_register() 注册一个 device 到 bus 上,

还是通过 driver_register() 注册一个 device_driver 到 bus 上,

都会导致 bus 尝试执行 device 和 driver 的绑定行为。

1. device_register() 触发的绑定

注册 device 时:

int device_register(struct device *dev);
 device_add(dev);
  bus_probe_device(dev);
   __device_attach(dev, true);

__device_attach(dev, true) 会为 device 遍历 bus 上的所有 driver:

bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver);
 driver_match_device(drv, dev);
  drv->bus->match ? drv->bus->match(dev, drv) : 1;
 driver_probe_device(drv, dev);

driver_match_device() 通过 bus 里的 match 函数来判断是否 device 和 driver 是否匹配,

是否 match 的判断标准一般是通过 of_match_table 或者是 id_table 作为衡量的标准,

以 i2c bus 的 match 函数为例

static int i2c_device_match(struct device *dev, struct device_driver *drv){
 struct i2c_client *client = i2c_verify_client(dev);
 struct i2c_driver *driver;


 /* Attempt an OF style match */
 if (i2c_of_match_device(drv->of_match_table, client))
  return 1;

 /* Then ACPI style match */
 if (acpi_driver_match_device(dev, drv))
  return 1;

 driver = to_i2c_driver(drv);

 /* Finally an I2C match */
 if (i2c_match_id(driver->id_table, client))
  return 1;

 return 0;
}

一旦 match 成功,就会调用 driver_probe_device() 以触发探测设备的行为:

int driver_probe_device(struct device_driver *drv, struct device *dev);
 really_probe(dev, drv);
  if (dev->bus->probe) {
   ret = dev->bus->probe(dev);
  } else if (drv->probe) {
   ret = drv->probe(dev);
  }

如果 bus 具有探测设备的能力的话,例如 pci bus, 则会使用 bus->probe() 探测设备,

否则,使用 driver->probe() 探测设备,driver 的 probe 操作跟具体的硬件设备挂钩。

2. 由 driver_register() 触发的绑定

int driver_register(struct device_driver *drv);
 bus_add_driver(drv);
  driver_attach(drv);

driver_attach(drv) 会为 driver 遍历 bus 上的所有 device:

int driver_attach(struct device_driver *drv);
 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
  __driver_attach();
   driver_match_device(drv, dev);
   driver_probe_device(drv, dev);

和 device_register() 一样,最终都会调用 driver_match_device(drv, dev),

进而通过 bus 里的 match 函数来判断是否 device 和 driver 是否匹配。

同样地,一旦 match 成功,就会调用 driver_probe_device() 以触发探测设备的行为,后续的操作和注册设备时是一模一样的。

3. device 和 drvier 的绑定关系

前面说了绑定是如何被触发的,现在来明确一下绑定的具体操作。

对于能成功匹配的 device 和 driver,两者之间的关系是 N 对 1,即可以有多个 device 和 1 个 driver 绑定在一起。

点击查看大图

对于 device:

其 driver 成员指向已绑定的 device_driver。

int driver_probe_device(struct device_driver *drv, struct device *dev)
 really_probe(dev, drv);
  dev->driver = drv;

对于 driver:

在 device_driver 里链表 klist_devices 保存了该 driver 上已绑定的所有 device。

int driver_probe_device(struct device_driver *drv, struct device *dev)
 really_probe(dev, drv);
  driver_bound(dev);
   klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

在 /driver/base/driver.c 中,提供了一些 api,用于遍历处理 driver 上绑定的所有 device:

  • int driver_for_each_device()
  • struct device *driver_find_device()

四、bus、device、driver 最简单示例

下面的例子,

构造了一个名为 "simple_bus" 的 bus 实例。

simple_bus.c:注册了一条名为 "sb" 的 bus,并且提供了注册 device 和 driver 的 api。

static int sb_match(struct device *dev, struct device_driver *driver){
 return !strncmp(dev_name(dev), driver->name, strlen(driver->name));
}

struct bus_type sb_bus_type = {
 .name = "sb",
 .match = sb_match,
};

static ssize_t version_show(struct bus_type *bus, char *buf){
 return snprintf(buf, PAGE_SIZE, "%s\n", Version);
}

static BUS_ATTR_RO(version);

static void sb_dev_release(struct device *dev){ }

int register_sb_device(struct sb_device *sbdev){
    sbdev->dev.bus = &sb_bus_type;
 sbdev->dev.release = sb_dev_release;
    dev_set_name(&sbdev->dev, sbdev->name);
    return device_register(&sbdev->dev);
}
EXPORT_SYMBOL(register_sb_device);

void unregister_sb_device(struct sb_device *sbdev){
 device_unregister(&sbdev->dev);
}
EXPORT_SYMBOL(unregister_sb_device);

static int sb_drv_probe(struct device *dev){
 printk(KERN_INFO"sb_drv probe %s\n", dev_name(dev));
 return 0;
}

int register_sb_driver(struct sb_driver *sdrv){
 sdrv->driver.bus = &sb_bus_type;
 sdrv->driver.probe = &sb_drv_probe;
 return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL(register_sb_driver);

void unregister_sb_driver(struct sb_driver *driver){
 driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(unregister_sb_driver);

static int __init sb_bus_init(void){
 int ret;

 ret = bus_register(&sb_bus_type);
 if (ret) {
  printk(KERN_ERR "Unable to register sb bus, failure was %d\n",ret);
  return ret;
 }
 if (bus_create_file(&sb_bus_type, &bus_attr_version))
  printk(KERN_ERR "Unable to create version attribute\n");
 return 0;
}

static void sb_bus_exit(void){
 bus_unregister(&sb_bus_type);
}

module_init(sb_bus_init);
module_exit(sb_bus_exit);

xxx_chip.c:注册4个名为 "chipX" 的 device

struct xxx_chip {
 char devname[20];
 struct sb_device sdev;
};

int chipdev_num = 4;
struct xxx_chip *chipdev;

static void chip_register_dev(struct xxx_chip *dev, int index){
 snprintf(dev->devname, sizeof(dev->devname), "chip%d", index);
 dev->sdev.name = dev->devname;
 dev_set_drvdata(&dev->sdev.dev, dev);
 register_sb_device(&dev->sdev);
}

int chip_init(void){
    int i;

    chipdev = kmalloc(chipdev_num*sizeof (struct xxx_chip), GFP_KERNEL);

    memset(chipdev, 0, chipdev_num*sizeof (struct xxx_chip));
    for (i = 0; i < chipdev_num; i++) {
  chip_register_dev(chipdev + i, i);
 }

    return 0;
}

void chip_cleanup(void){
    int i;
    for (i = 0; i < chipdev_num; i++) {
  unregister_sb_device(&chipdev[i].sdev);
 }
    kfree(chipdev);
}

module_init(chip_init);
module_exit(chip_cleanup);

xxx_chip_drv.c:注册1个名为 "chip" 的 driver

static struct sb_driver sculld_driver = {
 .driver = {
  .name = "chip",
 },
};

int xxx_chipdrv_init(void){
    return register_sb_driver(&sculld_driver);
}

void xxx_chipdrv_cleanup(void){
    unregister_sb_driver(&sculld_driver);
}

module_init(xxx_chipdrv_init);
module_exit(xxx_chipdrv_cleanup);

运行效果:

root@buildroot:~# insmod simple_bus.ko 
root@buildroot:~# tree /sys/bus/sb
/sys/bus/sb
├── devices
├── drivers
├── drivers_autoprobe
├── drivers_probe
├── uevent
└── version

root@buildroot:~# insmod xxx_chip.ko 
root@buildroot:~# tree /sys/bus/sb
/sys/bus/sb
├── devices
│   ├── chip0 -> ../../../devices/chip0
│   ├── chip1 -> ../../../devices/chip1
│   ├── chip2 -> ../../../devices/chip2
│   └── chip3 -> ../../../devices/chip3
├── drivers
├── drivers_autoprobe
├── drivers_probe
├── uevent
└── version

root@buildroot:~# insmod xxx_chip_drv.ko
sb_drv probe chip0
sb_drv probe chip1
sb_drv probe chip2
sb_drv probe chip3

root@buildroot:~# tree /sys/bus/sb
/sys/bus/sb
├── devices
│   ├── chip0 -> ../../../devices/chip0
│   ├── chip1 -> ../../../devices/chip1
│   ├── chip2 -> ../../../devices/chip2
│   └── chip3 -> ../../../devices/chip3
├── drivers
│   └── chip
│       ├── bind
│       ├── chip0 -> ../../../../devices/chip0
│       ├── chip1 -> ../../../../devices/chip1
│       ├── chip2 -> ../../../../devices/chip2
│       ├── chip3 -> ../../../../devices/chip3
│       ├── uevent
│       └── unbind
├── drivers_autoprobe
├── drivers_probe
├── uevent
└── version

通过打印信息可知,device 和 driver 经由 bus 判断是否 match 之后,执行了 driver 的 probe() 函数,符合我们前面的分析。

五、小结

Linux 的 device model 是个非常复杂的系统。

从一个比较高的层次来看,主要由总线、设备和驱动构成。

内核为了实现这些组件间的相关关系,定义了 kobject 和 kset 这样的基础底层数据结构,然后通过 sysfs 文件系统向用户空间展示发生在内核空间中的各组件间的互联层次关系,并以文件系统接口的方式为用户空间程序提供了访问内核对象属性信息的简易方法。

为了控制篇幅,本文并没有涉及到 kojbect 和 sysfs。

如果你感兴趣的话,去挖掘一下以下内容:

  • device model 的底层数据结构 kojbect、kset 是如何工作的?

  • 内核是如何使用 device model 去构建 i2c、spi、usb 等驱动框架?

  • device model 和 sysfs 是如何协同工作的?

  • sysfs 里如何创建属性文件以访问设备驱动?

  • sysfs 里的 class 有什么作用?

六、相关参考

《Linux 设备驱动》

  • 第 14 章 Linux 设备模型

《深入 Linux 设备驱动程序内核机制》

  • 第 9 章 Linux 设备驱动模型

《Linux设备驱动开发详解》

  • 第 5 章 Linux文件系统与设备文件
  • 第 12 章 Linux设备驱动的软件架构思想

Linux/Documentation/driver-model

  • bus.txt
  • class.txt
  • device.txt
  • driver.txt
  • overview.txt

推荐阅读:

专辑 | Linux 驱动开发

专辑 | 每天一点 C

专辑 | Linux 系统编程

本文使用 文章同步助手 同步

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,817评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,329评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,354评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,498评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,600评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,829评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,979评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,722评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,189评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,519评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,654评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,329评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,940评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,762评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,993评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,382评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,543评论 2 349

推荐阅读更多精彩内容