Chinaunix首页 | 论坛 | 博客
  • 博客访问: 620569
  • 博文数量: 168
  • 博客积分: 1053
  • 博客等级: 少尉
  • 技术积分: 1187
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-30 17:20
个人简介

公众号【嵌入式er笔记】持续记录和分享C/C++、Linux、ARM、Android、IoT等技术相关知识,以及职场、生活经验和感悟。

文章分类

全部博文(168)

分类:

2013-01-14 10:25:33

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处: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分析的基础,这部分分析工作应该会变得很轻松:)



阅读(699) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~