linux设备模型浅析之设备篇

发布时间:2024-11-12

linux系统设备模型

Linux设备模型浅析之设备篇

本文属本人原创,欢迎转载,转载请注明出处。由于个人的见识和能力有限,不可能面面俱到,也可能存在谬误,敬请网友指出,本人的邮箱是yzq.seen@http://,博客是http://Linux设备模型,仅仅看理论介绍,比如LDD3的第十四章,会感觉太抽象不易理解,而通过阅读内核代码就更具体更易理解,所以结合理论介绍和内核代码阅读能够更快速的理解掌握linux设备模型。这一序列的文章的目的就是在于此,看这些文章之前最好能够仔细阅读LDD3的第十四章。大部分device和driver都被包含在一个特定bus中,platform_device和platform_driver就是如此,包含在 platform_bus_type中。这里就以对platform_bus_type的调用为主线,浅析platform_device的注册过程,从而理解linux设备模型。platform_bus_type用于关联SOC的 platform device和 platform driver,比如在内核linux-2.6.29中所有S3C2410中的 platform device都保存在devs.c中。这里就以S3C2410 RTC为例。在文章的最后贴有一张针对本例的device model图片,可在阅读本文章的时候作为参照。

一、S3C2410 RTC的platform device定义在arch/arm/plat-s3c24xx/devs.c中,如下:static struct resource s3c_rtc_resource[] = {

[0] = {

.start = S3C24XX_PA_RTC,

.end = S3C24XX_PA_RTC + 0xff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_RTC,

.end = IRQ_RTC,

.flags = IORESOURCE_IRQ,

},

[2] = {

.start = IRQ_TICK,

.end = IRQ_TICK,

.flags = IORESOURCE_IRQ

}

};

struct platform_device s3c_device_rtc = {

.name = "s3c2410-rtc",

.id = -1,

.num_resources = ARRAY_SIZE(s3c_rtc_resource),

.resource = s3c_rtc_resource,

};

把它们添加在arch/arm/mach-s3c2440/ mach- smdk2440.c中,如下:

static struct platform_device *smdk2440_devices[] __initdata = {

&s3c_device_usb,

&s3c_device_lcd,

&s3c_device_wdt,

linux系统设备模型

&s3c_device_i2c0,

&s3c_device_iis,

& s3c_device_rtc

};

系统初始化的时候会调用drivers/base/platform.c里的platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices))将其注册到platform_bus_type,最终被添加到device hierarchy。platform_add_devices()调用了platform_device_register(),而后者又先后调用了

device_initialize()和platform_device_add()。有必要对platform_bus_type的定义作一番注释,其定义如下:

struct bus_type platform_bus_type = {

.name= "platform",// bus的名字,将会生成/sys/bus/platform目录

/* 该属性文件将产生在所有 platform_bus_type类型的设备目录下,文件名为"modalias” */.dev_attrs= platform_dev_attrs,

.match= platform_match,// 用于drive与device匹配的例程

.uevent= platform_uevent,// 用于输出环境变量,与属性文件“uevent”相关

.pm= PLATFORM_PM_OPS_PTR, // 电源管理方面

};

代码中,

1. 通过bus_register(&platform_bus_type)将platform_bus_type 注册到总线模块。本例中,当cat /sys/device/platform/s3c2410-rtc/modalias时将会打印出"platform:s3c2410-wdt",从

platform_dev_attrs的具体实现中你就能看出来。当cat /sys/device/platform/s3c2410-rtc/uevent时将会打印出”DRIVER=s3c2410-wdt MODALIAS=platform:s3c2410-wdt”,从 platform_uevent 的具体实现中你就能看出来。

二、下面解析device_initialize()和platform_device_add()两个例程,它们分别定义在drivers/base/core.c和drivers/base/platform.c中。

device_initialize()的代码如下:

void device_initialize(struct device *dev)

