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

    技术2025-09-28  82

    uthor: Eric Fang 

    Date:     2010-01-18

    -----------------------------------------------------------------

    本站分析linux内核源码,版本号为2.6.32.3

    转载请注明出处:http://ericfang.cublog.cn/

    -----------------------------------------------------------------

    接上一篇文章,在往总线注册注册设备前要先创建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)