`
cloudtech
  • 浏览: 4594855 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

LINUX设备驱动之设备模型三--device&driver&bus(一)

 
阅读更多

在清楚了kobject之后,就可以继续分析devicedriverbus了,这三者是设备驱动程序的基本数据结构。

我们可以这样理解,内核用device来表示各种设备,然后用driver来表示它的驱动,而设备有很多种,也属于相同类型或不同类型,而其对应的驱动可能同时也是另外一个设备的驱动,为了管理这些设备和驱动,就引入了总线bus_type,总线上有两个集合(也可以理解为两条链,如上图中的bus),分别用来存放该总线类型的设备和驱动,当添加一个设备时就将设备添加到总线的设备集合(图中操作2),同时可能会到驱动集合去匹配适合它的驱动(图中操作3,在此之前devicedriver没有挂钩),如何找到了就会将它们联系起来(图中操作6)。同样注册一个驱动,就会把它挂到相应总线类型的驱动集合里(图中操作1),同时去设备集合中找出它锁驱动的设备(图中操作4,在此之前devicedriver没有挂钩),如果找到就把设备链接到它支持的设备链表上(图中操作6)。

下面我们进入到代码中去:

struct bus_type结构定义如下:

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_type把部分私有字段封装到bus_type_private类型结构里:

struct bus_type_private {

struct kset subsys;

struct kset *drivers_kset;

struct kset *devices_kset;

struct klist klist_devices;

struct klist klist_drivers;

struct blocking_notifier_head bus_notifier;

unsigned int drivers_autoprobe:1;

struct bus_type *bus;

};

字段klist_devicesklist_drivers分别表示挂在bus_type上的驱动和设备链表,bus_type的其他字段和函数指针将在分析过程中说明。

首先我们要为设备和驱动注册一个总线类型:

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;

这里我们看到了subsys用来表示它的文件系统,可以回想上一节kset的注册。

这个bus_kset是系统启动是创建的,系统init进程kernel_init()中调用do_basic_setup(),其中调用driver_init(),其中调用的buses_init(),如下

int __init buses_init(void)

{

bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);

if (!bus_kset)

return -ENOMEM;

return 0;

}

从而知道创建的文件系统目录在/sys/bus下。

static struct kset_uevent_ops bus_uevent_ops = {

.filter = bus_uevent_filter,

};

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;

}

继续bus_register()中的代码:

retval = bus_create_file(bus, &bus_attr_uevent);

if (retval)

goto bus_uevent_fail;

bus_create_file()如下:

int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)

{

int error;

if (bus_get(bus)) {

error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);

bus_put(bus);

} else

error = -EINVAL;

return error;

}

bus_attr_uevent创建了bus->p->subsys.kobj的属性文件,由上面的赋值知道其读写操作在bus_ktypesysfs_ops,其定义如下:

static struct kobj_type bus_ktype = {

.sysfs_ops= &bus_sysfs_ops,

};

static struct sysfs_ops bus_sysfs_ops = {

.show= bus_attr_show,

.store= bus_attr_store,

};

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,

char *buf)

{

struct bus_attribute *bus_attr = to_bus_attr(attr);

struct bus_type_private *bus_priv = to_bus(kobj);

ssize_t ret = 0;

if (bus_attr->show)

ret = bus_attr->show(bus_priv->bus, buf);

return ret;

}

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,

const char *buf, size_t count)

{

struct bus_attribute *bus_attr = to_bus_attr(attr);

struct bus_type_private *bus_priv = to_bus(kobj);

ssize_t ret = 0;

if (bus_attr->store)

ret = bus_attr->store(bus_priv->bus, buf, count);

return ret;

}

由上面的程序可以看出文件的读写操作最终会回到struct bus_attribute &bus_attr_ueventshowstore方法。

bus_attr_uevent是通过宏定义的:

#define __ATTR(_name,_mode,_show,_store) { \

.attr = {.name = __stringify(_name), .mode = _mode },\

.show= _show,\

.store= _store,\

}

#define BUS_ATTR(_name, _mode, _show, _store)\

struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)

static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);

show方法为NULL,说明不可读。

static ssize_t bus_uevent_store(struct bus_type *bus,

const char *buf, size_t count)

{

enum kobject_action action;

if (kobject_action_type(buf, count, &action) == 0)

kobject_uevent(&bus->p->subsys.kobj, action);

return count;

}

在用户空间可以控制事件的发生,echo add > event将产生一个add的事件。

接着继续bus_register()中的代码:

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;

}

创建两个kset,其内嵌objectparent都指向priv->subsys.kobj,说明其文件系统在bus所在目录下。由上回分析中知道kset_create_and_add()时其内嵌kobjktype指向kset_ktype,而这里没有输入参数的uevent_opsNULL,则会以priv->subsys.kobj->kset->uevent_ops来产生事件,我们上面分析中知道这个uevent_opsbus_uevent_ops,其filter会比较kobjktype是不是&bus_ktype,而这里是&kset_ktype,所以这里是忽略了事件。

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;

add_probe_files()函数如下:

static int add_probe_files(struct bus_type *bus)

{

int retval;

retval = bus_create_file(bus, &bus_attr_drivers_probe);

if (retval)

goto out;

retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);

if (retval)

bus_remove_file(bus, &bus_attr_drivers_probe);

out:

return retval;

}

同上面创建bus_attr_uevent属性一样创建bus_attr_drivers_probebus_attr_drivers_autoprobe属性文件。粘出属性的代码:

static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);

static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,

show_drivers_autoprobe, store_drivers_autoprobe);

bus_attr_drivers_autoprobeshow指向NULL,说明其改文件不可写。

static ssize_t store_drivers_probe(struct bus_type *bus,

const char *buf, size_t count)

{

struct device *dev;

dev = bus_find_device_by_name(bus, NULL, buf);

if (!dev)

return -ENODEV;

if (bus_rescan_devices_helper(dev, NULL) != 0)

return -EINVAL;

return count;

}

将用户输(在用户空间)和的设备名称对应的设备与驱动匹配一次。

static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)

{

return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);

}

在用户空间可以打印drivers_autoprobe的值,cat drivers_autoprobe

static ssize_t store_drivers_autoprobe(struct bus_type *bus,

const char *buf, size_t count)

{

if (buf[0] == '0')

bus->p->drivers_autoprobe = 0;

else

bus->p->drivers_autoprobe = 1;

return count;

}

在用户空间可以改变drivers_autoprobe的值,echo 1 > drivers_autoprobe

继续分析bus_register()中的代码:

retval = bus_add_attrs(bus);

if (retval)

goto bus_attrs_fail;

bus_add_attrs()如下:

static int bus_add_attrs(struct bus_type *bus)

{

int error = 0;

int i;

if (bus->bus_attrs) {

for (i = 0; attr_name(bus->bus_attrs[i]); i++) {

error = bus_create_file(bus, &bus->bus_attrs[i]);

if (error)

goto err;

}

}

done:

return error;

err:

while (--i >= 0)

bus_remove_file(bus, &bus->bus_attrs[i]);

goto done;

}

如果bus->bus_attrs存在,则同样为其创建属性文件。

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;

}

bus_register()分析完了,总结一下,它注册了一个总线类型,创建对应的文件系统(包括目录和属性),初始化总线上的驱动和设备,这样我们就可以通过内核提供的函数往总线上注册设备和驱动了。

分享到:
评论

相关推荐

    linux设备模型之bus,device,driver

    linux设备模型之bus,device,driver

    linux驱动 bus-device-driver模型 bus.7z

    linux驱动 bus-device-driver模型

    linux设备驱动模型--设备篇

    介绍linux设备驱动模型概念中的设备篇

    linux设备驱动模型

    linux设备驱动模型 1、kobject原理与实例分析 2、kset原理与实例分析 3、bus(总线)原理与实例分析 4、device(设备)原理与实例分析 5、driver(驱动)原理与实例分析

    Linux驱动程序开发-设备驱动模型

    Linux驱动程序开发-设备驱动模型 Linux2.6设备驱动模型的基本元素是Class、Bus、Device、Driver,下面我们分别介绍各个部分。

    linux设备模型.pdf

    接着, 第8, 9, 10, 11章描述了设备模型中最重要的4个概念: Bus, Class, Device, Driver. 第12章介绍了platform系统, 它是基于Bus, Class, Device, Driver抽象出来的一个更上层的东西, 理解了前面的内容之后, 再来看...

    Linux 设备模型

    因此,由于这个共性,内核在设备模型的基础上(device和device_driver),对这些设备进行了更进一步的封装,抽象出paltform bus、platform device和platform driver,以便驱动开发人员可以方便的开发这类设备的驱动...

    驱动模型代码

    linux3.16 bus device driver 驱动模型

    unix分析关于UNIX的一些浅析

    下图即为Linux 2.6中引入的设备驱动模型的结构图(只是个总体框架,并不是指这的platform总线,设备和驱动)。 总线上包括设备和驱动的集合,总线上所有设备组成双向循环链表,包含在platform_device...

    linux 2.6.36+ok6410 SPI子系统接口讨论

    linux下的设备模型包括几个主要的概念sysfs (dev是用户空间接口,根据sysfs下的class目录由mdev负责建立)bus总线,linux下的设备都是建立在总线上的,platform总线是一个虚拟的总线,所有的的片上设备基本上都接在...

    总线,设备,驱动关联

    Linux设备模型中三个很重要的概念就是总线,设备,驱动.即bus,device,driver,们只需要知道,drivers 和 devices 的存在,让struct bus_type与两个链表联系了起来,一个是 devices 的链表,一个是drivers 的链表,也就是说,...

Global site tag (gtag.js) - Google Analytics