{

dev->kobj.kset = devices_kset;// 设置其指向的kset容器

kobject_init(&dev->kobj, &device_ktype);// 初始化 kobj,将 device_ktype传递给它

klist_init(&dev->klist_children, klist_children_get,

klist_children_put);// 初试化klist

INIT_LIST_HEAD(&dev->dma_pools);

init_MUTEX(&dev->sem);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

device_init_wakeup(dev, 0);

device_pm_init(dev);// 初试化电源管理

set_dev_node(dev, -1);

}

代码中,

1. devices_kset是所有dev的kset,也就是所有dev都被链接在该kset下,其在初试化例程devices_init()中通过调用kset_create_and_add("devices", &device_uevent_ops, NULL)来创建。由于参数parent=NULL ,所以生成/sys/devices 目录。这里说明下kobj,kset结构体中包含有一个

linux系统设备模型

kobj,一个kobj生成一个目录,在这里就是”devices "目录,通过调用kobject_add_internal()例程生成。所以从dev->kobj.kset = devices_kset 可以看出,该dev.kobj添加到了devices_kset容器中,所的kobj都归属于一个特定的kset。关于kset,kobj,ktype,kref的关系可以参考书LDD3的第十四章,在第370页有一张说明kobj和kset关系的图(英文版)。

2. kobject_init(&dev->kobj, &device_ktype)用于初始化 dev->kobj中变量的参数,如ktype、kref、entry和state*等。初试化例程devices_init()还会调用kobject_create_and_add()例程生成/sys/dev、/sys/dev/block和/sys/dev/char目录。

3. 其他初始化。

platform_device_add代码如下:

int platform_device_add(struct platform_device *pdev)

{

int i, ret = 0;

if (!pdev)

return -EINVAL;

if (!pdev->dev.parent)

pdev->dev.parent = &platform_bus; // 设置为 platform_bus device

pdev->dev.bus = &platform_bus_type; // 设置为 platform_bus_type

// 拷贝pdev name到 device bus_id

if (pdev->id != -1)

dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);

else

dev_set_name(&pdev->dev, pdev->name);

// 将所有 resources添加到列表

for (i = 0; i < pdev->num_resources; i++) {

struct resource *p, *r = &pdev->resource[i];

if (r->name == NULL)

r->name = dev_name(&pdev->dev);

p = r->parent;

if (!p) {

if (resource_type(r) == IORESOURCE_MEM)

p = &iomem_resource;

else if (resource_type(r) == IORESOURCE_IO)

p = &ioport_resource;

}

if (p && insert_resource(p, r)) {

printk(KERN_ERR

"%s: failed to claim resource %d\n",

dev_name(&pdev->dev), i);

ret = -EBUSY;

linux系统设备模型

goto failed;

}

}

pr_debug("Registering platform device '%s'. Parent at %s\n",

dev_name(&pdev->dev), dev_name(pdev->dev.parent));

// 添加 pdev->dev 到device hierarchy,后面详细分析

ret = device_add(&pdev->dev);

if (ret == 0)

return ret;

// 失败后的清除工作

failed:

while (--i >= 0) {

struct resource *r = &pdev->resource[i];

unsigned long type = resource_type(r);

if (type == IORESOURCE_MEM || type == IORESOURCE_IO)

release_resource(r);

}

return ret;

}

代码中,

1. 如果dev没有设置parent,显然本例中没有设置,则执行pdev->dev.parent =

&platform_bus,这个 platform_bus的定义如下:

struct device platform_bus = {

.init_name= "platform",// 这个名字将“变成”/sys/devices/platform目录

};

其在

int __init platform_bus_init(void)

{

int error;

error = device_register(&platform_bus); // 注册platform_bus device

if (error)

return error;

error = bus_register(&platform_bus_type); //注册platform_bus_type bus

if (error)

device_unregister(&platform_bus);

return error;

}

中被调用,显然platform_bus_init ()例程在内核初始化的时候会被调用。 platform_bus 通过device_register(先后调用device_initialize()和device_add()例程)添加到device hierarchy,由于其没有class和parent,所以 platform_bus .kobj->parent = devices_kset.kobj,故生成/sys/devices/platform目录。由于dev.parent = &platform_bus,所以注册的dev.kobj生成的目录在/sys/devices/

