platform device driver註冊過程

platform設備註冊過程。
已/sys/devices/platform/xxxxx 目錄下的文件與/sys/bus/platform/xxxxx之間是用軟連接對應起來的。
系統每添加一個Kobject文件,就對應一個sys目錄下的一個目錄。
在系統調用的讀文件時候,會判斷文件類型,如果是sysfs文件系統,則會根據Kset–查找得到Kobject–>Kobj_type.attribute—->Kobj_type.sysfs_ops—>sysfs_ops.show/store.
如果是MTD設備文件,則會去判斷文件系統類型,調用mtd文件系統的讀寫方法。
如果是網絡設備文件,則會調用系統的socket接口。
如果是字符設備文件,則會調用cdev的file_operations_ops。
下面只分析sysfs的platform。

int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}

void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;//dev->kobj.kset=device_kset,即/sys/devices。
kobject_init(&dev->kobj, &device_ktype);//kobj_type類型,包含sysfs_ops。sysfs_ops包含show、store方法。在它的show、store方法中,會根據attr和container_of找到對應的device_attribute。然後調用他的show、store方法。根據kobj找到dev,device_attribute的show方法的參數是(dev,device_attribute,buff)。注意device_attribute不同於Kobject_type,sysfs_ops;
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
}

    接下來看看platform_device_add

/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we’re adding
*
* This is part 2 of platform_device_register(), though may be called
* separately iff pdev was allocated by platform_device_alloc().
*/
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;

pdev->dev.bus = &platform_bus_type;

if (pdev->id != -1)
    dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
else
    dev_set_name(&pdev->dev, "%s", pdev->name);

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));

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;

}
EXPORT_SYMBOL_GPL(platform_device_add);

上面的device_initialize設置了他的kset和kobj_type。
下面的platform_device_add就是真正的把設備添加到總線上了。
        pdev->dev.parent = &platform_bus;
1.設置他的父指針指向platform_bus,接下來Kobject_add的時候會用到這個父指針,這樣就建立了/sys/devices/platform/xxxxx的文件夾。
2. 設置dev的總線類型爲platform_bus_type
3. 設置資源文件
4. 指向device_add

int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;

dev = get_device(dev);
if (!dev)
    goto done;

if (!dev->p) {
    error = device_private_init(dev);
    if (error)
        goto done;
}

/*
 * for statically allocated devices, which should all be converted
 * some day, we need to initialize the name. We prevent reading back
 * the name, and force the use of dev_name()
 */
if (dev->init_name) {
    dev_set_name(dev, "%s", dev->init_name);
    dev->init_name = NULL;
}

if (!dev_name(dev)) {
    error = -EINVAL;
    goto name_error;
}

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

parent = get_device(dev->parent);
setup_parent(dev, parent);

/* use parent numa_node */
if (parent)
    set_dev_node(dev, dev_to_node(parent));

/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
    goto Error;

/* notify platform of device entry */
if (platform_notify)
    platform_notify(dev);

error = device_create_file(dev, &uevent_attr);
if (error)
    goto attrError;

if (MAJOR(dev->devt)) {
    error = device_create_file(dev, &devt_attr);
    if (error)
        goto ueventattrError;

    error = device_create_sys_dev_entry(dev);
    if (error)
        goto devtattrError;

    devtmpfs_create_node(dev);
}

error = device_add_class_symlinks(dev);
if (error)
    goto SymlinkError;
error = device_add_attrs(dev);
if (error)
    goto AttrsError;
error = bus_add_device(dev);
if (error)
    goto BusError;
error = dpm_sysfs_add(dev);
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().
 */
if (dev->bus)
    blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                     BUS_NOTIFY_ADD_DEVICE, dev);

kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);
if (parent)
    klist_add_tail(&dev->p->knode_parent,
               &parent->p->klist_children);

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->klist_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))
devtmpfs_delete_node(dev);
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);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}

