在字符設備驅動開發的入門教程中,最常見的就是用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”:
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; 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);
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; 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);
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; 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);