Linux设备模型浅析之设备

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); // 初试化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结构体中包含有一个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;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/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 */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); // 添加到活跃设备的链表里/* 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); 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(); // 动态生成一个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;}
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的目录。*/
/* 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);
}}
七、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);  // 这里开始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 目录下生成名” 为 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()例程来完成的。很显然,linux设备
模型就这样建立起来了。
附图:
注:
1. 其中黑色字体的椭圆形表示是个文件夹;
2. 其中青色字体的椭圆形表示是个链接文件;
3. 用箭头表示文件夹之间的隶属关系和链接文件与文件夹之间的链接关系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值