linux系统设备模型

platform/下 。在本例中将生成/sys/devices/platform/s3c2410-rtc目录,后面会讲到。

2. pdev->dev.bus = &platform_bus_type表明该dev是platform_bus_type类型的,并且该dev也会被添加到platform_bus_type的devices_kset容器中和klist_devices列表中。

3. 如果pdev->id 不等于-1,那么说明其指定了序号,会被添加到pdev-name名字后面再拷贝给dev->bus_id,等于-1则直接拷贝。

4. 把所有的 resources 添加到列表中,iomem_resource和ioport_resource 为父节点。

5. 最后调用device_add(),把该dev添加到device hierarchy,下面就研究它。

三、device_add()定义在drivers/base/core.c中,其调用很多例程完成dev的添加工作,这个例程调用完后则完成添加工作,代码如下:

int device_add(struct device *dev)

{

struct device *parent = NULL;

struct class_interface *class_intf;

int error = -EINVAL;

dev = get_device(dev); // 增加对dev的引用,最终通过增加kobj->kref的引用来实现

if (!dev)// 判断是否成功

goto done;

/* Temporarily support init_name if it is set.

* It will override bus_id for now */

if (dev->init_name)// 如英文解释所言

dev_set_name(dev, "%s", dev->init_name);

if (!strlen(dev->bus_id))

goto done;// 判断是否设置了名字

pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

parent = get_device(dev->parent);  // 增加对dev->parent的引用

/* 设置kobj.parent,由于 s3c_device_rtc的dev不存在class,所以 dev->kobj.parent = dev->parent.kobj,也就是等于platform_bus.kobj ,后面会详细分析

*/

setup_parent(dev, parent);

/* use parent numa_node */

if (parent)

set_dev_node(dev, dev_to_node(parent));

// 添加kobj到kobj->kset(devices_kset )中,生成/sys/devices/platform/s3c2410-rtc 目录

/* first, register with generic layer. */

error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev_name(dev));

if (error)

goto Error;

/* notify platform of device entry */

linux系统设备模型

if (platform_notify)// NULL

platform_notify(dev);

// 在 s3c2410-rtc 目录下生成名为 “uevent” 的属性文件,所有注册的dev都会生成这个文件

error = device_create_file(dev, &uevent_attr);

if (error)

goto attrError;

// 如果有设备号,则生成相应的文件, s3c_device_rtc没有。

if (MAJOR(dev->devt)) {

/* 如果有,则在 s3c2410-rtc 目录下生成名为 “dev"的属性文件,这样udev就能读

取该属性文件获得设备号,从而在/dev目录下创建设备节点 */

error = device_create_file(dev, &devt_attr);

if (error)

goto ueventattrError;

/*

如果有,根据设备的类型,在sys/dev/char或block或其所属class指定的dev_kobj

目录下生成链接文件,其名字的样式为major:minor,如80:8,指向 s3c2410-rtc目

录。

*/

error = device_create_sys_dev_entry(dev);

if (error)

goto devtattrError;

}

/* 由于 s3c_device_rtc没有指定class,所以不会生成相应的链接,后面会详细分析。*/error = device_add_class_symlinks(dev);

if (error)

goto SymlinkError;

error = device_add_attrs(dev); // 生成类的属性文件和其他的属性文件

if (error)

goto AttrsError;

/*

将device加入到bus的kset中。

在s3c2410-rtc 目录下生成一个链接文件,其名为”subsystem",指向其所属的

platform_bus_type的目录/sys/bus/platform,以及在/sys/bus/platform/devices目录生成一个名为”s3c2410-rtc"的链接文件,指向/sys/devices/platform/s3c2410-rtc。后面会分析

*/

error = bus_add_device(dev);

if (error)

goto BusError;

error = dpm_sysfs_add(dev); // 在s3c2410-rtc 目录下生成power节点

if (error)

goto DPMError;

