device_register

原文地址:我對linux的理解之device_register 作者:amingriyue
------------------------------------------
本文系本站原創,歡迎轉載!
轉載請註明出處:amingriyue.blog.chinaunix.net
------------------------------------------


/**
 * device_register - register a device with the system.
 * @dev: pointer to the device structure
 *
 * This happens in two clean steps - initialize the device
 * and add it to the system. The two steps can be called
 * separately, but this is the easiest and most common.
 * I.e. you should only call the two helpers separately if
 * have a clearly defined need to use and refcount the device
 * before it is added to the hierarchy.
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up the
 * reference initialized in this function instead.
 */
int device_register(struct device *dev)
{
    device_initialize(dev);  //初始化dev,見第1部分分析
    return device_add(dev);  //添加設備,這是device_register的主要工作,見第2部分分析
}



函數定義很簡潔,第一步初始化dev,第二步添加設備。我們也分兩部分進行分析:
1,device_initialize()


void device_initialize(struct device *dev)
{
    dev->kobj.kset = devices_kset;  //devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL),即devices_kset代表了/sys/devices/
    kobject_init(&dev->kobj, &device_ktype);  //初始化dev的kobj,這個函數在driver_register中分析過了
    INIT_LIST_HEAD(&dev->dma_pools);  //初始化dev的內存池的列表
    init_MUTEX(&dev->sem);  //初始化信號量
    spin_lock_init(&dev->devres_lock); //初始化自旋鎖
    INIT_LIST_HEAD(&dev->devres_head); //初始化隊列頭
    device_init_wakeup(dev, 0);  //由其定義知初始化dev->power.can_wakeup = dev->power.should_wakeup = 0;
    device_pm_init(dev);  //初始化dev->power.status = DPM_ON;
    set_dev_node(dev, -1); //如果配置了numa,設置dev->numa_node = -1;  numa是非統一內存訪問架構,一般用於服務器中,所以一般嵌入式中不使用。
}



這個dev的初始化主要是設置dev結構中的各個變量等以及對kobj的相關操作。


2,device_add()


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;


    dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); //這裏p是dev中的device_private結構,跟驅動的driver_private相似
    if (!dev->p) {
        error = -ENOMEM;
        goto done;
    }
    dev->p->device = dev; //將dev賦值給p的device保存
    klist_init(&dev->p->klist_children, klist_children_get,
               klist_children_put);  //初始化p的klist_children


    /*
     * 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))
        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)); //不使用numa


    /* 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);  //這個函數在driver_register中分析過了,主要將kobj添加到sys的層次中
    if (error)
        goto Error;


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


    error = device_create_file(dev, &uevent_attr);  //在driver_register中已經分析,主要是在/sys/devices/.../中添加dev的uevent屬性文件
    if (error)
        goto attrError;


    if (MAJOR(dev->devt)) {
        error = device_create_file(dev, &devt_attr);  //主要是在sys/devices/...中添加dev屬性文件
        if (error)
            goto ueventattrError;


        error = device_create_sys_dev_entry(dev); //在/sys/dev/char/或者/sys/dev/block/創建devt的屬性的連接文件,形如10:45,由主設備號和次設備號構成,指向/sys/devices/.../的具體設備目錄,該鏈接文件只具備讀屬性,顯示主設備號:次設備號,如10:45,用戶空間udev相應uevent事件時,將根據設備號在/dev下創建節點文件
        if (error)
            goto devtattrError;
    }


    error = device_add_class_symlinks(dev); //相互創建dev和class之間的鏈接文件
    if (error)
        goto SymlinkError;
    error = device_add_attrs(dev); //添加設備屬性文件
    if (error)
        goto AttrsError;
    error = bus_add_device(dev); //將設備添加到bus上,創建subsystem鏈接文件,鏈接class下的具體的子系統文件夾
    if (error)
        goto BusError;
    error = dpm_sysfs_add(dev); //添加設備的電源管理屬性,截止這裏,我們的/sys/devices/.../具體設備目錄下至少生成有以下四個屬性文件:uevent,dev,subsystem,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().
     */
    if (dev->bus)
        blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                     BUS_NOTIFY_ADD_DEVICE, dev);//執行bus通知鏈上的註冊函數,由設備註冊上來


    kobject_uevent(&dev->kobj, KOBJ_ADD); //產生一個KOBJ_ADD的uevent事件,通過netlink機制和用戶空間通信,這個driver_register中已經分析過了
    bus_probe_device(dev); //去bus上找dev對應的drv,主要執行__device_attach,主要進行match,sys_add,執行probe函數和綁定等操作
    if (parent)
        klist_add_tail(&dev->p->knode_parent,
                       &parent->p->klist_children); //把設備添加到父設備的children列表中


    if (dev->class) {  //如果改dev有所屬類,則將dev的添加到類的設備列表裏面
        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) //執行改dev的class_intf->add_dev(),這個有個好處,就是隻有設備匹配註冊成功了,才進行其它的註冊工作(如字符設備的註冊,生成/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);
name_error:
    kfree(dev->p);
    dev->p = NULL;
    goto done;
}



這部分是device_register的主要工作,我們這裏就不在詳細分析了,相信有了driver_register分析的基礎,這部分分析工作應該會變得很輕鬆~
發佈了13 篇原創文章 · 獲贊 16 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章