Linux中device_create、device_add、device_register的区别

devtools/2024/10/21 18:14:44/

目录

前言:

一、源码分析

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);    //初始化devicedev->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);    //初始化devicereturn 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;
}

 


http://www.ppmy.cn/devtools/127629.html

相关文章

Maven与Gradle的区别

Maven与Gradle是两种流行的构建工具&#xff0c;广泛用于Java项目的管理和构建。以下是它们的对比&#xff0c;包括官网、Windows 11配置环境、在IDEA中的相同点和不同点&#xff0c;以及它们各自的优缺点。 官网 Maven官网: https://maven.apache.orgGradle官网: https://gr…

Docker基础操作

文章目录 一、安装Docker二、Docker镜像操作1.搜索镜像 docker search2.获取镜像 docker pull3.查看镜像信息 docker images4.获取镜像/容器 详细信息 docker inspect5.为本地的镜像添加新的标签 docker tag6.删除镜像 docker rmi7.将镜像保存成为本地文件 docker save -o8.将镜…

Redis优劣势分析

Redis优劣势分析 Redis是一个开源的、内存中的数据结构存储&#xff0c;用于数据库、缓存和消息代理。以下是Redis的优劣势分析&#xff1a; 优点&#xff1a; 高性能&#xff1a; Redis是内存数据库&#xff0c;读写速度非常快&#xff0c;适合需要高性能的场景。丰富的数据…

laravel .env环境变量原理

介绍 对于应用程序运行的环境来说&#xff0c;不同的环境有不同的配置通常是很有用的。Laravel 利用 Vance Lucas 的 PHP 库 DotEnv 使得此项功能的实现变得非常简单。当应用程序收到请求时&#xff0c;.env 文件中列出的所有变量将被加载到 PHP 的超级全局变量 $_ENV 中。 使…

2023年“网络建设与运维”广西省赛试题复盘

2023年“网络搭建与应用”省赛试题复盘 第一部分&#xff1a;网络搭建及安全部署项目 &#xff08;500分&#xff09; 一、竞赛内容分布 “网络搭建与应用”竞赛共分二个部分&#xff0c;其中&#xff1a; 第一部分&#xff1a;网络搭建及安全部署项目 第二部分&#xff1a;服…

SpringLDAP连接LDAPS证书报错解决办法(二)

一、前言 ​ 阅读此笔记之前需先了解之前的文章《SpringLDAP连接LDAPS证书报错解决办法》&#xff0c;之前文章中所阐述的不再一一重述。 二、现象描述 ​ 按照之前文章设置&#xff0c;在我所在的软件环境中有可能还会再出现SSL握手失败的异常&#xff0c;报错提示java.sec…

java导出带图形的word

先看效果图&#xff1a;方法都是一样的&#xff0c;所以数据只做了前两组 第一步需要准备模版&#xff1a; 新建一个word插入图表&#xff0c;选择想要的图表。 编辑图表&#xff1a;营业额表示数字&#xff0c;季度表示文字。其他的样式编辑可根据自己的需求更改&#xff0c;…

vscode 远程linux服务器 连接git

vscode 远程linux服务器 连接git 1. git 下载2. git 配置1&#xff09;github 设置2&#xff09;与github建立连接linux端&#xff1a;创建密钥github端&#xff1a;创建ssh key 3. 使用1&#xff09;初始化repository2&#xff09;commit 输入本次提交信息&#xff0c;提交到本…