首先看下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) |