device_pm_add(dev); // 添加到活跃设备的链表里

linux系统设备模型

/* Notify clients of device addition. This call must come

* after dpm_sysf_add() and before kobject_uevent().

*/

// 是个回调机制,通知platform_bus_type上所有的设备ADD_DEVICE。

if (dev->bus)

blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

BUS_NOTIFY_ADD_DEVICE, dev);

// 通过uevents设置几个环境变量并通知用户空间,以便调用程序来完成相关设置

kobject_uevent(&dev->kobj, KOBJ_ADD);

// 将设备添加到platform_bus_type列表中,并寻找与其匹配的driver,后面会详细分析bus_attach_device(dev);

// 将该设备链接到其父设备,本例中就是platform_bus

if (parent)

klist_add_tail(&dev->knode_parent, &parent->klist_children);

// 没有设置class ,所以不调用。

if (dev->class) {

mutex_lock(&dev->class->p->class_mutex);

/* tie the class to the device */

klist_add_tail(&dev->knode_class,// 如英文解释所言

&dev->class->p->class_devices);

/* notify any interfaces that the device is here */

list_for_each_entry(class_intf,

&dev->class->p->class_interfaces, node)

if (class_intf->add_dev)

class_intf->add_dev(dev, class_intf);

mutex_unlock(&dev->class->p->class_mutex);

}

done:

put_device(dev);

return error;

DPMError:

bus_remove_device(dev);

BusError:

device_remove_attrs(dev);

AttrsError:

device_remove_class_symlinks(dev);

SymlinkError:

if (MAJOR(dev->devt))

device_remove_sys_dev_entry(dev);

devtattrError:

if (MAJOR(dev->devt))

device_remove_file(dev, &devt_attr);

ueventattrError:

device_remove_file(dev, &uevent_attr);

linux系统设备模型

attrError:

kobject_uevent(&dev->kobj, KOBJ_REMOVE);

kobject_del(&dev->kobj);

Error:

cleanup_device_parent(dev);

if (parent)

put_device(parent);

goto done;

}

代码中,

1. kobject_add ()例程会调用kobject_add_internal()例程来完成kobj的添加工作,下面简要说明下kobject_add_internal()例程,其中有一小段代码:

parent = kobject_get(kobj->parent);

if (kobj->kset) {

if (!parent)

parent = kobject_get(&kobj->kset->kobj);

kobj_kset_join(kobj);

kobj->parent = parent;

}

很明显在我们的例子里if (kobj->kset)里的代码会被执行,因为 kobj->kset = devices_kset。但由于dev->kobj-> parent = dev->parent.kobj(即platform_bus.kobj),所以if (!parent)面的代码不执行,故dev->kobj的目录(”s3c2410-rtc")产生在 dev->parent.kobj目录(”platform")里,即/sys/devices/platform/s3c2410-rtc。

2. 我们可以使用cat /sys/devices/platform/s3c2410-rtc/uevent来查看其内容,具体的过程是这样的,cat()-->......-->( dev.kobj->ktype->sysfs_ops->show() )-->( uevent_attr->show() ),其中 ktype 就是在device_initialize()中通过kobject_init(&dev->kobj, &device_ktype)例程调用初始化的

device_ktype ,在sysfs_ops->show() 中是通过to_dev_attr(attr)(也就是container_of())得到注册的 uevent_attr。对所有的属性文件的读取和写入都是这个过程,都是使用其所在目录的kobj-> ktype来完成的。

四、setup_parent()定义在drivers/base/core.c中,用于找到并设置dev->kobj.parent ,代码如下:

static void setup_parent(struct device *dev, struct device *parent)

{

struct kobject *kobj;

kobj = get_device_parent(dev, parent);// 后面分析

if (kobj)

dev->kobj.parent = kobj;// 显然,如果找到了parent,就赋给dev->kobj.parent

}

get_device_parent()代码如下:

static struct kobject *get_device_parent(struct device *dev,

struct device *parent)

