热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux设备驱动深入解析:设备模型与devicedriverbus机制(下篇)

在上篇文章的基础上,本文将继续探讨Linux设备驱动中的设备模型与`devicedriverbus`机制。在将设备注册到总线之前,需要先创建`device`对象。可以通过静态定义`device`结构体变量,并调用`device_register`函数来完成这一过程。此外,文章还将详细解析设备模型的内部工作机制,以及`devicedriverbus`机制如何实现设备与驱动的自动匹配和管理。

接上一篇文章,在往总线注册注册设备前要先创建device,我们可以静态的定义device结构变量,然后调用device_register()将其注册,或者通过内核提供的device_create()接口函数创建和注册device。先看看device的数据结构定义:

struct device {

struct device*parent;

struct device_private*p;

struct kobject kobj;

const char*init_name; /* initial name of the device */

struct device_type*type;

struct semaphoresem;/* semaphore to synchronize calls to

* its driver.

*/

struct bus_type*bus;/* type of bus device is on */

struct device_driver *driver;/* which driver has allocated this

device */

void*platform_data;/* Platform specific data, device

core doesn't touch it */

struct dev_pm_infopower;

#ifdef CONFIG_NUMA

intnuma_node;/* NUMA node this device is close to */

#endif

u64*dma_mask;/* dma mask (if dma'able device) */

u64coherent_dma_mask;/* Like dma_mask, but for

alloc_coherent mappings as

not all hardware supports

64 bit addresses for consistent

allocations such descriptors. */

struct device_dma_parameters *dma_parms;

struct list_headdma_pools;/* dma pools (if dma'ble) */

struct dma_coherent_mem*dma_mem; /* internal for coherent mem

override */

/* arch specific additions */

struct dev_archdataarchdata;

dev_tdevt;/* dev_t, creates the sysfs "dev" */

spinlock_tdevres_lock;

struct list_headdevres_head;

struct klist_nodeknode_class;

struct class*class;

const struct attribute_group **groups;/* optional groups */

void(*release)(struct device *dev);

};

和总线类型一样,device也把部分私有数据封装到device_private结构中:

struct device_private {

struct klist klist_children;

struct klist_node knode_parent;

struct klist_node knode_driver;

struct klist_node knode_bus;

void *driver_data;

struct device *device;

};

创建device的函数device_create()如下:

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_vargs():

struct device *device_create_vargs(struct class *class, struct device *parent,

dev_t devt, void *drvdata, 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;

}

dev->devt = devt;

dev->class = class;

dev->parent = parent;

dev->release = device_create_release;

dev_set_drvdata(dev, drvdata);

retval = kobject_set_name_vargs(&dev->kobj, fmt, args);

if (retval)

goto error;

retval = device_register(dev);

if (retval)

goto error;

return dev;

error:

put_device(dev);

return ERR_PTR(retval);

}

该函数为创建的device分配内存空间,根据输入参数和一些默认值对其初始化,然后调用device_register将其注册到相应的总线上。

int device_register(struct device *dev)

{

device_initialize(dev);

return device_add(dev);

}

device_initialize()先对dev初始化:

void device_initialize(struct device *dev)

{

dev-> kobj.kset = devices_kset;

为其内嵌kobjkset赋值,类似bus_kset,devices_kset也是系统启动是创建的:

int __init devices_init(void)

{

devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

if (!devices_kset)

return -ENOMEM;

dev_kobj = kobject_create_and_add("dev", NULL);

if (!dev_kobj)

goto dev_kobj_err;

sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);

if (!sysfs_dev_block_kobj)

goto block_kobj_err;

sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);

if (!sysfs_dev_char_kobj)

goto char_kobj_err;

return 0;

char_kobj_err:

kobject_put(sysfs_dev_block_kobj);

block_kobj_err:

kobject_put(dev_kobj);

dev_kobj_err:

kset_unregister(devices_kset);

return -ENOMEM;

}

对应sysfs中的/sys/devices/sys/devices/block/sys/devices/char

接着device_initialize()中的代码:

kobject_init(&dev-> kobj, &device_ktype);

初始化dev内嵌的kobj

