2010年(15)
分类: LINUX
2010-01-18 11:33:56
Author: 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;
为其内嵌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等。
chinaunix网友2010-07-13 17:15:19
写得很好!!谢谢 devices_init中的解释“对应sysfs中的/sys/devices、/sys/devices/block、/sys/devices/char。” 应该是对应sysfs中的/sys/devices、/sys/dev/block、/sys/dev/char。” 吧