{

int retval;

if (dev->class) {// 先判断是否设置了 class,本例中没有设置

linux系统设备模型

struct kobject *kobj = NULL;

struct kobject *parent_kobj;

struct kobject *k;

/*

* If we have no parent, we live in "virtual".

* Class-devices with a non class-device as parent, live

* in a "glue" directory to prevent namespace collisions.

*/

if (parent == NULL)

/*如果 parent为 NULL,那么会生成一个kobj作为parent_kobj 和相应

的/sys/devices/virtual目录

*/

parent_kobj = virtual_device_parent(dev);

else if (parent->class)

/*如果 parent->class存在,则返回 parent->kobj

*/

return &parent->kobj;

else

parent_kobj = &parent->kobj;

/* find our class-directory at the parent and reference it */

spin_lock(&dev->class->p->class_dirs.list_lock);

list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)

if (k->parent == parent_kobj) { // 从 class_dirs.list查找

kobj = kobject_get(k); // 当之前已经添加时则能找到

break;

}

spin_unlock(&dev->class->p->class_dirs.list_lock);

if (kobj)// 如果找到就直接返回,

return kobj;

/* or create a new class-directory at the parent device */

k = kobject_create();// 动态生成一个kobj

if (!k)

return NULL;

k->kset = &dev->class->p->class_dirs; // 指向 class_dirs kset

/* 添加 k->kobj到k->kset,并生成/sys/devices/platform/s3c2410-wdt/xx目录,其中

xxx为dev->class->name*/

retval = kobject_add(k, parent_kobj, "%s", dev->class->name);

if (retval < 0) {

kobject_put(k);

return NULL;

}

/* 返回新生成的kobj,故后面的dev->parent.kobj = kobj,会在 k的目录下生成

dev.kobj的目录*/

/* do not emit an uevent for this simple "glue" directory */

return k;

linux系统设备模型

if (parent)

return &parent->kobj;

return NULL;

}

代码中,

1. 举个例子说明这个例程的作用,在rtc-s3c.c中会调用rtc_device_register("s3c", &pdev->dev, &s3c_rtcops, THIS_MODULE)注册一个rtc_device,部分代码如下:

struct rtc_device *rtc_device_register(const char *name, struct device *dev,

const struct rtc_class_ops *ops,

struct module *owner)

{

rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);

rtc->dev.parent = dev;

rtc->dev.class = rtc_class;

dev_set_name(&rtc->dev, "rtc%d", id);

err = device_register(&rtc->dev);

pdev->dev是 &s3c_device_rtc,所以rtc->dev.parent = s3c_device_rtc,class 是 rtc_class, dev->bus_id是 “rtc+id",这里假设是”rtc0"。这样在get_device_parent()中新创建的kobj(k)对应的目录是/sys/devices/platform/rtc,而 rtc->dev.kobj生成的目录是/sys/devices/platform/rtc/rtc0。

五、device_add_class_symlinks()定义在drivers/base/core.c中,去掉了DEPRECATED部分,代码如下:

static int device_add_class_symlinks(struct device *dev)

{

int error;

if (!dev->class) // 如果没有设置class,那么就直接返回了,但不报错

return 0;

/* 在dev->kobj目录下生成一个名为”subsystem"链接文件,指向其所属的class在sys的目录/sys/class/xxx

*/

error = sysfs_create_link(&dev->kobj,

&dev->class->p->class_subsys.kobj,

"subsystem");

if (error)

goto out;

/* 在/sys/class/xxx目录生成一个名为 “dev_name(dev)"的链接文件,指

向/sys/devices/platform/dev_name(dev)。dev_name(dev)用于获取dev的name字符串,xxx代表class的目录。

linux系统设备模型

/* link in the class directory pointing to the device */

error = sysfs_create_link(&dev->class->p->class_subsys.kobj,

&dev->kobj, dev_name(dev));

if (error)

goto out_subsys;

/*在/sys/devices/platform/dev_name(dev)目录中生成一个名为“device”的链接文件,指向dev->parent.kobj目录,

*/

if (dev->parent && device_is_not_partition(dev)) {

error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,

"device");

if (error)

goto out_busid;

}