INIT_LIST_HEAD(&dev->dma_pools);

init_MUTEX(&dev->sem);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

初始化一些其它的字段。

device_init_wakeup(dev, 0);

device_pm_init(dev);

电源管理的一些初始化。

set_dev_node(dev, -1);

设置numa

}

初始化dev后调用device_add()往相应总线上添加设备。分段分析这个函数:

int device_add(struct device *dev)

{

struct device *parent = NULL;

struct class_interface *class_intf;

int error = -EINVAL;

dev = get_device(dev);

if (!dev)

goto done;

if (!dev->p) {

error = device_private_init(dev);

if (error)

goto done;

}

增加dev的计数,初始化其私有结构,实际上面初始化调用的dev_set_drvdata(dev, drvdata)里边已经包含了device_private_init(),但如果是直接调用device_register()而不是device_create()时,必须在这里再初始化一次,显然第一个调用device_private_init()是不必要id。所以在这里才分析device_private_init(),函数如下:

int device_private_init(struct device *dev)

{

dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL);

if (!dev->p)

return -ENOMEM;

为其分配内存。

dev->p->device = dev;

关联到dev

klist_init(&dev->p-> klist_children, klist_children_get,

klist_children_put);

初始化klist_children

return 0;

}

接着device_add()函数。

/*

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

}

if (!dev_name(dev))

goto name_error;

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

dev->init_name设置dev->kobj.name,而后dev->init_name指向NULL,如果dev->kobj.name则跳到name_error

parent = get_device(dev->parent);

增加dev->parent-> kobj的计数。

setup_parent(dev, parent);

看下这个函数:

static void setup_parent(struct device *dev, struct device *parent)

{

struct kobject *kobj;

kobj = get_device_parent(dev, parent);

if (kobj)

dev->kobj.parent = kobj;

}

static struct kobject *get_device_parent(struct device *dev,

struct device *parent)

{

int retval;

if (dev->class) {

struct kobject *kobj = NULL;

struct kobject *parent_kobj;

struct kobject *k;

/*

* If we have no parent, we live in "virtual".

* Class-devices with a non class-device as parent, live

* in a "glue" directory to prevent namespace collisions.

*/

if (parent == NULL)

parent_kobj = virtual_device_parent(dev);

else if (parent->class)

return &parent->kobj;

else

parent_kobj = &parent->kobj;

/* find our class-directory at the parent and reference it */

spin_lock(&dev->class->p->class_dirs.list_lock);

list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)

if (k->parent == parent_kobj) {

kobj = kobject_get(k);

break;

}

spin_unlock(&dev->class->p->class_dirs.list_lock);

if (kobj)

return kobj;

/* or create a new class-directory at the parent device */

k = kobject_create();

if (!k)

return NULL;

k->kset = &dev->class->p->class_dirs;

retval = kobject_add(k, parent_kobj, "%s", dev->class->name);

if (retval <0) {

kobject_put(k);

return NULL;

}

/* do not emit an uevent for this simple "glue" directory */

return k;

}

if (parent)

return &parent->kobj;

return NULL;

}

dev-> kobj.parent的设置如下&#xff1a;如果dev->parent&#xff0c;则将dev->parent->kobj赋给它&#xff0c;否则如果device_create()class参数不为为NULL时&#xff0c;则通过virtual_device_parent(dev)获得其parent&#xff0c;否则指向NULL,在调用kobject_add()时使其kset字段的kset内嵌的object

继续device_add()函数

/* use parent numa_node */

if (parent)

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 &#61; kobject_add(&dev->kobj, dev->kobj.parent, NULL);

if (error)

goto Error;

dev->kobj添加到系统中。

/* notify platform of device entry */

if (platform_notify)

platform_notify(dev);

如果定义了platform_notify&#xff08;&#xff09;函数&#xff0c;则调用它&#xff0c;在drivers/base/core.c中有&#xff1a;

int (*platform_notify)(struct device *dev) &#61; NULL;

说明默认将不会调用它。

error &#61; device_create_file(dev, &uevent_attr);

if (error)

goto attrError;

建立devuevent_attr属性文件&#xff0c;这里的uevent_attr定义如下&#xff1a;

