总线
内核所支持的每一种总线类型都由一个bus_type描述。因为bus_type结构体中定义了struct subsystem subsys,所以总线的级别处于subsystem,即总线的父结构也一定是一个subsystem。同时bus_type结构体中也定义了struct kset drivers和struct kset devices,这两个kset的父结构都是bus_type的struct subsystem subsys。
struct bus_type {
/**
* 总线类型的名称。例如"pci"
*/
char * name;
/**
* 与总线类型相关的kobject子系统。这些子系统并不在sysfs的顶层。
* 一个总线包含两个kset,分别代表了总线的驱动程序和插入总线的所有设备。
*/
struct subsystem subsys;
/**
* 驱动程序的kobject集合
*/
struct kset drivers;
/**
* 设备的kobject集合
*/
struct kset devices;
/**
* 指向对象的指针,该对象包含总线属性和用于导出此属性到sysfs文件系统的方法
*/
struct bus_attribute * bus_attrs;
/**
* 指向对象的指针,该对象包含设备属性和用于导出此属性到sysfs文件系统的方法
*/
struct device_attribute * dev_attrs;
/**
* 指向对象的指针,该对象包含驱动程序属性和用于导出此属性到sysfs文件系统的方法
*/
struct driver_attribute * drv_attrs;
/**
* 检验给定的设备驱动程序是否支持特定设备的方法.
* 当一个总线上的新设备或者新驱动程序被添加时,会一次或多次调用这个函数。如果指定的驱动程序能够处理指定的设备,该函数返回非0值。
*/
int (*match)(struct device * dev, struct device_driver * drv);
/**
* 注册设备时调用的方法
* 在为用户空间产生热插拨事件前,这个方法允许总线添加环境变量。
*/
int (*hotplug) (struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size);
/**
* 保存硬件设备的上下文状态并改变设备供电状态的方法
*/
int (*suspend)(struct device * dev, pm_message_t state);
/**
* 改变供电状态和恢复硬件设备上下文的方法
*/
int (*resume)(struct device * dev);
};
注册总线
新注册的总线被注册到sysfs/bus中
int bus_register(struct bus_type * bus)
{
/*
注册总线的子系统,这里将所属总线子系统的目录创建在bus_subsys下面,也就是/sysfs/bus下面
*/
kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name);
subsys_set_kset(bus, bus_subsys);
(obj)->subsys.kset.kobj.kset = &(_subsys).kset
subsystem_register(&bus->subsys);
/*
注册该总线的设备kset,挂载到该总线上的所有设备都属于这个kset,并在该总线目录下创建devices目录
*/
kobject_set_name(&bus->devices.kobj, "devices");
bus->devices.subsys = &bus->subsys;
kset_register(&bus->devices);
/*
注册该总线的驱动kset,该总线的所有驱动都属于这个kset,并在该总线目录下创建drivers目录
*/
kobject_set_name(&bus->drivers.kobj, "drivers");
bus->drivers.subsys = &bus->subsys;
bus->drivers.ktype = &ktype_driver;
kset_register(&bus->drivers);
bus_add_attrs(bus);
驱动
设备驱动程序模型中的每个设备由device表示,device结构体中定义了struct kobject kobj;所以device在层级上属于kobject。
struct device {
/**
* 指向兄弟设备的指针
*/
struct list_head node; /* node in sibling list */
/**
* 指向连于同一类型总路线上的设备链表的指针。
*/
struct list_head bus_list; /* node in bus's list */
/**
* 指向设备驱动程序链表的指针。
*/
struct list_head driver_list;
/**
* 子设备链表的首部
*/
struct list_head children;
/**
* 指向父设备的指针
* 父设备。即该设备所属的设备。
* 大多数情况下,一个父设备通常是某种总线或者是宿主控制器。
* 如果parent为NULL,表示该设备是顶层设备。
*/
struct device * parent;
/**
* 内嵌kobject。
* 通过该字段将设备连接到结构体系中。
* 作为通用准则,device->kobj->parent和device->parent->kobj是相同的。
*/
struct kobject kobj;
/**
* 连接到总线上设备的位置
* 在总线上唯一标识该设备的字符串。如PCI设备使用了标准PCI ID格式,它包括:域编号、总线编号、设备编号和功能编号。
*/
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
/**
* 指向所连接总线的指针
* 标识该设备连接在何种类型的总线上。
*/
struct bus_type * bus; /* type of bus device is on */
/**
* 指向控制设备驱动程序的指针
*/
struct device_driver *driver; /* which driver has allocated this
device */
/**
* 指向驱动程序私有数据的指针
*/
void *driver_data; /* data private to the driver */
/**
* 指向遗留设备驱动程序私有数据的指针
*/
void *platform_data; /* Platform specific data (e.g. ACPI,
BIOS data relevant to device) */
/**
* 电源管理信息
*/
struct dev_pm_info power;
/**
* 卸载设备驱动程序时电源进入的状态
*/
u32 detach_state; /* State to enter when device is
detached from its driver. */
/**
* 指向设备的DMA屏蔽字的指针
*/
u64 *dma_mask; /* dma mask (if dma'able device) */
/**
* 设备的一致性DMA的屏蔽字
*/
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. */
/**
* DMA缓冲池链表的首部
*/
struct list_head dma_pools; /* dma pools (if dma'ble) */
/**
* 指向设备所使用的一致性DMA存储器描述符的指针
*/
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/**
* 释放回调函数
*/
void (*release)(struct device * dev);
};
设备注册
/**
- 往设备驱动程序模型中插入一个新的设备驱动。
- 并自动的在sysfs文件系统下为其创建一个新的目录
*/
int device_register(struct device *dev)
{
device_initialize(dev);
// 设置该设备的父kset为devices_subsys.kset
kobj_set_kset_s(dev, devices_subsys);
(obj)->kobj.kset = &(subsys).kset
kobject_init(&dev->kobj);
INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children);
INIT_LIST_HEAD(&dev->driver_list);
INIT_LIST_HEAD(&dev->bus_list);
INIT_LIST_HEAD(&dev->dma_pools);
return device_add(dev);
dev = get_device(dev);
parent = get_device(dev->parent);
kobject_set_name(&dev->kobj, "%s", dev->bus_id);
// 如果父设备存在,则对应kobj就指向父设备的kobj
if (parent)
dev->kobj.parent = &parent->kobj;
// 创建设备kobj对应的目录,如果父设备存在,就在父设备目录下创建,否则就在devices_subsys对应目录(/sys/devices)下创建
kobject_add(&dev->kobj)
device_pm_add(dev)
bus_add_device(dev)
struct bus_type * bus = get_bus(dev->bus);
list_add_tail(&dev->bus_list, &dev->bus->devices.list);
device_attach(dev);
device_add_attrs(bus, dev);
// 在对应总线的devices目录下创建一个链接文件dev->bus_id,链接到dev->kobj
sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
if (parent)
list_add_tail(&dev->node, &parent->children);
if (platform_notify)
platform_notify(dev);
}
device_register主要内容如下
(1)设置该设备的父kset为devices_subsys.kset,这将使得该设备没有父设备时,会被注册到devices_subsys下。
(2)调用device_add将该设备添加到设备层次结构中,这会在devices_subsys目录下或者父设备目录下创建该设备目录。
(3)调用bus_add_device将设备和驱动绑定在一起。
device_attach(struct device * dev)
struct bus_type * bus = dev->bus;
// 如果驱动存在
if (dev->driver)
device_bind_driver(dev);
list_add_tail(&dev->driver_list, &dev->driver->devices);
// 在驱动对应的目录下创建一个名为 kobject_name(&dev->kobj)的链接文件,链接到dev->kobj目录下
sysfs_create_link(&dev->driver->kobj, &dev->kobj, kobject_name(&dev->kobj));
// 在dev->kobj目录下创建一个名为 driver的链接文件,链接到驱动目录下
sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
// 如果驱动不存在
if (bus->match)
//遍历设备所在总线上的驱动链表
list_for_each(entry, &bus->drivers.list)
struct device_driver * drv = to_drv(entry);
//调用总线的匹配函数或者驱动的探测函数
driver_probe_device(drv, dev);
if (drv->bus->match && !drv->bus->match(dev, drv))
return -ENODEV;
dev->driver = drv;
if (drv->probe)
drv->probe(dev);
device_bind_driver(dev);
device_attach目的就是把驱动和设备关联起来,主要内容如下:
(1)如果驱动已经存在,就调用device_bind_driver,该函数在驱动对应的目录下创建一个名为 kobject_name(&dev->kobj)的链接文件,链接到dev->kobj目录下,并在在dev->kobj目录下创建一个名为 driver的链接文件,链接到驱动目录下。
(2)如果没有驱动,则调用总线的匹配函数或者驱动的探测函数去寻找驱动,然后再调用device_bind_driver把驱动和设备关联起来。
驱动
驱动由device_driver结构体描述,因为其含有struct kobject kobj;,所以device_driver处于kobject级别
struct device_driver {
/**
* 驱动程序的名称
*/
char * name;
/**
* 指向总线描述符的指针。
*/
struct bus_type * bus;
/**
* 禁止卸载设备驱动程序的信号量。
*/
struct semaphore unload_sem;
/**
* 内嵌kobject
*/
struct kobject kobj;
/**
* 驱动程序所支持的所有设备组成的链表的首部。
*/
struct list_head devices;
/**
* 驱动程序所在模块(如果有的话)
*/
struct module * owner;
/**
* 探测设备的方法
*/
int (*probe) (struct device * dev);
/**
* 移走设备的方法(检测设备驱动程序是否可以控制该设备)
*/
int (*remove) (struct device * dev);
/**
* 设备断电时调用的方法。
*/
void (*shutdown) (struct device * dev);
/**
* 设备置于低功率状态时所调用的方法
*/
int (*suspend) (struct device * dev, u32 state, u32 level);
/**
* 设备恢复正常状态时所调用的方法
*/
int (*resume) (struct device * dev, u32 level);
};
注册驱动
int driver_register(struct device_driver * drv)
{
INIT_LIST_HEAD(&drv->devices);
init_MUTEX_LOCKED(&drv->unload_sem);
return bus_add_driver(drv);
struct bus_type * bus = get_bus(drv->bus);
// 注册driver到bus->drivers目录下
kobject_set_name(&drv->kobj, "%s", drv->name);
drv->kobj.kset = &bus->drivers;
kobject_register(&drv->kobj)
// 遍历总线上的设备,将驱动和设备绑定在一起
driver_attach(drv);
struct bus_type * bus = drv->bus;
list_for_each(entry, &bus->devices.list)
struct device * dev = container_of(entry, struct device, bus_list);
if (!dev->driver)
driver_probe_device(drv, dev);
module_add_driver(drv->owner, drv);
driver_add_attrs(bus, drv);
}