return 0;

out_busid:

sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev_name(dev));

out_subsys:

sysfs_remove_link(&dev->kobj, "subsystem");

out:

return error;

}

代码中

1. 以上面介绍过的rtc-s3c.c为例,则生成链接/sys/devices/platform/s3c2410-

rtc/rtc/rtc0/subsystem-->/sys/class/rtc,并且生成链接/sys/class/rtc/rtc0--

>/sys/devices/platform/s3c2410-rtc/rtc/rtc0,以及生成链接/sys/devices/platform/s3c2410-rtc/rtc/rtc0/device-->/sys/devices/platform/s3c2410-rtc。

六、接着执行bus_attach_device()例程,定义在drivers/base/bus.c中,代码如下:

void bus_attach_device(struct device *dev)

{

struct bus_type *bus = dev->bus; // 在这是 platform_bus_type

int ret = 0;

if (bus) {

if (bus->p->drivers_autoprobe) // 在 bus_register()例程中已经设置为1了

ret = device_attach(dev); // 从drver列表中找到与dev 相匹配的driver,后面

// 分析

WARN_ON(ret < 0);

// 添加到 klist_devices 列表,后续方便使用,比如匹配驱动程序什么的

if (ret >= 0)

klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);

}

linux系统设备模型

}

七、device_attach()定义在drivers/base/dd.c中,代码如下:

int device_attach(struct device *dev)

{

int ret = 0;

down(&dev->sem);// 进入临界区

if (dev->driver) {

/*如果设备和驱动已经关联了,则在dev目录下,即s3c2410-rtc 目录下生成名

为”driver"的链接文件,指向其关联的驱动dev->driver的sys目录,并且在dev->

driver的sys目录下生成链接文件,名字和dev的名字一样,即”3c2410-wdt ",指

向/sys/devices/platform/s3c2410-rtc 目录*/

ret = device_bind_driver(dev);

if (ret == 0)

ret = 1;

else {

dev->driver = NULL;

ret = 0;

}

} else {

/*没有关联则需要从 klist_drivers 找到关联的driver,一旦找到,则调用

__device_attach()回调函数*/

ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);

}

up(&dev->sem);  //出临界区

return ret;

}

代码中,

1. 以上面介绍过的rtc-s3c.c为例,则生成链接/sys/devices/platform/s3c2410-rtc/driver--

>/sys/bus/platform/drivers/s3c2410-rtc,以及生成链接/sys/bus/platform/drivers/s3c2410-rtc/s3c2410-rtc-->/sys/devices/platform/s3c2410-rtc。rtc-s3c.c driver生成的目录

是/sys/bus/platform/drivers/s3c2410-rtc。显然从程序中的platform_driver 定义能看出:static struct platform_driver s3c2410_rtc_driver = {

.probe= s3c_rtc_probe,

.remove= __devexit_p(s3c_rtc_remove),

.suspend= s3c_rtc_suspend,

.resume= s3c_rtc_resume,

.driver= {

.name= "s3c2410-rtc",// 目录就是这个名字

.owner= THIS_MODULE,

},

}。

__device_attach()也是定义在drivers/base/dd.c中,代码如下:

static int __device_attach(struct device_driver *drv, void *data)

{

linux系统设备模型

struct device *dev = data;

return driver_probe_device(drv, dev);

} // 这里开始probe

driver_probe_device()也是定义在drivers/base/dd.c中,代码如下:

int driver_probe_device(struct device_driver *drv, struct device *dev)

{

int ret = 0;

if (!device_is_registered(dev)) // 判断 dev 是否已经注册

return -ENODEV;

// 调用bus的match ,在这里是platform_bus_type的mach,即platform_match()

if (drv->bus->match && !drv->bus->match(dev, drv))

goto done;

pr_debug("bus: '%s': %s: matched device %s with driver %s\n",

drv->bus->name, __func__, dev_name(dev), drv->name);

// 这里真正开始调用用户在device_driver 中注册的porbe()例程

ret = really_probe(dev, drv);

done:

return ret;

}

