关闭

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

标签: linuxstructclassnulllistaction
429人阅读 评论(0) 收藏 举报
分类:

接上一篇文章,在往总线注册注册设备前要先创建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 semaphore    sem;       /* 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_info       power;

 

#ifdef CONFIG_NUMA

       int           numa_node;    /* NUMA node this device is close to */

#endif

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

       u64         coherent_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_head       dma_pools;     /* dma pools (if dma'ble) */

 

       struct dma_coherent_mem    *dma_mem; /* internal for coherent mem

                                        override */

       /* arch specific additions */

       struct dev_archdata       archdata;

 

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

 

       spinlock_t              devres_lock;

       struct list_head       devres_head;

 

       struct klist_node     knode_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等。

 接下一篇文章。        

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:74021次
    • 积分:959
    • 等级:
    • 排名:千里之外
    • 原创:18篇
    • 转载:33篇
    • 译文:0篇
    • 评论:5条
    文章分类
    最新评论