Linux 驱动模型初探1——BUS

##写在前面的话##

这几篇文章是2011年,当时的老大对我提出的一个“作业”。当时研究了一把,完成了第一篇BUS,老大看过之后,表示满意,要我把后面继续完成。然,世事变迁,老大离开了公司,去了其它公司。之后,我也从S公司离开了。所做的工作也有小范围的调整。近期又回到驱动这块,再看到之前的笔记,感慨万千,我决计是要完成搁浅了近3年“作业”。

测试代码我已经提交到github上:https://github.com/koffuxu/kornel/tree/dev/driver-mode

---------------历史笔记----------------

兼顾工作和自身学习的需要,想尽可能多的弄懂 linux内核驱动的“四大天王”—— BUS、 DEVICE、 DEVICE-DRIVER、 CLASS。通过几天跟踪代码、参考牛人的一些 BLOG和自己 DEMO一些例子之后,才发现确实有难度。正如某人说的“懂八成,理解五成,融会贯通三成,能用出来就只剩一成了,而就这一成要往死里用才行”,何况,我更没有弄懂八成,要写成文档更让我迷茫了。没办法,反反复复地看,反反复复地试,反反复复地想。但效果还是不明显,但
Deadline又近了,只能窃取牛人的成果,加入自己的一些理解,写写这几天多所看的,所想的。权宜当个抛砖引玉的作用,如果有什么错误和纰漏,欢迎大家指正。

I、目标:

到底要达到怎样还真不好说,其实我自己现在也很混沌,那就一起开始学习吧,至少,完成这个系列之后,应该要知道“ Driver和 Device是怎样匹配的?”、“ Bus是不是 Device?”、“一个 Driver只匹配一个 Device还是多个?”、“在系统中,是先加 Device还是先加 Driver还是没要求?”等等。

II、规划:

驱动模型应该讲Kobject/Kset。Kobject/Kset相当于是LinuxKernel对所有设备驱动的抽象类,是基类。Linux Kernle的设备驱动大概有Bus、 Device、 Device-driver,这个东东是紧贴Kobject/Kset这一层,那么就基于这个实现自己的bus/device/device_driver,来理解Kobject/Kset。

1 什么是BUS

BUS是主机和外设的连接通道,也可以说是“红娘”,因为它提供了 Device和 Driver找对象匹配的场所。有些总线是比较规范的,形成了很多协议。如 PCI,USB,IIC等。我们也在内核添加自己定义的总线。任何设备都可以选择合适的总线连接到主机。

如下图,查看 /sys/目录下,有一个 Bus的文件夹,没错,我们就是要这个 Bus。

再进去Bus看看,

看到没有,有我们非常熟悉的 i2c总线。其实其它也是总线,分别用于接通不同的外设。也包括“ platform”这条虚拟的平台总线。

2 我从哪里来

跟踪代码,我们先找出 bus的定义

struct bus_type {

const char             *name;

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 bus_type_private *p;

};

还就是Bus一些重要的方法:

int bus_register(struct bus_type *bus)

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 *))

struct bus_type也有几个很重要的函数指针,比如

int (*match)(struct device *dev, struct device_driver *drv);

int (*probe)(struct device *dev);

注册一条总线就是实现这个 bus_type的结构体,好,查找一下 i2c总线是怎样实现的。

跟踪代码,果然找到了这个:

.name = "i2c"就说明在/sys/bus/ 下有一个i2c的目录,同理,反过来,我要找 /sys/bus/hid对应的 bus_type,那么一定有个 struct bus_type hid_bus_type,

找找看,

果然,其它的也一样。

再深入一点,接下来分析一下 int bus_register(struct bus_type *bus)这个函数,为什么通过使用它能在 /sys/bus/目录下建一个相关的文件夹。

先给出原型如下

int bus_register(struct bus_type *bus)

{

int retval;

struct bus_type_private *priv;

priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);

if (!priv)

return -ENOMEM;

priv->bus = bus;

bus->p = priv;

BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

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

if (retval)

goto out;

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;

}

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

klist_init(&priv->klist_drivers, NULL, NULL);

retval = add_probe_files(bus);

if (retval)

goto bus_probe_files_fail;

retval = bus_add_attrs(bus);

if (retval)

goto bus_attrs_fail;

pr_debug("bus: ‘%s‘: registered\n", bus->name);

return 0;

bus_attrs_fail:

remove_probe_files(bus);

bus_probe_files_fail:

kset_unregister(bus->p->drivers_kset);

bus_drivers_fail:

kset_unregister(bus->p->devices_kset);

bus_devices_fail:

bus_remove_file(bus, &bus_attr_uevent);

bus_uevent_fail:

kset_unregister(&bus->p->subsys);

kfree(bus->p);

out:

bus->p = NULL;

return retval;

}

太长了,唉,看不下去,避重就轻地看一些我们关心的。简化之后就这样。