下面分析device_add函數
1.設置設備名,dev.kobj.parent爲之前設置過的parent,這裏就是devices_kset。
2.kobject_add,kobject_add會把obj加入到kobj.kset的list中。
3.創建uevent、dev_attr、以及軟連接文件。
4.bus_add_device,創建軟連接文件且       klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); 把device->p->knode_bus加入到bus->p->klist_devices。以後bus可以用bus->p->klist_devices來遍歷掛載他上面的所有設備。
5.      kobject_uevent(&dev->kobj, KOBJ_ADD);
如果suppress不爲真,並且也沒有被過濾掉,最終就會執行/sbin/hotplug命令。
6.然後執行bus_probe_device,他裏面調用了device_attach函數,device_attach函數會判斷device的driver成員是否爲空,如果不爲空則把dev和他的driver進行綁定。如果爲空,則會在總線上遍歷driver,如果bus上的match函數返回真,則執行driver的probe函數。然後把driver和device進行綁定。
7.與開始對應,對dev的引用計數減1.

下面分析下driver的註冊過程。

int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;

return driver_register(&drv->driver);

}

int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;

BUG_ON(!drv->bus->p);

if ((drv->bus->probe && drv->probe) ||
    (drv->bus->remove && drv->remove) ||
    (drv->bus->shutdown && drv->shutdown))
    printk(KERN_WARNING "Driver '%s' needs updating - please use "
        "bus_type methods\n", drv->name);

other = driver_find(drv->name, drv->bus);
if (other) {
    put_driver(other);
    printk(KERN_ERR "Error: Driver '%s' is already registered, "
        "aborting...\n", drv->name);
    return -EBUSY;
}

ret = bus_add_driver(drv);
if (ret)
    return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
    bus_remove_driver(drv);
return ret;

}
EXPORT_SYMBOL_GPL(driver_register);

1,先設置driver的bus類型
2.執行driver_register
3、driver_register裏面先根據driver_find看是否已經註冊過,如果已經註冊過則直接返回BUSY
4. 執行bus_add_driver。    priv->kobj.kset = bus->p->drivers_kset;
5.如果driver-bus->p->autoprobo爲真,則執行driver_attach,driver_attach根據bus的match函數返回值來判斷是否要執行probe函數,如果match函數爲真,則綁定,且執行probe函數,並返回執行結果。
6,  klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
7,創建屬性文件
8.  kobject_uevent(&priv->kobj, KOBJ_ADD);會根據suppress標誌和bustype的subsys_private的Kset subsys的filter函數,如果方法來判斷是否要執行sbin/hotplug命令
9. `struct bus_type {
    const char      *name;
    struct bus_attribute    *bus_attrs;
    struct device_attribute *dev_attrs;
    struct driver_attribute *drv_attrs;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);
    void (*shutdown)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    const struct dev_pm_ops *pm;

    struct subsys_private *p;
};
struct subsys_private {
    struct kset subsys;
    struct kset *devices_kset;

    struct kset *drivers_kset;
    struct klist klist_devices;
    struct klist klist_drivers;
    struct blocking_notifier_head bus_notifier;
    unsigned int drivers_autoprobe:1;
    struct bus_type *bus;

    struct list_head class_interfaces;
    struct kset glue_dirs;
    struct mutex class_mutex;
    struct class *class;
};

`

struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
struct kset_uevent_ops {
int (* const filter)(struct kset *kset, struct kobject *kobj);
const char ( const name)(struct kset *kset, struct kobject *kobj);
int (* const uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
“`
10,執行uevent_helper命令,其中
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;//sbin/mdev
所以執行的就是/sbin/mdev命令。
綜上可以知道
struct bus_type 的 struct subsys_private *p;成員有幾個很重要的成員
struct kset subsys; //提供了sysfs需要的一些方法,如uvent_ops
struct kset *devices_kset;//掛載在總線上的設備kset。/sys/bus/platform/devices/ 目錄下都是軟連接到/sys/devices/platform/xxx。然後 klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);

struct kset *drivers_kset;//掛載在總線上的驅動kset,在bus_add_driver中會把driver的driver_private的kobj.kset設置爲bus->p->drivers_kset;然後執行kobject_init_and_add.就會在/sys/bus/platform/driver目錄下建立對應的文件夾。
struct klist klist_devices;//可以根據這個遍歷總線上的設備
struct klist klist_drivers;//可以根據這個遍歷總線上的驅動。

1)devices_kset和drivers_kset分別指向該總線下所有設備和驅動的集合,在sysfs中,/sys/bus/xxx/下總是存在/sys/bus/xxx/devices和/sys/bus/xxx/drivers目錄。

2)bus_type還管理着兩個鏈表,klist_devices和 klist_drivers,分別表示該總線下的所有設備和所有驅動。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章