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 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等。

 接下一篇文章。        

發佈了18 篇原創文章 · 獲贊 2 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章