- 论坛徽章:
- 0
|
Author: Eric Fang
Date: 2010-01-12
-----------------------------------------------------------------
本站分析linux内核源码,版本号为2.6.32.3
转载请注明出处:
http://ericfang.cublog.cn/
-----------------------------------------------------------------
在清楚了kobject之后,就可以继续分析device、driver、bus了,这三者是设备驱动程序的基本数据结构。
![]()
我们可以这样理解,内核用device来表示各种设备,然后用driver来表示它的驱动,而设备有很多种,也属于相同类型或不同类型,而其对应的驱动可能同时也是另外一个设备的驱动,为了管理这些设备和驱动,就引入了总线bus_type,总线上有两个集合(也可以理解为两条链,如上图中的bus),分别用来存放该总线类型的设备和驱动,当添加一个设备时就将设备添加到总线的设备集合(图中操作2),同时可能会到驱动集合去匹配适合它的驱动(图中操作3,在此之前device和driver没有挂钩),如何找到了就会将它们联系起来(图中操作6)。同样注册一个驱动,就会把它挂到相应总线类型的驱动集合里(图中操作1),同时去设备集合中找出它锁驱动的设备(图中操作4,在此之前device和driver没有挂钩),如果找到就把设备链接到它支持的设备链表上(图中操作6)。
下面我们进入到代码中去:
struct bus_type结构定义如下:
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct bus_type_private *p;
};
较之先前一些内核版本,bus_type把部分私有字段封装到bus_type_private类型结构里:
struct bus_type_private {
struct kset subsys;
struct kset *drivers_kset;
struct kset *devices_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
字段klist_devices和klist_drivers分别表示挂在bus_type上的驱动和设备链表,bus_type的其他字段和函数指针将在分析过程中说明。
首先我们要为设备和驱动注册一个总线类型:
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->bus = bus;
bus->p = priv;
分配私有区域的内存空间,并将其关联
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
初始化回调函数
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);
if (retval)
goto out;
这里我们看到了subsys用来表示它的文件系统,可以回想上一节kset的注册。
这个bus_kset是系统启动是创建的,系统init进程kernel_init()中调用do_basic_setup(),其中调用driver_init(),其中调用的buses_init(),如下
int __init buses_init(void)
{
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
if (!bus_kset)
return -ENOMEM;
return 0;
}
从而知道创建的文件系统目录在/sys/bus下。
static struct kset_uevent_ops bus_uevent_ops = {
.filter = bus_uevent_filter,
};
static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
struct kobj_type *ktype = get_ktype(kobj);
if (ktype == &bus_ktype)
return 1;
return 0;
}
继续bus_register()中的代码:
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
bus_create_file()如下:
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr)
{
int error;
if (bus_get(bus)) {
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
bus_put(bus);
} else
error = -EINVAL;
return error;
}
用bus_attr_uevent创建了bus->p->subsys.kobj的属性文件,由上面的赋值知道其读写操作在bus_ktype的sysfs_ops,其定义如下:
static struct kobj_type bus_ktype = {
.sysfs_ops = &bus_sysfs_ops,
};
static struct sysfs_ops bus_sysfs_ops = {
.show = bus_attr_show,
.store = bus_attr_store,
};
static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->show)
ret = bus_attr->show(bus_priv->bus, buf);
return ret;
}
static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct bus_attribute *bus_attr = to_bus_attr(attr);
struct bus_type_private *bus_priv = to_bus(kobj);
ssize_t ret = 0;
if (bus_attr->store)
ret = bus_attr->store(bus_priv->bus, buf, count);
return ret;
}
由上面的程序可以看出文件的读写操作最终会回到struct bus_attribute &bus_attr_uevent的show和store方法。
bus_attr_uevent是通过宏定义的:
#define __ATTR(_name,_mode,_show,_store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
#define BUS_ATTR(_name, _mode, _show, _store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
其show方法为NULL,说明不可读。
static ssize_t bus_uevent_store(struct bus_type *bus,
const char *buf, size_t count)
{
enum kobject_action action;
if (kobject_action_type(buf, count, &action) == 0)
kobject_uevent(&bus->p->subsys.kobj, action);
return count;
}
在用户空间可以控制事件的发生,如echo add > event将产生一个add的事件。
接着继续bus_register()中的代码:
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
创建两个kset,其内嵌object的parent都指向priv->subsys.kobj,说明其文件系统在bus所在目录下。由上回分析中知道kset_create_and_add()时其内嵌kobj的ktype指向kset_ktype,而这里没有输入参数的uevent_ops为NULL,则会以priv->subsys.kobj->kset->uevent_ops来产生事件,我们上面分析中知道这个uevent_ops为bus_uevent_ops,其filter会比较kobj的ktype是不是&bus_ktype,而这里是&kset_ktype,所以这里是忽略了事件。
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
初始化总线上设备和驱动的链表。
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
add_probe_files()函数如下:
static int add_probe_files(struct bus_type *bus)
{
int retval;
retval = bus_create_file(bus, &bus_attr_drivers_probe);
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
if (retval)
bus_remove_file(bus, &bus_attr_drivers_probe);
out:
return retval;
}
同上面创建bus_attr_uevent属性一样创建bus_attr_drivers_probe和bus_attr_drivers_autoprobe属性文件。粘出属性的代码:
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
show_drivers_autoprobe, store_drivers_autoprobe);
bus_attr_drivers_autoprobe的show指向NULL,说明其改文件不可写。
static ssize_t store_drivers_probe(struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
dev = bus_find_device_by_name(bus, NULL, buf);
if (!dev)
return -ENODEV;
if (bus_rescan_devices_helper(dev, NULL) != 0)
return -EINVAL;
return count;
}
将用户输(在用户空间)和的设备名称对应的设备与驱动匹配一次。
static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
}
在用户空间可以打印drivers_autoprobe的值,如cat drivers_autoprobe
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
const char *buf, size_t count)
{
if (buf[0] == '0')
bus->p->drivers_autoprobe = 0;
else
bus->p->drivers_autoprobe = 1;
return count;
}
在用户空间可以改变drivers_autoprobe的值,如echo 1 > drivers_autoprobe
继续分析bus_register()中的代码:
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
bus_add_attrs()如下:
static int bus_add_attrs(struct bus_type *bus)
{
int error = 0;
int i;
if (bus->bus_attrs) {
for (i = 0; attr_name(bus->bus_attrs); i++) {
error = bus_create_file(bus, &bus->bus_attrs);
if (error)
goto err;
}
}
done:
return error;
err:
while (--i >= 0)
bus_remove_file(bus, &bus->bus_attrs);
goto done;
}
如果bus->bus_attrs存在,则同样为其创建属性文件。
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
kfree(bus->p);
out:
bus->p = NULL;
return retval;
}
bus_register()分析完了,总结一下,它注册了一个总线类型,创建对应的文件系统(包括目录和属性),初始化总线上的驱动和设备,这样我们就可以通过内核提供的函数往总线上注册设备和驱动了。
在往总线注册注册设备前要先创建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/block、/sys/block。
接着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
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内嵌的object。
待续。。。
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/92745/showart_2146768.html |
|