int bus_register(struct bus_type *bus)

{

retval = kset_register(&priv->subsys);

retval = bus_create_file(bus, &bus_attr_uevent);

priv->devices_kset = kset_create_and_add("devices", NULL,    &priv->subsys.kobj);

priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

klist_init(&priv->klist_drivers, NULL, NULL);

retval = add_probe_files(bus);

}

先看kset_register()这个方法,跟踪代码可以发现会调用 kobject_add_internal()这个函数,如下图

继续跟进代码,进去看看 kobject_add_internal()这个函数做了什么事情。其中里面有个这个东西

,不用进去看做了什么事,看一下函数名大概就知道是创建一个目录。好吧就这样,它会在 sysfs/里面创建相应的文件夹。具体怎么创建,怎么组织这些文件和文件夹那就是设备模型的精髓 Kobject、 Kset、 Ktype。里面有很多东西,回头认真研读一下光哥关于设备模型这个文档。

好,回到bus_register()这个函数,接下来

bus_create_file(bus, &bus_attr_uevent);

kset_create_and_add("devices", NULL,      &priv->subsys.kobj);

kset_create_and_add("drivers", NULL, &priv->subsys.kobj);

这三个函数是在你定义的 Bus主文件夹下创建一些 Kset和属性文件,这个不详细说,在下面的例子再展开分析,

一条Bus上会有很多设备,也会有很多驱动。暂且这样说吧, Bus就是《非诚勿扰》栏目,它为多个男( Driver)女( Device)提供约会配对的场所。多个“男” “女”是怎样放在一起的呢?没错,就是下面这两个函数 klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put) 和klist_init(&priv->klist_drivers,
NULL, NULL); 通过两个内核链表,把它们组织起来的。

还要说的就是那个 match()方法。也就是说《非诚勿扰》在怎样在孟非老师主持下进行配对的。具体怎样配对,可以看看 i2c BUS是怎样实现的。

继续深入,看看这个函数到是怎样进行 id match的。

没错,是通过比较 name这个字符串来匹配的。

写一个小例子来增强一下我们的理解,我们要在 /sys/bus/下创建一条 BUS,暂且命名为 kf-bus,它将是和i2c在同一个目录里的 BUS。

>>>kf_bus.c文件如下

#include <linux/device.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/module.h>

#include <linux/stat.h>

#include <linux/slab.h>

#include <linux/kobject.h>

static int kf_bus_match(struct device *dev, struct device_driver *drv)

{

return 0;

}

static struct bus_type kf_bus_type = {

.name = "kf-bus",

.match         = kf_bus_match,

};

static int __init kf_bus_init()

{

printk(">>>kf_bus_init successed!!\n");

return bus_register(&kf_bus_type);

}

static void __exit kf_bus_exit()

{

printk(">>>kf_bus_exit successed!!\n");

bus_unregister(&kf_bus_type);

}

module_init(kf_bus_init);

module_exit(kf_bus_exit);

MODULE_AUTHOR("Koffuxu");

MODULE_LICENSE("GPL");

>>>Makefile文件如下,

obj-m += kf_bus.o

KERNELDIR        = ../../../kernel_rk29sdk2

PWD := $(shell pwd)

all:

make -C $(KERNELDIR) M=$(PWD) modules

clean:

rm -rf *.o* *.ko* *.mod.c* .cmd *.symvers .tmp_version.*.cmd

放到合适的目录, make一下,就会在当面目录生成一个 kf_bus.ko, copy到 MID上,

1 检查下内核已有的模块

2 加载我们的模块

通过lsmod我们已经成功加载自己的 "kf_bus"。

接下来我们去 /sys/bus/下面去看看是否有猜想的 kf_bus文件夹。

果然,得瑟一下,“ so easy, 妈妈再也不用担心我的学习了”。好了,我们再进去看看里有什么东东。

怎么多出来这么多东西?接下来,我们就来一一找出来这五个文件(目录也是文件)是怎样产生的。

回头看看我们的代码,首先定义了一个 bus_type的结构体。名字就是 "kf-bus",按照我们之前的分析,会在 /sys/bus/下产生一个 kf-bus的文件夹,这个我们已经验证,接下来,我们调用了 bus_register()这个函数,玄机就是只在这个函数里面。其实这个函数我们之前也有初步分析了一下,我们简化为这样:

int bus_register(struct bus_type *bus)

{

retval = kset_register(&priv->subsys);

retval = bus_create_file(bus, &bus_attr_uevent);

priv->devices_kset = kset_create_and_add("devices", NULL,    &priv->subsys.kobj);

priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

klist_init(&priv->klist_drivers, NULL, NULL);

retval = add_probe_files(bus);

}

kset_register()上文已经讲过,接下来就是

这个函数,进去看看,这个函数调用了

,唉,没办法,基础差,只能望文生义,就是在 sys文件系统里建一个属性文件,不妨进去看

