您的当前位置:首页正文

Linux设备模型浅析之设备篇

2024-07-10 来源:九壹网
Linux设备模型浅析之设备篇

本文属本人原创,欢转载,转载请注明出处。由于个人的见识和能力有限,不可能面面俱到,也可能存在谬误,敬请网友指出,本人的邮箱是yzq.seen@gmail.com,博客是http:// zhiqiang0071.cublog.cn 。 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,

&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);// 初试化klistINIT_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\来创建。由

于参数parent=NULL ,所以生成/sys/devices 目录。这里说明下kobj,kset结构体中包含有一个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 devicepdev->dev.bus = &platform_bus_type;

// 设置为 platform_bus_type

// 拷贝pdev name到 device bus_idif (pdev->id != -1)

dev_set_name(&pdev->dev, \"%s.%d\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;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 deviceif (error)

return error;

error = bus_register(&platform_bus_type); //注册platform_bus_type busif (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/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\if (!strlen(dev->bus_id))

goto done;

// 判断是否设置了名字

pr_debug(\"device: '%s': %s\\n\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\if (error)

goto Error;

/* notify platform of device entry */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 目录下生成名为 “devt \"的文件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); // 添加到活跃设备的链表里

/* 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_busif (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); 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,本例中没有设置

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();// 动态生成一个kobjif (!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\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;}

if (parent)

return &parent->kobj;return NULL;

}

代码中,

1. 举个例子说明这个例程的作用,在rtc-s3c.c中会调用rtc_device_register(\"s3c\>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\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的目录。*/

/* 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_typeint 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);

}}

七、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)

{

struct device *dev = data;

return driver_probe_device(drv, dev);}

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,以便后续使用

// 这里开始probe

/*如果设备和驱动已经关联了,则在dev目录下,即s3c2410-rtc 目录下生成名为”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);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()例程来完成的。很显然,device model就这样建立起来了。

附图:

注:

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

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

因篇幅问题不能全部显示,请点此查看更多更全内容