`
cloudtech
  • 浏览: 4603459 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

LINUX设备驱动之设备模型四--device&driver&bus(二)

 
阅读更多

接上一篇文章,在往总线注册注册设备前要先创建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_ksetdevices_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的设置如下:如果dev->parent,则将dev->parent->kobj赋给它,否则如果device_create()class参数不为为NULL时,则通过virtual_device_parent(dev)获得其parent,否则指向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 = 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()函数,则调用它,在drivers/base/core.c中有:

int (*platform_notify)(struct device *dev) = NULL;

说明默认将不会调用它。

error = device_create_file(dev, &uevent_attr);

if (error)

goto attrError;

建立devuevent_attr属性文件,这里的uevent_attr定义如下:

static struct device_attribute uevent_attr =

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

show_uevent()函数如下:

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 = NULL;

int i;

size_t count = 0;

int retval;

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

top_kobj = &dev->kobj;

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

top_kobj = top_kobj->parent;

if (!top_kobj->kset)

goto out;

kset = 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 = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

if (!env)

return -ENOMEM;

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

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

if (retval)

goto out;

/* copy keys to file */

for (i = 0; i < env->envp_idx; i++)

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

out:

kfree(env);

return count;

}

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

store_uevent定义如下:

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) == 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;

}

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

接着device_add()函数中的代码:

if (MAJOR(dev->devt)) {

error = device_create_file(dev, & devt_attr);

if (error)

goto ueventattrError;

error = device_create_sys_dev_entry(dev);

if (error)

goto devtattrError;

devtmpfs_create_node(dev);

}

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

static struct device_attribute devt_attr =

__ATTR(dev, S_IRUGO, show_dev, NULL);

该属性的store函数为NULL,说明为只读。读操作函数show_dev():

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 = device_to_dev_kobj(dev);

int error = 0;

char devt_str[15];

if (kobj) {

format_dev_t(devt_str, dev->devt);

error = 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 = dev->class->dev_kobj;

else

kobj = sysfs_dev_char_kobj;

return kobj;

}

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

接着device_add ()函数:

error = device_add_class_symlinks(dev);

if (error)

goto SymlinkError;

error = device_add_attrs(dev);

if (error)

goto AttrsError;

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

接下一篇文章。

分享到:
评论

相关推荐

    linux设备模型之bus,device,driver

    linux设备模型之bus,device,driver

    linux驱动 bus-device-driver模型 bus.7z

    linux驱动 bus-device-driver模型

    linux设备驱动模型--设备篇

    介绍linux设备驱动模型概念中的设备篇

    linux设备驱动模型

    linux设备驱动模型 1、kobject原理与实例分析 2、kset原理与实例分析 3、bus(总线)原理与实例分析 4、device(设备)原理与实例分析 5、driver(驱动)原理与实例分析

    Linux驱动程序开发-设备驱动模型

    Linux驱动程序开发-设备驱动模型 Linux2.6设备驱动模型的基本元素是Class、Bus、Device、Driver,下面我们分别介绍各个部分。

    linux设备模型.pdf

    接着, 第8, 9, 10, 11章描述了设备模型中最重要的4个概念: Bus, Class, Device, Driver. 第12章介绍了platform系统, 它是基于Bus, Class, Device, Driver抽象出来的一个更上层的东西, 理解了前面的内容之后, 再来看...

    Linux 设备模型

    因此,由于这个共性,内核在设备模型的基础上(device和device_driver),对这些设备进行了更进一步的封装,抽象出paltform bus、platform device和platform driver,以便驱动开发人员可以方便的开发这类设备的驱动...

    驱动模型代码

    linux3.16 bus device driver 驱动模型

    unix分析关于UNIX的一些浅析

    下图即为Linux 2.6中引入的设备驱动模型的结构图(只是个总体框架,并不是指这的platform总线,设备和驱动)。 总线上包括设备和驱动的集合,总线上所有设备组成双向循环链表,包含在platform_device...

    linux 2.6.36+ok6410 SPI子系统接口讨论

    linux下的设备模型包括几个主要的概念sysfs (dev是用户空间接口,根据sysfs下的class目录由mdev负责建立)bus总线,linux下的设备都是建立在总线上的,platform总线是一个虚拟的总线,所有的的片上设备基本上都接在...

    总线,设备,驱动关联

    Linux设备模型中三个很重要的概念就是总线,设备,驱动.即bus,device,driver,们只需要知道,drivers 和 devices 的存在,让struct bus_type与两个链表联系了起来,一个是 devices 的链表,一个是drivers 的链表,也就是说,...

Global site tag (gtag.js) - Google Analytics