static struct device_attribute uevent_attr &#61;

__ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent);

show_uevent()函数如下&#xff1a;

static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,

char *buf)

{

struct kobject *top_kobj;

struct kset *kset;

struct kobj_uevent_env *env &#61; NULL;

int i;

size_t count &#61; 0;

int retval;

/* search the kset, the device belongs to */

top_kobj &#61; &dev->kobj;

while (!top_kobj->kset && top_kobj->parent)

top_kobj &#61; top_kobj->parent;

if (!top_kobj->kset)

goto out;

kset &#61; top_kobj->kset;

if (!kset->uevent_ops || !kset->uevent_ops->uevent)

goto out;

/* respect filter */

if (kset->uevent_ops && kset->uevent_ops->filter)

if (!kset->uevent_ops->filter(kset, &dev->kobj))

goto out;

env &#61; kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

if (!env)

return -ENOMEM;

/* let the kset specific function add its keys */

retval &#61; kset->uevent_ops->uevent(kset, &dev->kobj, env);

if (retval)

goto out;

/* copy keys to file */

for (i &#61; 0; i envp_idx; i&#43;&#43;)

count &#43;&#61; sprintf(&buf[count], "%s\n", env->envp[i]);

out:

kfree(env);

return count;

}

改函数表明如果读devuevent则会显示dev-> kobj所属kset产生的环境变量。

store_uevent定义如下&#xff1a;

static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,

const char *buf, size_t count)

{

enum kobject_action action;

if (kobject_action_type(buf, count, &action) &#61;&#61; 0) {

kobject_uevent(&dev->kobj, action);

goto out;

}

dev_err(dev, "uevent: unsupported action-string; this will "

"be ignored in a future kernel version\n");

kobject_uevent(&dev->kobj, KOBJ_ADD);

out:

return count;

}

从程序可以看出对这个文件的作用写则会产生相应的事件。如果输入的字符串不合法&#xff0c;则就会产生一个add事件。

接着device_add()函数中的代码&#xff1a;

if (MAJOR(dev->devt)) {

error &#61; device_create_file(dev, & devt_attr);

if (error)

goto ueventattrError;

error &#61; device_create_sys_dev_entry(dev);

if (error)

goto devtattrError;

devtmpfs_create_node(dev);

}

如果dev->devt的煮设备号不为空&#xff0c;则创建devt_attr属性文件和连接文件&#xff0c;devt_attr定义如下

static struct device_attribute devt_attr &#61;

__ATTR(dev, S_IRUGO, show_dev, NULL);

该属性的store函数为NULL&#xff0c;说明为只读。读操作函数show_dev&#xff08;&#xff09;&#xff1a;

static ssize_t show_dev(struct device *dev, struct device_attribute *attr,

char *buf)

{

return print_dev_t(buf, dev->devt);

}

#define print_dev_t(buffer, dev)\

sprintf((buffer), "%u:%u\n", MAJOR(dev), MINOR(dev))

读操作为打印dev的设备号。

static int device_create_sys_dev_entry(struct device *dev)

{

struct kobject *kobj &#61; device_to_dev_kobj(dev);

int error &#61; 0;

char devt_str[15];

if (kobj) {

format_dev_t(devt_str, dev->devt);

error &#61; sysfs_create_link(kobj, &dev->kobj, devt_str);

}

return error;

}

static struct kobject *device_to_dev_kobj(struct device *dev)

{

struct kobject *kobj;

if (dev->class)

kobj &#61; dev->class->dev_kobj;

else

kobj &#61; sysfs_dev_char_kobj;

return kobj;

}

如果定义了dev->class&#xff0c;则在相应的目录下建立链接文件&#xff0c;否则默认在/sys/devices/char下建立链接文件。

接着device_add ()函数&#xff1a;

error &#61; device_add_class_symlinks(dev);

if (error)

goto SymlinkError;

error &#61; device_add_attrs(dev);

if (error)

goto AttrsError;

同样在class子系统下建立链接文件、class->dev_attrs属性文件、group等。

接下一篇文章。



推荐阅读
author-avatar
尕心疼TammyY
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有