本文导读:本文主要分析USB子系统的内核源代码(V2.6.34)实现.主要实现三个目标:1.充分理解设备模型以及相关的结构:bus, driver, device. 2.类似与其他子系统,本子系统是怎么在内核中执行。3设备驱动程序的编写,从而帮助理解驱动开发中的各种语法细节
Keywords: usb子系统,设备模型,驱动开发 writed by huangjl
1. 找地图开始分析
/kernel/driver/usb目录下三个文件readme,kconfig,makefile
/*************************readme.txt*******************************/
Here is a list of what each subdirectory here is, and what is contained in them.
core/ - This is for the core USB host code, including the
usbfs files and the hub class driver ("khubd").
host/ - This is for USB host controller drivers. This
includes UHCI, OHCI, EHCI, and others that might
be used with more specialized "embedded" systems.
gadget/ - This is for USB peripheral controller drivers and
the various gadget drivers which talk to them.
Individual USB driver directories. A new driver should be added to the
first subdirectory in the list below that it fits into.
image/ - This is for still image drivers, like scanners or
digital cameras.
../input/ - This is for any driver that uses the input subsystem,
like keyboard, mice, touchscreens, tablets, etc.
../media/ - This is for multimedia drivers, like video cameras,
radios, and any other drivers that talk to the v4l
subsystem.
../net/ - This is for network drivers.
serial/ - This is for USB to serial drivers.
storage/ - This is for USB mass-storage drivers.
class/ - This is for all USB device drivers that do not fit
into any of the above categories, and work for a range
of USB Class specified devices.
misc/ - This is for all USB device drivers that do not fit
into any of the above categories.
/*************************readme.txt*******************************/
从readme.txt文件中对该目录做了个全局的介绍。Core文件代码提供对USB主机代码提供支持。Host目录下对不同的USB主机控制器支持。Gadget则是对USB外围控制器驱动。由于USB支持不同类型的外围设备,这些设备通常被分为不同的逻辑连接,所以才有像image,input等目录。这些目录下是外围设备驱动程序。再来看Kconfig文件
/************************Kconfig*******************************/
config USB_ARCH_HAS_HCD
…..
config USB_ARCH_HAS_OHCI
….
config USB_ARCH_HAS_EHCI
….
config USB
/************************Kconfig*******************************/
/************************Makefile*******************************/
obj-$(CONFIG_USB) += core/
….
obj-$(CONFIG_PCI) += host/
……
obj-$(CONFIG_USB_STORAGE) += storage/
/************************Makefile*******************************/
从上面两个文件看出,USB子系统最主要的就是目录core,host,各类驱动程序目录。
2.USB子系统的分层设计
USB模块在内核层次结构
首先我们来看看USB驱动程序在内核系统中的层次如下:
VFS Layer |
Block Layer |
Net Layer |
char Layer |
TTY Layerv…etc…. |
USB Device Driver | ||||
USB Core | ||||
USB Host Controllers | ||||
Hardware |
对于设备驱动程序,从设备的角度来看
|
<<<driver
Kernel
<<<driver |
很容易看出,usb各层在代码目录体现得很具体
3.USB子系统是怎么被系统识别和运行的
/********************core.c***********************/
subsys_initcall(usb_init);
module_exit(usb_exit);
/*******************core.c***********************/
/******************include/linux/init.h*********************/
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
* This only exists for built-in code, not for modules.
*/
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
代码中给出的注释是built-in code,也就是内建代码。也就是内核的构成
该部分的功能是将某个函数放在区域里,该区域的内存分布如下:
subsys_initcall(usb_init)就是将usb_init函数放在.initcall4.init这段内存中,系统在启动的时候,会按照先后顺序全部调用这段内存中的全部函数指针,调用的方式如下:
extern initcall_t __initcall_start[], __initcall_end[], __early_initcall_end[];
static void __init do_initcalls(void)
{
initcall_t *fn;
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
下面是usb_init的执行路线:
为了分析驱动程序的模型,需要一个个函数追根到底分析。Usb_debugfs_init与USB文件系统的调试有关。与模型无关,暂时不分析。
3.1. bus_register:
bus_register(struct bus_type *bus)函数:该函数注册总线到系统,具体来说将总线注册到Kobject框架中,然后才能注册总线所属设备和驱动的子系统。
首先来看看bus_type结构:
/*****************************device.h*************************************/
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;
};
/**************************************************************************/
pm指向电源管理指针,设计者真是个节能减员的好孩子。
P指向总线私带的指针数据,这个指针只有驱动核心才能使用
/********************kernel/drivers.h/base.h*************************************/
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;
};
/**************************************************************************/
/****************kernel/include/linux/Kobject.h********************************/
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset; /*设置所属的kset集*/
struct kobj_type *ktype; /*设置的obj类型*/
struct sysfs_dirent *sd;
struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
/**************************************************************************/
如上图:总线注册要做的事情分为下几类:
1. 将bus_type_private与bus相关联
2. 初始化bus_type_private事件通知链表
3. 设置bus_type_private的内嵌object中的Kset,ktype
4. 创建该总线所属的子系统集
5. 创建属于该总线的设备和驱动链表
6. 创建该总线的属性文件,方便用户在sysfs中查看(根据配置不同,在用户空间来是否支持热拔插)
7. 增加引用计数
可以看出,总线的注册其实就是配置bus_type_private结构
3.2 . bus_register_notifier
这个函数在前面稍微有接触,这个应该比较熟悉。总之执行后结果如下:
3.3. usb_major_init
该函数主要对文件系统提供支持。 即提供驱动程序对sysfs文件系统的操作
3.4 usb_register
函数调用是usb_register(&usbfs_driver);
可以看出,该接口提供对usb文件系统的支持。方便用户以用户接口调用
先看看这个结构:
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf, const struct usb_device_id *id);
void (*disconnect) (struct usb_interface *intf);
int (*ioctl) (struct usb_interface *intf, unsigned int code,void *buf);
int (*suspend) (struct usb_interface *intf, pm_message_t message);
int (*resume) (struct usb_interface *intf);
int (*reset_resume)(struct usb_interface *intf);
int (*pre_reset)(struct usb_interface *intf);
int (*post_reset)(struct usb_interface *intf);
const struct usb_device_id *id_table;
struct usb_dynids dynids;
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
unsigned int soft_unbind:1;
};
#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
该结构是对interface的抽象,在core层识别接口驱动。
从上图可以看出,驱动的注册是很繁杂的过程,特别是跟object核心交互,在注册过程中驱动就开始寻找设备,这是自动搜索过程,从这些可以看出,总线有驱动和设备的两边,到最后驱动和设备的匹配最终要调用总线的match函数。如果支持设备的热拔插,需要提供设备的厂商,ID等信息,具体的实现见源代码。到此驱动模型可以说分析得差不多了,设备和驱动就是通过这样的思路关联起来的。
3.5. usb_devio_init
这个函数功能文件解释如下:
* This file implements the usbfs/x/y files, where
* x is the bus number and y the device number.
* It allows user space programs/"drivers" to communicate directly
* with USB devices without intervening kernel driver.
这个函数功能放到后面分析。
3.6. usbfs_init
3.7. usb_hub_init
3.8. usb_register_device_driver
* drivers/usb/generic.c - generic driver for USB devices (not interfaces)
* usb_register_device_driver - register a USB device (not interface) driver
这个函数是对设备驱动层来说的
4.自下而上全面分析Core层
好了,这个子系统的识别完了。好像没有想象得那么难。工作才开始,因为现在不是设计驱动程序,而是分析提供给驱动程序的接口,那么这就相当于分析库函数。下面才是真正的开始。
4.1. 找地图,重新选择路线
/************************core/makefile******************************/
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
config.o file.o buffer.o sysfs.o endpoint.o \
devio.o notify.o generic.o quirks.o devices.o
/************************core/makefile******************************/
从上面来看,需要关注的.c文件有
Usb.c, hub.c, hcd.c, urb.c, message.c, driver.c, config.c, file.c, buffer.c, sysfs.c, endpoint.c
Devio.c, notify.c, generic.c, quirks.o, devices.c.
为了方便,分析的过程,我不再详述,直接在源码中注释。有关流程图可能需要,因此这里只总结每个文件的用途,细节到每个函数。当然先从简单的开始。既然core屏蔽了硬件的细节,为了更加深入的了解core层,必须有一种硬件(HC:主机控制器作为参考),还是MPC8309体系结构为例。那么开始从hcd.h,hcd.c两个文件开始,首先看一下相关的硬件知识【待续】