在前面学习了 kobject和 kset之后,就迫不及待的想开始“研究”设备模型了。经过这几天的学习,感觉受益匪浅。所以就将自己的理解整理了下来
想要完成一个设备的驱动,就要涉及三部分: Bus, device, driver。当然这些“新”节点都是最终继承于kobject。
一.Bus
这里先整理一下BUS,总线负责在设备与驱动间建立连接,包括 I2C, PCI, 串口,platform等。其中platform是虚拟总线。
1.1 结构体
信息结构体是 bus_type.
struct bus_type {
const char *name; //the name of bus
struct bus_attribute *bus_attrs;
//attribute for bus, contain attribute file and some operate function.
// this is a interface between kernel space and user space.
struct device_attribute *dev_attrs; //attribute for device,
struct driver_attribute *drv_attrs; //attribute for deriver
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 (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct dev_pm_ops *pm; //power manage
struct bus_type_private *p;
//private data for bus. In fact, it is core of this structure
};
在其中 bus_attrs, dev_attrs 和 drv_attrs记录了该总线的一些属性信息,而最重要的被用来构建该总线的逻辑结构的信息都记录在了bus_type_private中。对应这个总线私有数据结构体的解析如下。
struct bus_type_private {
struct kset subsys;
//there are two points:
//1).this is a set. It is contain some devices and derivers about this bus.
//2). it's parent is @bus_kset, which is the root of all other bus.@bus_kset have many subset, this is just one of them.
//
struct kset *drivers_kset;
//all drivers about this bus will belong to this set.
struct kset *devices_kset;
//all devices of this bus will belong to this set.
struct klist klist_devices;
struct klist klist_drivers;
//they are two lists , for mount all corresponding nodes.
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
//is this bus automaticly run when a new devices arrvied.
//sometime, we can see some attribute files in user space.(for example:@drivers_autoprobe).
//it is interface that kernel leave user to modify this argument.
struct bus_type *bus;
//just a port for return this bus.
};
其中的klist_devices, klist_drivers 链表会用来挂载该总线的设备与驱动。当需要找东西的时候就会去俩面翻。而上面的两个kset 分别是它们所属的集合。不同的集合对应于不同的操作特性。这是一种很给力的组织结构。就拿这里来说,我们用kobject来组织了一个二维链表(或其他什么数据结构),每个kobject在这个链表中充当了一个节点。但又想让其中指定的一些kobject节点具有一些属性。kset相当于kobject的属性。它包含了进行事件通知需要的一些数据信息。每当kobject有需要时,就会去找到自己所属的kset,或者上级kobject的kset来用。
1.2 重要函数分析
对于总线的注册需要使用到如下函数,通过分析它的行为对于理解bus_type的逻辑结构是很有帮助。
int bus_register(struct bus_type *bus)
{
int retval;
struct bus_type_private *priv;
//alloc a private data package for @bus. It is the core of this structure,
//include device list, deriver list and so on.
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);
// before we keep on, what we should to know is that this bus is one of members of the great building,
//so it must be inherit form @kobject.
//and @(priv->subsys.kobj) is it's kobject.
retval=kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
priv->subsys.kobj.kset=bus_kset; //1).@bus_kset is the root of all buses. 2). this structure is the type of bus.
priv->subsys.kobj.ktype=&bus_ktype; //corresponding operation function for bus
priv->drivers_autoprobe=1; //automaticly probe when new device arrived.
retval=kset_register(&priv->subsys);
if (retval)
goto out;
retval=bus_create_file(bus, &bus_attr_uevent); //create attribute file for bus,it is a interface between user space and kernel space.
if (retval)
goto bus_uevent_fail;
//给该总线创建一个设备子集,it is the set of all devices about this bus.
//在文件系统中的表现就是在该总线的目录下多了一个名字叫"devices"的子目录
//还需要提醒的一点就是:设备模型中的层次结构关系都是由kobject对象来指定,所以凡是属于这个设备模型的节点必须要继承kobject.
priv->devices_kset=kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval=-ENOMEM;
goto bus_devices_fail;
}
//create a deriver set for this bus
priv->drivers_kset=kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval=-ENOMEM;
goto bus_drivers_fail;
}
//thoes two list is used to mount some nodes. device-node or deriver-node.
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
//create attribute file for this structure
retval=add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
retval=bus_add_attrs(bus); //create attribute file for @bus_attr
if (retval)
goto bus_attrs_fail;
pr_debug("bus: '%s': registeredn", 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;
}
在函数体中已经对行为进行了较详细的分析。
1.3 device_bind_driver
那么device到底又是怎么和一个driver进行绑定的呢?让它们在需要时能找到彼此
//bind a driver to one device.
int device_bind_driver(struct device *dev)
{
int ret;
ret=driver_sysfs_add(dev);
if (!ret)
driver_bound(dev);
return ret;
}
//这个函数是在设备已经绑定驱动之后使用,
static void driver_bound(struct device *dev)
{
if (klist_node_attached(&dev->p->knode_driver)) {
printk(KERN_WARNING "%s: device %s already boundn",
__func__, kobject_name(&dev->kobj));
return;
}
pr_debug("driver: '%s': %s: bound to device '%s'n", dev_name(dev),
__func__, dev->driver->name);
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev);
//把设备节点加入驱动的节点链表中
//?既然设备已经能找到自己的驱动了,为什么还需要加入驱动的链表
//?一个驱动可能支持一组设备,对于那些逻辑操作相同的设备,所有驱动有一个设备链表
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
}
二. 设备信息
2.1 信息结构体
在总线的其中一端挂载的是设备。其用于记录数据和操作的结构体是device.
struct device {
struct device *parent; //parent device
struct device_private *p; //private data for device, 包含了其拓扑结构信息
struct kobject kobj;
//@device is inherit from @kobject. so it is belong to @devices_kset, which is created by kernel for devices and contain of some operation functions for devices.
//devices_kset
const char *init_name;
struct device_type *type;
struct semaphore sem;
struct bus_type *bus;
struct device_driver *driver;
void *driver_data;
void *platform_data;
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node;
#endif
u64 *dma_mask;
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
struct dev_archdata archdata;
dev_t devt;
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
struct attribute_group **groups;
void (*release)(struct device *dev);
};
而这个节点的拓扑结构信息则保存在 device_private 中
struct device_private {
struct klist klist_children;
//klist containing all children of this device
struct klist_node knode_parent;
//node in sibling list
struct klist_node knode_driver;
//node in driver list, @device is mount on the list of driver by this member.
//In linux system, one device have only one driver.
//But it is not same as to driver, one driver may have a lot of devices, if only those device have a same logical operation.
struct klist_node knode_bus;
//apparently, this device is need to mount on the list of bus, too.
struct device *device;
//point to this device
};
2.2 重要函数
设备的注册函数为 device_register( ), 其会建立设备的拓扑关系,并将设备添加到对应总线的拓扑列表中。
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}