@zifehng
2017-06-28T07:02:53.000000Z
字数 3306
阅读 2671
kernel device_create device_register device_add
本文基于linux 3.10.40,其他版本仅供参考
在字符设备驱动开发的入门教程中,最常见的就是用device_create()函数来创建设备节点了,但是在之后阅读内核源码的过程中却很少见device_create()的踪影了,取而代之的是device_register()与device_add(),将device_create()函数展开不难发现:其实device_create()只是device_register()的封装,而device_register()则是device_add()的封装。
struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...){......dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);......return dev;}
struct device *device_create_vargs(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt,va_list args){......dev->devt = devt;dev->class = class;dev->parent = parent;dev->release = device_create_release;dev_set_drvdata(dev, drvdata);......retval = device_register(dev);......}
int device_register(struct device *dev){device_initialize(dev);return device_add(dev);}
device_add()会在/sys目录对应设备目录下创建uevent属性节点,应用层的udev会根据uevent来创建/dev目录下的设备节点,这里关于udev的部分不再赘述,我们继续分析device_create()、device_register()、device_add()三个函数在实际运用中的区别。
以一个简单的led设备字符设备驱动为例,下面分别用device_create()、device_register()、device_add()三个函数来创建设备节点“/dev/led”:
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;out_del_class:class_destroy(c78x_class);out_unregister_cdev:cdev_del(cdev);out_free_cdev:kfree(cdev);out_unregister_devno:unregister_chrdev_region(devno, 1);return ret;}module_init(led_init);
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;out_put_dev:put_device(dev);kree(dev);out_del_class:class_destroy(c78x_class);out_unregister_cdev:cdev_del(cdev);out_free_cdev:kfree(cdev);out_unregister_devno:unregister_chrdev_region(devno, 1);return ret;}module_init(led_init);
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;out_put_dev:put_device(dev);kree(dev);out_del_class:class_destroy(c78x_class);out_unregister_cdev:cdev_del(cdev);out_free_cdev:kfree(cdev);out_unregister_devno:unregister_chrdev_region(devno, 1);return ret;}module_init(led_init);