标签:封装 sys 加载驱动 des stat 字符 drive release void
在字符设备驱动开发的入门教程中,最常见的就是用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()函数,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);
linux driver ------ 字符设备驱动之“ 创建设备节点流程 ”
标签:封装 sys 加载驱动 des stat 字符 drive release void
原文地址:https://www.cnblogs.com/god-of-death/p/10216103.html