接上一篇文章,在往總線註冊註冊設備前要先創建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;
爲其內嵌kobj的kset賦值,類似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的設置如下:如果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;
建立dev的uevent_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;
}
改函數表明如果讀dev的uevent則會顯示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等。
接下一篇文章。