Linux中device_create、device_add、device_register的区别

发布于:2024-10-15 ⋅ 阅读:(77) ⋅ 点赞:(0)

目录

前言:

一、源码分析

1、device_create()

2、device_register

3、device_add

二、device_create()、device_register()、device_add()使用中的区别

1、device_create()

2、device_register()

3、device_add()


前言:


        Linux设备驱动开发中,经常会用到device_create()函数创建设备节点,除了device_create()内核源码中也经常用device_register()和device_add()创建设备节点。

        本文主要讲解device_create()、device_register()、deivce_add()的区别。

一、源码分析


1、device_create()


device_create函数是我们写设备驱动经常用到的函数,它的功能是创建一个设备,并注册它到sysfs中,源码如下:

/* 源码路径:drivers/base/core.c */
struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)
{
	va_list vargs;
	struct device *dev;

	va_start(vargs, fmt);
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
	va_end(vargs);
	return dev;
}

device_create() 最终调用 device_create_groups_vargs()函数,device_create_groups_vargs功能是创建一个device并且将它注册到sysfs中

device_create()
--->device_create_vargs()
--->--->device_create_groups_vargs()

device_create_groups_vargs()函数源码:

static __printf(6, 0) struct device *
device_create_groups_vargs(struct class *class, struct device *parent,
			   dev_t devt, void *drvdata,
			   const struct attribute_group **groups,
			   const char *fmt, va_list args)
{
	struct device *dev = NULL;
	int retval = -ENODEV;

	if (class == NULL || IS_ERR(class))
		goto error;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
	if (!dev) {
		retval = -ENOMEM;
		goto error;
	}

	device_initialize(dev);    //初始化device
	dev->devt = devt;
	dev->class = class;
	dev->parent = parent;
	dev->groups = groups;
	dev->release = device_create_release;
	dev_set_drvdata(dev, drvdata);

	retval = kobject_set_name_vargs(&dev->kobj, fmt, args);    //设置device节点名字
	if (retval)
		goto error;

	retval = device_add(dev);
	if (retval)
		goto error;

	return dev;

error:
	put_device(dev);
	return ERR_PTR(retval);
}

可以看到,device_create()是device_add()的封装,最终还是调用 device_add()

2、device_register


/* drivers/base/core.c */
int device_register(struct device *dev)
{
	device_initialize(dev);    //初始化device
	return device_add(dev);
}

device_register()也是device_add()的封装。

3、device_add


device_add()会在/sys目录对应设备目录下创建uevent属性节点,应用层的udev则会根据uevent来创建/dev目录下的设备节点,这里关于udev的部分不再赘述

/* drivers/base/core.c */
int device_add(struct device *dev)
{
	struct device *parent;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;
	struct kobject *glue_dir = NULL;

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

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

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

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

	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (IS_ERR(kobj)) {
		error = PTR_ERR(kobj);
		goto parent_error;
	}
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
		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) {
		glue_dir = get_glue_dir(dev);
		goto Error;
	}

	/* notify platform of device entry */
	error = device_platform_notify(dev, KOBJ_ADD);
	if (error)
		goto platform_error;

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

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

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

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

		devtmpfs_create_node(dev);
	}

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_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);

	/*
	 * Check if any of the other devices (consumers) have been waiting for
	 * this device (supplier) to be added so that they can create a device
	 * link to it.
	 *
	 * This needs to happen after device_pm_add() because device_link_add()
	 * requires the supplier be registered before it's called.
	 *
	 * But this also needs to happen before bus_probe_device() to make sure
	 * waiting consumers can link to it before the driver is bound to the
	 * device and the driver sync_state callback is called for this device.
	 */
	if (dev->fwnode && !dev->fwnode->dev) {
		dev->fwnode->dev = dev;
		fw_devlink_link_device(dev);
	}

	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->mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->p->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;
 SysEntryError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &dev_attr_dev);
 DevAttrError:
	device_pm_remove(dev);
	dpm_sysfs_remove(dev);
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	device_remove_file(dev, &dev_attr_uevent);
 attrError:
	device_platform_notify(dev, KOBJ_REMOVE);
platform_error:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	glue_dir = get_glue_dir(dev);
	kobject_del(&dev->kobj);
 Error:
	cleanup_glue_dir(dev, glue_dir);
parent_error:
	put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}

二、device_create()、device_register()、device_add()使用中的区别


分别用device_create()、device_register()、device_add()来创建设备节点“/dev/led”,实现相同的功能能直观看到使用它们的区别。

1、device_create()


static class *led_class;

static int __init led_init(void)
{
    int ret;
    dev_t devno;
    struct cdev *cdev;
    struct dev *dev;

    /* 注册设备号 */
    ret = alloc_chrdev_region(&devno, 0, 1, "led");
    if (ret < 0) 
        return ret;

    /* 分配、初始化、注册cdev*/
    cdev = cdev_alloc();
    if (IS_ERR(cdev)) {
        ret = PTR_ERR(cdev);
        goto out_unregister_devno;
    }
    cdev_init(&cdev, &led_fops);
    cdev.owner = THIS_MODULE;
    ret = cdev_add(&cdev, devno, 1);    
    if (ret) 
        goto out_free_cdev;

    /* 创建设备类 */
    led_class = class_create(THIS_MODULE, "led_class");
    if (IS_ERR(led_class)) {
        ret = PTR_ERR(led_class);
        goto out_unregister_cdev;
    } 

    /* 创建设备节点 */
    dev = device_create(led_class, NULL, devno, NULL, "led");
    if (IS_ERR(dev)) {
        ret = PTR_ERR(dev);
        goto out_del_class;
    }

    return 0;
}

module_init(led_init);

2、device_register()


static class *led_class;

static int __init led_init(void)
{
    ......

     /* 注册设备号 */
    ......
    /* 分配、初始化、注册cdev*/
    ......
    /* 创建设备类 */
    ......

    /* 创建设备节点 */
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev) {
        ret = -ENOMEM;
        goto out_del_class;
    }

    dev->class = led_class;         // 关联设备类
    dev->parent = NULL;             
    dev->devt = devno;              // 关联设备号
    dev_set_drvdata(dev, NULL);     
    dev_set_name(dev, "led");       // 设置节点名字
    dev->release = device_create_release;

    ret = device_register(dev);
    if (ret) 
        goto out_put_dev;

    return 0;
}

module_init(led_init);

3、device_add()


static class *led_class;

static int __init led_init(void)
{
    ......

     /* 注册设备号 */
    ......
    /* 分配、初始化、注册cdev*/
    ......
    /* 创建设备类 */
    ......

    /* 创建设备节点 */
    dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    if (!dev) {
        ret = -ENOMEM;
        goto out_del_class;
    }

    dev->class = led_class;         // 关联设备类
    dev->parent = NULL;             
    dev->devt = devno;              // 关联设备号
    dev_set_drvdata(dev, NULL);     
    dev_set_name(dev, "led");       // 设置节点名字
    dev->release = device_create_release;

    device_initialize(dev);
    ret = device_add(dev);
    if (ret) 
        goto out_put_dev;

    return 0;
}

module_init(led_init);

 


最后看下 dev_set_name 函数源码,它调用kobject_set_name_vargs去设置device节点名字

/* drivers/base/core.c */
int dev_set_name(struct device *dev, const char *fmt, ...)
{
	va_list vargs;
	int err;

	va_start(vargs, fmt);
	err = kobject_set_name_vargs(&dev->kobj, fmt, vargs);
	va_end(vargs);
	return err;
}

 


网站公告

今日签到

点亮在社区的每一天
去签到