从注释也能看出来,是这样的。那么

就是在自己定义的bus下,那一个 bus_aat_uevent这个属性文件,没错就是对应我们要分析的五个文件的第一个文件 uevent,它是与 bus_attr通过一个连接符号组成了。

好第一个已经出来了,继续看代码: priv->devices_kset = kset_create_and_add("devices", NULL,        &priv->subsys.kobj);这个函数貌似是个/sys/bus/kf-bus/devices/这个文件夹相关,空说无凭,进去代码看看。

这个函数首先调用 kset_create()创建一个name的kset,再把这个 kset传给 kse_register(),注册进内核。是不是看到老朋友,这个函数我们上面分析过,它最后会调用create_dir()去创建一个目录。所以就有了我们devices目录。同理, drivers文件夹是由 priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj)这个函数产生的。接下来就是 drivers_probe和 drivers_autoprobe两个文件了,我们一定能找得出来的,果然, add_probe_files(bus);进去这个函数

就两个我们很熟悉的方法 bus_create_fle(),这里产生了最后的两个文件了。

时间: 2024-12-28 12:35:31

Linux 驱动模型初探1——BUS的相关文章

Linux 驱动模型初探3——device

讲device之前,我要引入一个比喻,这个比喻来自一个学长(z2007b).driver是帅哥,device是美女,bus是红娘,bus是提供device和driver配对的场所(方法?).好吧,暂时先这样定,现在要讲的就是美女. 1,老规则,先看看struce device这个美女有哪些特性(成员)和方法 struct device { struct device *parent; struct device_private*p; struct kobject kobj; const char

Linux 驱动模型初探4——汇总和拾遗

1,直接上code #include <linux/device.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/stat.h> #include <linux/slab.h> #include <linux/kobject.h> #include <linux/kdev_t.

Linux 驱动模型初探2——device_driver

1,思考 device_driver也是一个kobject?,但你注意没有,在sys/目录下有bus/,有device/就是没有device_driver/,Y?为什么这么设计? 先看struct devic_driver; struct device_driver { const char *name; struct bus_type *bus; ... int (*probe) (struct device *dev); int (*remove) (struct device *dev);

浅析Linux驱动模型中的底层数据结构kobject和kset

1.kobject Linux内核用kobject来表示一个内核对象.它和Sysfs文件系统联系密切,在内核中注册到系统中的每个kobject对象在sysfs文件系统中对对应着一个文件目录.kobject数据结构通常的用法是嵌入到其对他的数据结构中(即容器,比如cdev结构),用于实现内核对该类数据结构对象的管理.这些数据结构(容器)通过kobject连接起来,形成了一个树状结构. 它在源码中的定义为: /*<include/linux/kobject.h>*/ struct kobject

Linux驱动模型解析bus之platform bus

这是内核启动之后要调用的驱动模型的开始代码: drivers/base/init.c/** * driver_init - initialize driver model. * * Call the driver model init functions to initialize their * subsystems. Called early from init/main.c. */ void __init driver_init(void) { /* These are the core

linux内核驱动模型

linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构成内核对象集,用kset表示,内核对象集也包含自己的内核对象,从而组成层次化的结构.2.用sysfs文件系统导出到用户空间.内核中的所有内核对象组织成树状,以对象属性为叶子.通过sysfs文件系统,将用户空间对文件的读写操作转化为对内核对象属性的显示和保存方法.从而导出内核对象信息,并提供配置接口.

转 Linux设备模型 (1)

作者:wwang 出处:http://www.cnblogs.com/wwang 本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接. 随着计算机的周边外设越来越丰富,设备管理已经成为现代操作系统的一项重要任务,这对于Linux来说也是同样的情况.每次Linux内核新版本的发布,都会伴随着一批设备驱动进入内核.在Linux内核里,驱动程序的代码量占有了相当大的比重.下图是我在网络上搜索到的

Linux 设备模型基本概念

1.设备模型引入 Linux 2.6内核最初为了应付电源管理的需要,提出了一个设备模型来管理所有的设备.在物理上,外设之间是有一种层次关系的,比如把一个U盘插到笔记本上,实际上这个U盘是接在一个USB Hub上,USB Hub又是接在USB 2.0 Host Controller (EHCI)上,最终EHCI又是一个挂在PCI Bus上的设备.这里的一个层次关系是:PCI->EHCI->USB Hub->USB Disk.如果操作系统要进入休眠状态,首先要逐层通知所有的外设进入休眠模式,

linux总线设备驱动模型

本篇文章通过平台总线设备模型为例,讲解总线设备驱动模型: platform_device_register的作用: 1.把device放入bus的device链表 2.从bus的driver链表中取出每一个driver,用bus的match函数判断driver能否支持这个device 3.若可以支持,调用driver的probe函数 platform_driver_register的作用: 1.将driver放入bus的driver链表 2.从bus的device链表中取出每一个device,用