LDM上层建筑之dev---局部窥探
时间:2016-02-22 13:04 来源:linux.it.net.cn 作者:IT
首先看下sys/devices怎么来的?
在初始的启动汇编中会跳到start_kernel---->rest_init---->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)---->do_basic_setup(void)---->driver_init(void)---->devices_init();
int __init devices_init(void)
{
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
return 0;
}
了解了kset后,可以知道在sys下面有了devices目录。
在smdk2440_machine_init函数中调用platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
而
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_usbgadget,
&s3c_device_ts,
&s3c_device_rtc,
&s3c_device_sdi,
};
所以platform_add_devices对这个结构体数组中的每一个设备做一次platform_device_register操作,我们用s3c_device_rtc为例。
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,
};
那么,
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
1、device_initialize(&pdev->dev);
这里pdev是&s3c_device_rtc
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; //devices_kset就是文章开头devices_init中的devices_kset
kobject_init(&dev->kobj, &device_ktype); //初始化dev->kobj
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}
2、platform_device_add(pdev);
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;
//
说明:
struct device platform_bus = {
.bus_id = "platform",
};
//
pdev->dev.bus = &platform_bus_type;
//
//struct bus_type platform_bus_type = {
// .name = "platform",
// .dev_attrs = platform_dev_attrs,
// .match = platform_match,
// .uevent = platform_uevent,
// .suspend = platform_suspend,
// .suspend_late = platform_suspend_late,
// .resume_early = platform_resume_early,
// .resume = platform_resume,
//};
//
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
pdev->id);
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
//因为在
//struct platform_device s3c_device_rtc = {
// .name = "s3c2410-rtc",
// .id = -1,
// .num_resources = ARRAY_SIZE(s3c_rtc_resource),
// .resource = s3c_rtc_resource,
//};中将id定为了-1
//所以这里走else
//将pdev->name拷贝到pdev->dev.bus_id中,这里我们的name是"s3c2410-rtc"。
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d/n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
//给pdev的resource做初始化
pr_debug("Registering platform device '%s'. Parent at %s/n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
ret = device_add(&pdev->dev); //关键的一步,见下面的展开
if (ret == 0)
return ret;
failed:
while (--i >= 0)
if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource[i]);
return ret;
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error;
dev = get_device(dev);
if (!dev || !strlen(dev->bus_id)) {
error = -EINVAL;
goto Done;
}
pr_debug("device: '%s': %s/n", dev->bus_id, __FUNCTION__);
parent = get_device(dev->parent);
//因为前面有pdev->dev.parent = &platform_bus;
//所以这里parent指向platform_bus。
setup_parent(dev, 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;
}
看get_device_parent(dev, parent);
因为没有定义CONFIG_SYSFS_DEPRECATED这个宏,所以这个函数用下面的
用下面的函数意味着所有的设备,无论是物理设备还是虚拟设备,都放在sys/devices目录下,
整个系统中的设备树统一在/sys/devices/目录中。
//*******************************//
这个函数如下:
static struct kobject *get_device_parent(struct device *dev,
struct device *parent)
{
int retval;
if (dev->class) { //没有看到定义dev->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_kobj = virtual_device_parent(dev);
else if (parent->class)
return &parent->kobj;
else
parent_kobj = &parent->kobj;
/* find our class-directory at the parent and reference it */
spin_lock(&dev->class->class_dirs.list_lock);
list_for_each_entry(k, &dev->class->class_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
spin_unlock(&dev->class->class_dirs.list_lock);
if (kobj)
return kobj;
/* or create a new class-directory at the parent device */
k = kobject_create();
if (!k)
return NULL;
k->kset = &dev->class->class_dirs;
retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
if (retval < 0) {
kobject_put(k);
return NULL;
}
/* do not emit an uevent for this simple "glue" directory */
return k;
}
if (parent) //走这里,返回&parent->kobj
return &parent->kobj;
return NULL;
}
//所以呢,dev->kobj.parent = kobj;将dev->kobj.parent 设为了platform_bus(name为"platform")这个device结构体的kobj
继续所以,pdev->dev.parent = &platform_bus;以及dev->kobj.parent = kobj;两个地方的父子关系都正确。
//***************************************//
/* first, register with generic layer. */
error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
//
将rtc这个dev的kobj作为第一个参数,dev->kobj.parent(platform_bus的kobj)作为第二个参数,而name为
dev->bus_id
而在前面说过了:
//将pdev->name拷贝到pdev->dev.bus_id中,这里我们的name是"s3c2410-rtc"。
所以这里的name就是"s3c2410-rtc".
那个可以看到这里的kobject_add操作会在devices下建立platform目录,platform目录下建立s3c2410-rtc目录。
//
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
/* notify clients of device entry (new way) */
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
//因为在platform_device_add函数开头处有pdev->dev.bus = &platform_bus_type;
//所以这个if会执行 但我看不懂这个会做什么事,先搁着吧。。。
//#define BUS_NOTIFY_ADD_DEVICE 0x00000001 /* device added */
// 是个回调机制,通知platform_bus_type上所有的设备ADD_DEVICE。
error = device_create_file(dev, &uevent_attr);
//在s3c2410-rtc目录下创建uevent属性文件,所有注册的dev都会生成这个文件
//具体uevent做什么用,以后再学习
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
}
//// 如果有设备号,则生成相应的文件, s3c_device_rtc没有。
///* 如果有,则在 s3c2410-rtc 目录下生成名为 “dev"的属性文件,这样udev就能读
//取该属性文件获得设备号,从而在/dev目录下创建设备节点 */
error = device_add_class_symlinks(dev);
/// 由于 s3c_device_rtc没有指定class,所以不会生成相应的链接
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
//具体看下函数(生成类的属性文件和其他的属性文件)
/********************************************
static int device_add_attrs(struct device *dev)
{
struct class *class = dev->class;
struct device_type *type = dev->type;
int error;
if (class) { //因为class不存在,这里不执行
error = device_add_attributes(dev, class->dev_attrs);
if (error)
return error;
}
if (type) { //type也不存在,这里也不执行
error = device_add_groups(dev, type->groups);
if (error)
goto err_remove_class_attrs;
}
error = device_add_groups(dev, dev->groups); //这里要执行,但我没找到dev->groups是什么,所以也没做什么
if (error)
goto err_remove_type_groups;
return 0;
err_remove_type_groups:
if (type)
device_remove_groups(dev, type->groups);
err_remove_class_attrs:
if (class)
device_remove_attributes(dev, class->dev_attrs);
return error;
}
*******************************************************/
if (error)
goto AttrsError;
error = dpm_sysfs_add(dev);//建立power的属性文件
if (error)
goto PMError;
device_pm_add(dev);//// 添加到活跃设备的链表里
error = bus_add_device(dev);
/**************************************
/**
* bus_add_device - add device to bus
* @dev: device being added
*
* - Add the device to its bus's list of devices.
* - Create link to device's bus.
*/
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
//首先获取dev->bus,就是说 bus 是指向platform_bus_type的
error = 0;
if (bus) {
pr_debug("bus: '%s': add device %s/n", bus->name, dev->bus_id);
error = device_add_attrs(bus, dev);
//*******************************
static int device_add_attrs(struct bus_type *bus, struct device *dev)
{
int error = 0;
int i;
if (!bus->dev_attrs)
return 0;
for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
error = device_create_file(dev, &bus->dev_attrs[i]);
if (error) {
while (--i >= 0)
device_remove_file(dev, &bus->dev_attrs[i]);
break;
}
}
return error;
}
//因为struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
...
}
所以不会return 0;看后面的for循环
建立文件名为modalias的属性文件
//
一般在bus的uevent函数中,都会添加MODALIAS环境变量,设置成dev的名字。这样,uevent传到用户空间后,就可以通过对MODALIAS的匹配自动加载模块。这样的bus例子有platform和I2C等等。
****************************//
if (error)
goto out_put;
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev->bus_id);
//*************************
sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)的注释:
/**
* sysfs_create_link - create symlink between two objects.
* @kobj: object whose directory we're creating the link in.
* @target: object we're pointing to.
* @name: name of the symlink.
*/
可以看出来这个函数在kobj所表示的目录中新建一个name为name的链接文件,该链接文件指向target这个kobject代表的目录。
具体到这句话,是说在/sys/bus/platform/devices目录生成一个
名为”s3c2410-rtc"的链接文件,指向/sys/devices/platform/s3c2410-rtc
************************//
if (error)
goto out_id;
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
//在s3c2410-rtc 目录下生成一个链接文件,其名为”subsystem",指向其所属的
platform_bus_type的目录/sys/bus/platform
if (error)
goto out_subsys;
error = make_deprecated_bus_links(dev);
//这一行没执行
if (error)
goto out_deprecated;
}
return 0;
out_deprecated:
sysfs_remove_link(&dev->kobj, "subsystem");
out_subsys:
sysfs_remove_link(&bus->p->devices_kset->kobj, dev->bus_id);
out_id:
device_remove_attrs(bus, dev);
out_put:
bus_put(dev->bus);
return error;
}
*********************************/
if (error)
goto BusError;
kobject_uevent(&dev->kobj, KOBJ_ADD);
//// 通过uevents设置几个环境变量并通知用户空间,以便调用程序来完成相关设置 uevent以后来学习
bus_attach_device(dev);
//***********************************
/**
* bus_attach_device - add device to bus
* @dev: device tried to attach to a driver
*
* - Add device to bus's list of devices.
* - Try to attach to driver.
*/
void bus_attach_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
int ret = 0;
if (bus) {
dev->is_registered = 1; //表明dev已经注册了
if (bus->p->drivers_autoprobe) //drivers_autoprobe会在bus_register函数中有bus->p = priv;和priv-//>drivers_autoprobe = 1;所以这里的if要跑
ret = device_attach(dev); //这个见下面
WARN_ON(ret < 0);
if (ret >= 0) //如果bus添加device成功了,就将dev的总线节点加到bus的dev链表中。
klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);
else //如果失败了,那么dev注册失败。
dev->is_registered = 0;
}
}
int device_attach(struct device *dev)
{
int ret = 0;
down(&dev->sem);
if (dev->driver) { //如果这个地方的dev有对应的driver了,那么就调用device_bind_driver函数
ret = device_bind_driver(dev); //bind a driver to one device 将他们捆绑起来
/*****************
/**
* device_bind_driver - bind a driver to one device.
* @dev: device.
*
* Allow manual attachment of a driver to a device.
* Caller must have already set @dev->driver.
********************/
int device_bind_driver(struct device *dev)
{
int ret;
ret = driver_sysfs_add(dev); //又和sysfs有关了,先调用一次sysfs_create_link函数来创建一个链接文件,链接文件
//的name是"s3c2410-rtc",该链接文件位于/buses/platform/drivers/s3c2410-rtc下面,指向devices/platform/s3c2410-rtc目录。如果这个创建成功了,再调用一次sysfs_create_link函数来创建一个链接文件,链接文件的name是“driver”,该链接文件位于
devices/platform/s3c2410-rtc目录下,指向/buses/platform/drivers/s3c2410-rtc目录。
if (!ret)
driver_bound(dev); //如果上面的sysfs链接文件创建成功了,那么执行driver_bound.
//将dev加入到与之相关联的driver的devices链表中(注:一个dev只能由一个driver来驱动,但是一个driver却可以驱动多个dev,driver的devices链表中的设备就是该driver所支持的所有设备)。
在driver_bound(dev)中有
//#define BUS_NOTIFY_BOUND_DRIVER 0x00000003/* driver bound to device */
// 是个回调机制,通知platform_bus_type上所有的设备。
//最后一步,是将dev加入到与之相关联的driver的devices链表中。
//klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices);
//单看下面:
/******************
/**
* klist_add_tail - Initialize a klist_node and add it to back.
* @n: node we're adding.
* @k: klist it's going on.
*/
void klist_add_tail(struct klist_node * n, struct klist * k)
{
klist_node_init(k, n);
add_tail(k, n);
}
klist当一个链表看,klist_node当链表上的一个节点。
struct klist {
spinlock_t k_lock;
struct list_head k_list;
void (*get)(struct klist_node *);
void (*put)(struct klist_node *);
};
struct klist_node {
struct klist * n_klist;
struct list_head n_node;
struct kref n_ref;
struct completion n_removed;
};
static void klist_node_init(struct klist * k, struct klist_node * n) //初始化函数
{
INIT_LIST_HEAD(&n->n_node);
init_completion(&n->n_removed);
kref_init(&n->n_ref);
n->n_klist = k; //主要的初始化操作,将节点的n_klist指针指向klist。
if (k->get)
k->get(n);
}
static void add_tail(struct klist * k, struct klist_node * n)
{
spin_lock(&k->k_lock);
list_add_tail(&n->n_node, &k->k_list); 将n->n_node加到k->k_list这个双向链表中
spin_unlock(&k->k_lock);
}
至此,将dev加入到与之相关联的driver的devices链表中
***********************/
return ret;
}
*******************/
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
//如果dev->driver为空,那就在bus中给dev找driver。遍历该总线上所有的driver,执行一次__device_attach(),看能不能将设备关联(attach)到某个已登记的驱动上去。
/***************
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
struct device_driver *drv;
int error = 0;
if (!bus) //既然函数名是bus_for_each_drv,没有bus肯定不行
return -EINVAL;
klist_iter_init_node(&bus->p->klist_drivers, &i,
start ? &start->p->knode_bus : NULL); //因为start为null,所以这里的第三个参数就为null
/******
void klist_iter_init_node(struct klist * k, struct klist_iter * i, struct klist_node * n)
{
i->i_klist = k; //i->i_klist=bus->p->klist_drivers
i->i_head = &k->k_list; //i->i_head=bus->p->klist_drivers->k_list
i->i_cur = n; //i->i_cur=null
if (n)
kref_get(&n->n_ref);
}
******/
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data); //通过while循环来遍历bus上的bus->p->klist_drivers所有driver,然后调用
//__device_attach(struct device_driver *drv, void *data)函数(其中data为dev)
/**********
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
return driver_probe_device(drv, dev); //driver 探测device
/******
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev)) //首先检查device是否注册了
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv)) //drv->bus->match =platform_match函数,
//执行这个函数,比较dev的name和driver的name,如果相等,返回1,所以这里的if就不成立,所以要跑下面的
//really_paobe函数
goto done;
pr_debug("bus: '%s': %s: matched device %s with driver %s/n",
drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);
ret = really_probe(dev, drv); // 这里真正开始调用用户在device_driver 中注册的porbe()例程
done:
return ret;
}
********/
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, __FUNCTION__, drv->name, dev->bus_id);
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv; //将dev和drv联系起来 将匹配的driver指针关联到 dev,
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",
__FUNCTION__, dev->bus_id);
goto probe_failed;
}
//又和sysfs有关了,先调用一次sysfs_create_link函数来创建一个链接文件,链接文件
//的name是"s3c2410-rtc",该链接文件位于/buses/platform/drivers/s3c2410-rtc下面,指向devices/platform/s3c2410-rtc目录。如果这个创建成功了,再调用一次sysfs_create_link函数来创建一个链接文件,链接文件的name是“driver”,该链接文件位于
devices/platform/s3c2410-rtc目录下,指向/buses/platform/drivers/s3c2410-rtc目录。
if (dev->bus->probe) { //如果dev->bus->probe 存在,调用这个probe函数,但是这个没有看到 在platform_bus_type没有设置
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) { //driver的probe函数,这里是
//static struct platform_driver s3c2410_rtcdrv = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
.name = "s3c2410-rtc",
.owner = THIS_MODULE,
},
};//有定义的,在分析上层建筑的时候再详细看这个probe函数
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev); ////将dev加入到与之相关联的driver的devices链表中 dev和drv相互关联起来了
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s/n",
drv->bus->name, __FUNCTION__, dev->bus_id, 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->bus_id, 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;
}
}
**********/
klist_iter_exit(&i);
return error;
}
***************/
}
up(&dev->sem);
return ret;
}
*************************************//
if (parent) //将dev的父节点加到platform这个种的klist_chilren链表。
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
down(&dev->class->sem);
/* tie the class to the device */
list_add_tail(&dev->node, &dev->class->devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf, &dev->class->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
Done:
put_device(dev);
return error;
BusError:
device_pm_remove(dev);
PMError:
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_DEL_DEVICE, dev);
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
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;
}
至此,platform_add_devices就走完了,但从这个分析过程来看,dev和bus、drv有很多很多的关系,要看完bus和drv后才可能
将三者的关系理清楚。
至少这个分析完了,sysfs下的devices目录就有内容了。
(责任编辑:IT)
首先看下sys/devices怎么来的? 在初始的启动汇编中会跳到start_kernel---->rest_init---->kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND)---->do_basic_setup(void)---->driver_init(void)---->devices_init(); int __init devices_init(void) { devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; return 0; } 了解了kset后,可以知道在sys下面有了devices目录。 在smdk2440_machine_init函数中调用platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)); 而 static struct platform_device *smdk2440_devices[] __initdata = { &s3c_device_usb, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c, &s3c_device_iis, &s3c_device_usbgadget, &s3c_device_ts, &s3c_device_rtc, &s3c_device_sdi, }; 所以platform_add_devices对这个结构体数组中的每一个设备做一次platform_device_register操作,我们用s3c_device_rtc为例。 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, }; 那么, int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); } 1、device_initialize(&pdev->dev); 这里pdev是&s3c_device_rtc void device_initialize(struct device *dev) { dev->kobj.kset = devices_kset; //devices_kset就是文章开头devices_init中的devices_kset kobject_init(&dev->kobj, &device_ktype); //初始化dev->kobj klist_init(&dev->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->dma_pools); INIT_LIST_HEAD(&dev->node); init_MUTEX(&dev->sem); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_init_wakeup(dev, 0); set_dev_node(dev, -1); } 2、platform_device_add(pdev); 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; // 说明: struct device platform_bus = { .bus_id = "platform", }; // pdev->dev.bus = &platform_bus_type; // //struct bus_type platform_bus_type = { // .name = "platform", // .dev_attrs = platform_dev_attrs, // .match = platform_match, // .uevent = platform_uevent, // .suspend = platform_suspend, // .suspend_late = platform_suspend_late, // .resume_early = platform_resume_early, // .resume = platform_resume, //}; // if (pdev->id != -1) snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name, pdev->id); else strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); //因为在 //struct platform_device s3c_device_rtc = { // .name = "s3c2410-rtc", // .id = -1, // .num_resources = ARRAY_SIZE(s3c_rtc_resource), // .resource = s3c_rtc_resource, //};中将id定为了-1 //所以这里走else //将pdev->name拷贝到pdev->dev.bus_id中,这里我们的name是"s3c2410-rtc"。 for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; if (r->name == NULL) r->name = pdev->dev.bus_id; p = r->parent; if (!p) { if (r->flags & IORESOURCE_MEM) p = &iomem_resource; else if (r->flags & IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { printk(KERN_ERR "%s: failed to claim resource %d/n", pdev->dev.bus_id, i); ret = -EBUSY; goto failed; } } //给pdev的resource做初始化 pr_debug("Registering platform device '%s'. Parent at %s/n", pdev->dev.bus_id, pdev->dev.parent->bus_id); ret = device_add(&pdev->dev); //关键的一步,见下面的展开 if (ret == 0) return ret; failed: while (--i >= 0) if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO)) release_resource(&pdev->resource[i]); return ret; } int device_add(struct device *dev) { struct device *parent = NULL; struct class_interface *class_intf; int error; dev = get_device(dev); if (!dev || !strlen(dev->bus_id)) { error = -EINVAL; goto Done; } pr_debug("device: '%s': %s/n", dev->bus_id, __FUNCTION__); parent = get_device(dev->parent); //因为前面有pdev->dev.parent = &platform_bus; //所以这里parent指向platform_bus。 setup_parent(dev, 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; } 看get_device_parent(dev, parent); 因为没有定义CONFIG_SYSFS_DEPRECATED这个宏,所以这个函数用下面的 用下面的函数意味着所有的设备,无论是物理设备还是虚拟设备,都放在sys/devices目录下, 整个系统中的设备树统一在/sys/devices/目录中。 //*******************************// 这个函数如下: static struct kobject *get_device_parent(struct device *dev, struct device *parent) { int retval; if (dev->class) { //没有看到定义dev->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_kobj = virtual_device_parent(dev); else if (parent->class) return &parent->kobj; else parent_kobj = &parent->kobj; /* find our class-directory at the parent and reference it */ spin_lock(&dev->class->class_dirs.list_lock); list_for_each_entry(k, &dev->class->class_dirs.list, entry) if (k->parent == parent_kobj) { kobj = kobject_get(k); break; } spin_unlock(&dev->class->class_dirs.list_lock); if (kobj) return kobj; /* or create a new class-directory at the parent device */ k = kobject_create(); if (!k) return NULL; k->kset = &dev->class->class_dirs; retval = kobject_add(k, parent_kobj, "%s", dev->class->name); if (retval < 0) { kobject_put(k); return NULL; } /* do not emit an uevent for this simple "glue" directory */ return k; } if (parent) //走这里,返回&parent->kobj return &parent->kobj; return NULL; } //所以呢,dev->kobj.parent = kobj;将dev->kobj.parent 设为了platform_bus(name为"platform")这个device结构体的kobj 继续所以,pdev->dev.parent = &platform_bus;以及dev->kobj.parent = kobj;两个地方的父子关系都正确。 //***************************************// /* first, register with generic layer. */ error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id); // 将rtc这个dev的kobj作为第一个参数,dev->kobj.parent(platform_bus的kobj)作为第二个参数,而name为 dev->bus_id 而在前面说过了: //将pdev->name拷贝到pdev->dev.bus_id中,这里我们的name是"s3c2410-rtc"。 所以这里的name就是"s3c2410-rtc". 那个可以看到这里的kobject_add操作会在devices下建立platform目录,platform目录下建立s3c2410-rtc目录。 // if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); /* notify clients of device entry (new way) */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); //因为在platform_device_add函数开头处有pdev->dev.bus = &platform_bus_type; //所以这个if会执行 但我看不懂这个会做什么事,先搁着吧。。。 //#define BUS_NOTIFY_ADD_DEVICE 0x00000001 /* device added */ // 是个回调机制,通知platform_bus_type上所有的设备ADD_DEVICE。 error = device_create_file(dev, &uevent_attr); //在s3c2410-rtc目录下创建uevent属性文件,所有注册的dev都会生成这个文件 //具体uevent做什么用,以后再学习 if (error) goto attrError; if (MAJOR(dev->devt)) { error = device_create_file(dev, &devt_attr); if (error) goto ueventattrError; } //// 如果有设备号,则生成相应的文件, s3c_device_rtc没有。 ///* 如果有,则在 s3c2410-rtc 目录下生成名为 “dev"的属性文件,这样udev就能读 //取该属性文件获得设备号,从而在/dev目录下创建设备节点 */ error = device_add_class_symlinks(dev); /// 由于 s3c_device_rtc没有指定class,所以不会生成相应的链接 if (error) goto SymlinkError; error = device_add_attrs(dev); //具体看下函数(生成类的属性文件和其他的属性文件) /******************************************** static int device_add_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; int error; if (class) { //因为class不存在,这里不执行 error = device_add_attributes(dev, class->dev_attrs); if (error) return error; } if (type) { //type也不存在,这里也不执行 error = device_add_groups(dev, type->groups); if (error) goto err_remove_class_attrs; } error = device_add_groups(dev, dev->groups); //这里要执行,但我没找到dev->groups是什么,所以也没做什么 if (error) goto err_remove_type_groups; return 0; err_remove_type_groups: if (type) device_remove_groups(dev, type->groups); err_remove_class_attrs: if (class) device_remove_attributes(dev, class->dev_attrs); return error; } *******************************************************/ if (error) goto AttrsError; error = dpm_sysfs_add(dev);//建立power的属性文件 if (error) goto PMError; device_pm_add(dev);//// 添加到活跃设备的链表里 error = bus_add_device(dev); /************************************** /** * bus_add_device - add device to bus * @dev: device being added * * - Add the device to its bus's list of devices. * - Create link to device's bus. */ int bus_add_device(struct device *dev) { struct bus_type *bus = bus_get(dev->bus); //首先获取dev->bus,就是说 bus 是指向platform_bus_type的 error = 0; if (bus) { pr_debug("bus: '%s': add device %s/n", bus->name, dev->bus_id); error = device_add_attrs(bus, dev); //******************************* static int device_add_attrs(struct bus_type *bus, struct device *dev) { int error = 0; int i; if (!bus->dev_attrs) return 0; for (i = 0; attr_name(bus->dev_attrs[i]); i++) { error = device_create_file(dev, &bus->dev_attrs[i]); if (error) { while (--i >= 0) device_remove_file(dev, &bus->dev_attrs[i]); break; } } return error; } //因为struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, ... } 所以不会return 0;看后面的for循环 建立文件名为modalias的属性文件 // 一般在bus的uevent函数中,都会添加MODALIAS环境变量,设置成dev的名字。这样,uevent传到用户空间后,就可以通过对MODALIAS的匹配自动加载模块。这样的bus例子有platform和I2C等等。 ****************************// if (error) goto out_put; error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev->bus_id); //************************* sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name)的注释: /** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. * @target: object we're pointing to. * @name: name of the symlink. */ 可以看出来这个函数在kobj所表示的目录中新建一个name为name的链接文件,该链接文件指向target这个kobject代表的目录。 具体到这句话,是说在/sys/bus/platform/devices目录生成一个 名为”s3c2410-rtc"的链接文件,指向/sys/devices/platform/s3c2410-rtc ************************// if (error) goto out_id; error = sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem"); //在s3c2410-rtc 目录下生成一个链接文件,其名为”subsystem",指向其所属的 platform_bus_type的目录/sys/bus/platform if (error) goto out_subsys; error = make_deprecated_bus_links(dev); //这一行没执行 if (error) goto out_deprecated; } return 0; out_deprecated: sysfs_remove_link(&dev->kobj, "subsystem"); out_subsys: sysfs_remove_link(&bus->p->devices_kset->kobj, dev->bus_id); out_id: device_remove_attrs(bus, dev); out_put: bus_put(dev->bus); return error; } *********************************/ if (error) goto BusError; kobject_uevent(&dev->kobj, KOBJ_ADD); //// 通过uevents设置几个环境变量并通知用户空间,以便调用程序来完成相关设置 uevent以后来学习 bus_attach_device(dev); //*********************************** /** * bus_attach_device - add device to bus * @dev: device tried to attach to a driver * * - Add device to bus's list of devices. * - Try to attach to driver. */ void bus_attach_device(struct device *dev) { struct bus_type *bus = dev->bus; int ret = 0; if (bus) { dev->is_registered = 1; //表明dev已经注册了 if (bus->p->drivers_autoprobe) //drivers_autoprobe会在bus_register函数中有bus->p = priv;和priv-//>drivers_autoprobe = 1;所以这里的if要跑 ret = device_attach(dev); //这个见下面 WARN_ON(ret < 0); if (ret >= 0) //如果bus添加device成功了,就将dev的总线节点加到bus的dev链表中。 klist_add_tail(&dev->knode_bus, &bus->p->klist_devices); else //如果失败了,那么dev注册失败。 dev->is_registered = 0; } } int device_attach(struct device *dev) { int ret = 0; down(&dev->sem); if (dev->driver) { //如果这个地方的dev有对应的driver了,那么就调用device_bind_driver函数 ret = device_bind_driver(dev); //bind a driver to one device 将他们捆绑起来 /***************** /** * device_bind_driver - bind a driver to one device. * @dev: device. * * Allow manual attachment of a driver to a device. * Caller must have already set @dev->driver. ********************/ int device_bind_driver(struct device *dev) { int ret; ret = driver_sysfs_add(dev); //又和sysfs有关了,先调用一次sysfs_create_link函数来创建一个链接文件,链接文件 //的name是"s3c2410-rtc",该链接文件位于/buses/platform/drivers/s3c2410-rtc下面,指向devices/platform/s3c2410-rtc目录。如果这个创建成功了,再调用一次sysfs_create_link函数来创建一个链接文件,链接文件的name是“driver”,该链接文件位于 devices/platform/s3c2410-rtc目录下,指向/buses/platform/drivers/s3c2410-rtc目录。 if (!ret) driver_bound(dev); //如果上面的sysfs链接文件创建成功了,那么执行driver_bound. //将dev加入到与之相关联的driver的devices链表中(注:一个dev只能由一个driver来驱动,但是一个driver却可以驱动多个dev,driver的devices链表中的设备就是该driver所支持的所有设备)。 在driver_bound(dev)中有 //#define BUS_NOTIFY_BOUND_DRIVER 0x00000003/* driver bound to device */ // 是个回调机制,通知platform_bus_type上所有的设备。 //最后一步,是将dev加入到与之相关联的driver的devices链表中。 //klist_add_tail(&dev->knode_driver, &dev->driver->p->klist_devices); //单看下面: /****************** /** * klist_add_tail - Initialize a klist_node and add it to back. * @n: node we're adding. * @k: klist it's going on. */ void klist_add_tail(struct klist_node * n, struct klist * k) { klist_node_init(k, n); add_tail(k, n); } klist当一个链表看,klist_node当链表上的一个节点。 struct klist { spinlock_t k_lock; struct list_head k_list; void (*get)(struct klist_node *); void (*put)(struct klist_node *); }; struct klist_node { struct klist * n_klist; struct list_head n_node; struct kref n_ref; struct completion n_removed; }; static void klist_node_init(struct klist * k, struct klist_node * n) //初始化函数 { INIT_LIST_HEAD(&n->n_node); init_completion(&n->n_removed); kref_init(&n->n_ref); n->n_klist = k; //主要的初始化操作,将节点的n_klist指针指向klist。 if (k->get) k->get(n); } static void add_tail(struct klist * k, struct klist_node * n) { spin_lock(&k->k_lock); list_add_tail(&n->n_node, &k->k_list); 将n->n_node加到k->k_list这个双向链表中 spin_unlock(&k->k_lock); } 至此,将dev加入到与之相关联的driver的devices链表中 ***********************/ return ret; } *******************/ if (ret == 0) ret = 1; else { dev->driver = NULL; ret = 0; } } else { ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); //如果dev->driver为空,那就在bus中给dev找driver。遍历该总线上所有的driver,执行一次__device_attach(),看能不能将设备关联(attach)到某个已登记的驱动上去。 /*************** int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)) { struct klist_iter i; struct device_driver *drv; int error = 0; if (!bus) //既然函数名是bus_for_each_drv,没有bus肯定不行 return -EINVAL; klist_iter_init_node(&bus->p->klist_drivers, &i, start ? &start->p->knode_bus : NULL); //因为start为null,所以这里的第三个参数就为null /****** void klist_iter_init_node(struct klist * k, struct klist_iter * i, struct klist_node * n) { i->i_klist = k; //i->i_klist=bus->p->klist_drivers i->i_head = &k->k_list; //i->i_head=bus->p->klist_drivers->k_list i->i_cur = n; //i->i_cur=null if (n) kref_get(&n->n_ref); } ******/ while ((drv = next_driver(&i)) && !error) error = fn(drv, data); //通过while循环来遍历bus上的bus->p->klist_drivers所有driver,然后调用 //__device_attach(struct device_driver *drv, void *data)函数(其中data为dev) /********** static int __device_attach(struct device_driver *drv, void *data) { struct device *dev = data; return driver_probe_device(drv, dev); //driver 探测device /****** int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; if (!device_is_registered(dev)) //首先检查device是否注册了 return -ENODEV; if (drv->bus->match && !drv->bus->match(dev, drv)) //drv->bus->match =platform_match函数, //执行这个函数,比较dev的name和driver的name,如果相等,返回1,所以这里的if就不成立,所以要跑下面的 //really_paobe函数 goto done; pr_debug("bus: '%s': %s: matched device %s with driver %s/n", drv->bus->name, __FUNCTION__, dev->bus_id, drv->name); ret = really_probe(dev, drv); // 这里真正开始调用用户在device_driver 中注册的porbe()例程 done: return ret; } ********/ 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, __FUNCTION__, drv->name, dev->bus_id); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; //将dev和drv联系起来 将匹配的driver指针关联到 dev, if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n", __FUNCTION__, dev->bus_id); goto probe_failed; } //又和sysfs有关了,先调用一次sysfs_create_link函数来创建一个链接文件,链接文件 //的name是"s3c2410-rtc",该链接文件位于/buses/platform/drivers/s3c2410-rtc下面,指向devices/platform/s3c2410-rtc目录。如果这个创建成功了,再调用一次sysfs_create_link函数来创建一个链接文件,链接文件的name是“driver”,该链接文件位于 devices/platform/s3c2410-rtc目录下,指向/buses/platform/drivers/s3c2410-rtc目录。 if (dev->bus->probe) { //如果dev->bus->probe 存在,调用这个probe函数,但是这个没有看到 在platform_bus_type没有设置 ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { //driver的probe函数,这里是 //static struct platform_driver s3c2410_rtcdrv = { .probe = s3c_rtc_probe, .remove = s3c_rtc_remove, .suspend = s3c_rtc_suspend, .resume = s3c_rtc_resume, .driver = { .name = "s3c2410-rtc", .owner = THIS_MODULE, }, };//有定义的,在分析上层建筑的时候再详细看这个probe函数 ret = drv->probe(dev); if (ret) goto probe_failed; } driver_bound(dev); ////将dev加入到与之相关联的driver的devices链表中 dev和drv相互关联起来了 ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s/n", drv->bus->name, __FUNCTION__, dev->bus_id, 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->bus_id, 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; } } **********/ klist_iter_exit(&i); return error; } ***************/ } up(&dev->sem); return ret; } *************************************// if (parent) //将dev的父节点加到platform这个种的klist_chilren链表。 klist_add_tail(&dev->knode_parent, &parent->klist_children); if (dev->class) { down(&dev->class->sem); /* tie the class to the device */ list_add_tail(&dev->node, &dev->class->devices); /* notify any interfaces that the device is here */ list_for_each_entry(class_intf, &dev->class->interfaces, node) if (class_intf->add_dev) class_intf->add_dev(dev, class_intf); up(&dev->class->sem); } Done: put_device(dev); return error; BusError: device_pm_remove(dev); PMError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: 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; } 至此,platform_add_devices就走完了,但从这个分析过程来看,dev和bus、drv有很多很多的关系,要看完bus和drv后才可能 将三者的关系理清楚。 至少这个分析完了,sysfs下的devices目录就有内容了。 (责任编辑:IT) |