八、platform_match()定义在drivers/base/platform.c中,代码如下:

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

{

struct platform_device *pdev;

pdev = container_of(dev, struct platform_device, dev); // 获取platform_device

return (strcmp(pdev->name, drv->name) == 0); // 仅仅是比较名字的字符串相同否

}

九、really_probe()定义在drivers/base/dd.c中,代码如下:

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

{

int ret = 0;

atomic_inc(&probe_count);

pr_debug("bus: '%s': %s: probing driver %s with device %s\n",

drv->bus->name, __func__, drv->name, dev_name(dev));

WARN_ON(!list_empty(&dev->devres_head));

dev->driver = drv; // 将匹配的driver指针关联到 dev,以便后续使用

/*如果设备和驱动已经关联了,则在dev目录下,即s3c2410-rtc 目录下生成名

linux系统设备模型

为”driver"的链接文件,指向其关联的驱动dev->driver的sys目录,并且在dev->

driver的sys目录下生成链接文件,名字和dev的名字一样,即”3c2410-wdt ",指

向/sys/devices/platform/s3c2410-rtc 目录

*/

if (driver_sysfs_add(dev)) {

printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",

__func__, dev_name(dev));

goto probe_failed;

}

// 如果设置了 dev->bus->probe,则调用,在platform_bus_type没有设置

if (dev->bus->probe) {

ret = dev->bus->probe(dev);

if (ret)

goto probe_failed;

/* 所以,调用驱动注册在device_driver里的probe,这个很常用,用于获得硬件资源,初始化硬件等,在后续对platform_driver的分析文章中会讲到。

*/

} else if (drv->probe) {

ret = drv->probe(dev);

if (ret)

goto probe_failed;

}

// 将device添加到driver列表中,并通知bus上的设备,表明BOUND_DRIVER。

driver_bound(dev);

ret = 1;

pr_debug("bus: '%s': %s: bound device %s to driver %s\n",

drv->bus->name, __func__, dev_name(dev), drv->name);

goto done;

probe_failed:

devres_release_all(dev);

driver_sysfs_remove(dev);

dev->driver = NULL;

if (ret != -ENODEV && ret != -ENXIO) {

/* driver matched but the probe failed */

printk(KERN_WARNING

"%s: probe of %s failed with error %d\n",

drv->name, dev_name(dev), ret);

}

/*

* Ignore errors returned by ->probe so that the next driver can try

* its luck.

*/

ret = 0;

done:

atomic_dec(&probe_count);

linux系统设备模型

wake_up(&probe_waitqueue);

return ret;

}

至此,platform_add_devices()例程完成调用。由于 s3c_device_rtc是在系统初始化的时候注册的,所以不会找到匹配的driver。当rtc-s3c.c驱动程序中使用platform_driver_register()例程注册platform_driver时也同样会寻找匹配的device,过程类似于寻找匹配的driver。所以rtc-s3c.c驱动程序驱动中注册的platform_drive的name必须和s3c_device_rtc中的相符。 后续将以rtc-s3c.c为例对platform_driver进行分析。

作个小结,从上面的分析可以看出,sys文件系统中devices、bus、class和dev目录里的内容之间的关联是通过调用device_register()和driver_register()例程来完成的。很显然,linux设备模型就这样建立起来了。

附图:

注:

1. 其中黑色字体的椭圆形表示是个文件夹;

2. 其中青色字体的椭圆形表示是个链接文件;

3. 用箭头表示文件夹之间的隶属关系和链接文件与文件夹之间

的链接关系。

linux设备模型浅析之设备篇.doc 将本文的Word文档下载到电脑

    精彩图片

    热门精选

    大家正在看

    × 游客快捷下载通道(下载后可以自由复制和排版)

    限时特价:7 元/份 原价:20元

    支付方式:

    开通VIP包月会员 特价:29元/月

    注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
    微信:fanwen365 QQ:370150219