标签: USB协议 UKey分析
资源来自《Linux那些事儿之USB》,本文主要是读书笔记,附加一些自己的分析和相关知识点说明
内核代码版本2.6.24
在USB出现以前,电脑的接口很多,串口、并口多方割据,键盘、鼠标、MODEM、打印机、扫描仪等都要链接到不同种类的接口上,一个接口只能链接一个设备。电脑不可能有那么多接口,扩展能力不足,速度也有限,而且不支持热插拔。
USB正是为了解决速度、扩展能力、易用性等问题应景而生的。
USB最初是替代串行、并行等各种低速总线,以单一类型总线链接各种不同的设备,USB2.0速度达到480MB/s,USB3.0是USB2.0速度的十倍。
Linux对USB1.1和USB2.0都是支持的,并抢在Windows前,在2.6.31内核中率先进行了USB3.0的支持
USB能够支持几千字节到几十兆字节的传输速率,来适应不同种类的外设。可以支持多达127个设备同时操作,也支持多功能设备。多功能设备当前指的就是一个设备同时有多个功能,比如USB扬声器。这通过在一个设备中包含多个接口来支持,一个接口支持一个功能。USB可以保证固定的贷款,这对视频/音频设备是利好的。
USB子系统的拓扑也是一棵树,并不以总线的方式来部署。这颗树包括USB连接、USB Host Controller和USB设备三个部分。而USB设备还包含Hub和功能设备。如下图所示:
USB主机控制器控制所有USB设备的通信,PC和USB主机控制器交互。Hub是对一个USB接口的扩展。现实中通常一个USB控制器和一个Hub集成在一起,该Hub称为Root Hub。
USB连接指的就是USB设备和主机(或Hub)的四线电缆,包括VBUS(电源线)、GND(地线)和两根信号线。
Compound Device就是将Hub和连在Hub上的设备封装在一起的所组成的设备。如下图所示。Composite Device是包含彼此独立的多个接口的设备。Compound Device的地址是独立的,Composite Device不论由多少接口,都只有一个地址。
USB总线是一种轮询式总线,协议规定所有的数据传输必须由主机发起,主机控制器初始化所有的数据传输,各种设备围绕在主机周围。
USB通信通过USB设备中的Endpoint(端点),主机和端点之间通过Pipe(管道)传输数据。端点就是通信的发送点和接受点,将数据给端点即可。管道实际上是为了让我们能够找到端点,常说的编码地址。
端点是有方向的,in或者out,生来注定。此外,协议规定USB设备必须有0号端点,可以是in也可以是out,实现默认的控制管道,进行设备控制。除了端点0,低速设备最多拥有两个端点,高速设备最多有15个in和15个out,这些端点在设备内部有唯一的端点号,在设备设计时就已经指定的。
管道分两种:message和stream,message要求数据时有格式的,协议规定message管道必须对应两个相同号码的端点:一个用来in,一个用来out,默认管道就是message管道,与之对应的0号端点就必须是有两个同样端点号0的端点。
USB端点有4中类型,对应四种数据传输方式:控制、中断、批量、等时。
虽然实际树形物理拓扑比较复杂,但是对于内核来说,所有的Hub和设备都被看做连接在Root Hub上的一个个逻辑设备。一个USB逻辑设备就是一系列端点的集合,与主机之间的通信发生在主机上的一个缓冲区和设备上的一个端点之间,通过管道来传输数据。
USB端点被捆绑为接口(Interface),一个接口代表一个基本功能,有的设备具有多个接口,例如USB扬声器就包含一个键盘接口和一个音频流接口。内核中一个接口对应一个驱动程序。
/sys/devices/pci0000:00/0000:00:09.0/usb2/2-1/2-1:1.0
USB系统中的第一个USB设备是Root Hub,通常包含在PCI设备中,是连接PCI总线和USB总线的bridge,控制着连接到其上的整个USB总线,所有的Root Hub,内核的USB Core都分配有独特的编号,例如上面例子中的USB2。
2-1表示Root Hub的哪个端口上插入了设备。2-1::1.0,后面1.0表示配置编号和接口编号。
通过usbfs可以找到设备的可选配置,usbfs挂载在/proc/bus/usb目录,从/proc/bus/usb/device文件可以直到系统中存在的所有USB设备的可选配置。
drivers/usb目录包含内容:
atm/
class/
core/
gadget/
host/
image/
misc/
mon/
serial/
storage/
Kconfig
Makfile
README
usb-skeleton.c
Core,内核开发人员专门写了一些代码,负责实现一些核心的功能,为别的设备驱动程序提供服务,例如申请内存等,实现一些所有的设备都需要的公共函数,美其名曰为USB Core。
Host,各主机控制器单独的代码移到host目录下,负责各种主机控制器的人来维护。主机控制器公共的代码任然保留在core目录下。
USB gadget,配件,一些内部运行Linux的嵌入式设备,设备有USB设备控制器,可以将PC,也即是我们的主机作为master端,将设备作为slave端和主机通过USB进行通信,从主机的观点来看,主机系统的USB驱动程序控制插入其中的USB设备,而USB gadget的驱动程序控制外围设备作为一个USB设备和主机通信。(因为连接的设备端其实也是一个host controller,所以需要模拟,U盘、网卡等)。
gadget目录下大概分为两个模块:
image、input、media、net、serial、storage:剩下的几个目录分别存放各种USB设备的驱动,U盘驱动在storage目录下,触摸屏和USB键盘鼠标的驱动在input目录下。
class:如果前面几个目录没有该驱动,则在此目录下寻找。USB协议中除了通用的软硬件电气接口规范等,还包含了各种各样的Class协议,来为不同的功能定义各自的标准接口和具体总线上的数据交互格式和内容,例如支持U盘功能的Mass Storage Class。
misc:如果之前的目录都没有该设备的驱动,就在该目录下寻找。
usb-skeleton.c是一个简单的USB driver框架
分析代码时Kconfig和Makefile就是Linux kernel迷宫的地图。
drivers/usb/core/usb.c
subsystem_initcall(usb_init);
可以理解为module_init,只不过因为这部分代码比较核心,开发人员把它看作一个子系统,而不仅仅是一个模块。usb_init是真正的初始话函数。
module_exit(usb_exit);
usb_exit是整个USB子系统结束时的清理函数。
static int __init usb_init(void)
__init对于内核来说是一种暗示,表示这个函数仅在初始化期间使用,在模块被装载后,它占用的资源就会释放掉用于它处。
#define __init __attribute__((__section__(".init.text")))
__attribute__是GNU C扩展,指示编译器进行特定方面的优化和更仔细的代码检查。GNU支持十几个属性,section就是其中之一。
通常编译器将函数放在.text节,变量放在.data节或.bss节,使用section属性,可以让编译器将函数或变量放在指定的节中。
链接器将相同节的代码或数据安排在一起,__init修饰的所有代码都会被放在.init.text节中,初始化结束后就可以释放这部分内存。
#define subsys_initcall(fn) __define_initcall("4",fn,4)
__define_initcall用于将指定的函数指针fn放到initcall.init节中,subsys_initcall是把fn放到.initcall.init的子节.initcall4.init中。关于.initcall.init、.init.text和.initcall4.init,需要了解一些内核可执行文件相关的概念,说明如下:
内核可执行文件由许多链接在一起的对象文件组成,对象文件有许多节,如文本、数据、init数据、bss等。这些对象文件都是由一个称为链接器脚本的文件链接被装入的。这个链接器脚本的功能就是将输入对象文件的各节映射到输出文件中。
换句话说,它将所有输入对象的文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。
vmlinux.lds是存在于arch//目录中的内核链接器脚本,负责链接内核的各个节并将他们转入内存中特定的偏移量处。
__initcall_start = .;
.initcall.init : AT(ADDR(.initcall.init) -0xC0000000) {
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
}
__initcall_end = .;
各个子节中函数指针的调用顺序是确定的,__init修饰的初始化函数在内核初始化过程中调用的顺序和.initcall.init节里函数指针的顺序有关,不同的初始化函数被放在不同的子节中,因此,也决定了他们的调用顺序。
实际执行函数调用的地方,就在/init/main.c文件中,do_initcalls函数慧直接调用这里的__initcall_start、__initcall_end进行判断。
if(nousb) {
pr_info("USB support disabled\n");
return 0;
}
nousb标识:启动内核时通过内核参数去掉USB子系统。
pr_info只是一个打印信息的可变参数宏,即printk的变体,在include/linux/kernel.h中定义:
#define pr_info(fmt, arg...) \
printk(KERN_INFO fmt, ##arg)
C99规定了可变参数宏
#define debug(format, ...) fprintf (stderr, format, __VA_ARGS__)
其中…表示可变参数,调用时替代__VA_ARGS__
GCC支持更复杂的形式,可以给可变参数取名字,例如:
#define debug(format, args...) fprintf(stderr, format, args)
pr_info定义中还有一个##,是为了处理特殊情况:没有可变参数,如果没有##,就会有一个多余的逗号,使用##使预处理器去掉这个多余的逗号。
这里简单说一下宏定义的特殊符号#和@
#表示将内容变为字符串,添加”“、##表示连接、#@表示将内容变为字符,添加”
##在变参宏__VA_ARGS__中表示去掉多余的逗号
之后就是usb_init最核心的工作了
模型的中心就是:总线、设备、驱动,bus、device和driver,都有自己的专属结构,定义在include/linux/device.h中:
struct bus_type {
const char * name;
struct module * owner;
struct kset subsys;
struct kset drivers;
struct kset devices;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
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 (*suspend_late)(struct device * dev, pm_message_t state);
int (*resume_early)(struct device * dev);
int (*resume)(struct device * dev);
unsigned int drivers_autoprobe:1;
};
struct device_driver {
const char * name;
struct bus_type * bus;
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module * owner;
const char * mod_name; /* used for built-in modules */
struct module_kobject * mkobj;
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);
};
struct device {
struct klist klist_children;
struct klist_node knode_parent; /* node in sibling list */
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE]; /* position on parent bus */
struct device_type *type;
unsigned is_registered:1;
unsigned uevent_suppress:1;
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 *driver_data; /* data private to the driver */
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 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;
spinlock_t devres_lock;
struct list_head devres_head;
/* class_device migration path */
struct list_head node;
struct class *class;
dev_t devt; /* dev_t, creates the sysfs "dev" */
struct attribute_group **groups; /* optional groups */
void (*release)(struct device * dev);
};
struct bus_type中有struct kset drivers和struct kset devices;
struct device中有struct bus_type *bus和struct device_driver *driver;
struct device_driver中有struct bus_type *bus和struct klist klist_devices;
相互知道彼此的存在。
这里面包含了Linux设备模型中最基本的元素:kobject和kset。
kobject是所有内核对象的积累,所实现的只是一些公共的接口,kset是同种类型kobject对象的集合,可以说是对象的容器。
这样内核使用kobject将各个对象连接起来组成了一个分层的结构体系,kobject包含了parent成员,kset使用链表来实现。
struct kset {
struct kobj_type *ktype;
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
};
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops * sysfs_ops;
struct attribute ** default_attrs;
};
struct bus_type中drivers和devices表示了一条总线拥有的两条链表。
此外,klist包含了一个链表和一个自旋锁,暂且把它看成链表也可以。
struct klist {
spinlock_t k_lock;
struct list_head k_list;
void (*get)(struct klist_node *);
void (*put)(struct klist_node *);
};
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
LIST_HEAD(xxx_list) ==>
struct list_head xxx_list = {
&(xxx_list),
&(xxx_list)
}
设备与驱动的匹配过程:
在usb_init的bus_register函数调用过程中注册
retval = bus_register(&usb_bus_type);
drivers/usb/core/driver.c中定义:
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.suspend = usb_suspend,
.resume = usb_resume,
};
这里说一下C语言中结构体初始化语法(C99):结构的指定初始化器使用点运算符和成员名标识特定的元素,例如只初始化book结构的value成员,可以这样写:
struct book {
char title[MAXTITL];
char author[AXAUTL];
float value;
};
struct book surprise = {.value = 10.99};
可以按照任意顺序使用指定初始化器:
struct book gift = { .value = 25.99,
.author = "James Broadfool",
.title = "Rue for the Toad"};
对特定成员的最后一次赋值才是它实际获得的值,例如:
struct book gift = { .value = 18.90,
.author = "Philionna Pestle",
0.25};
最终value的值就是0.25,因为在结构体声明中value紧跟在author成员之后,新值0.25取代了之前的18.9。
所以USB总线的name是“usb”,match就是总线设备和驱动之间的匹配函数,指向了usb_device_match函数
下面看一下bus_register的实现:
int bus_register(struct bus_type * bus)
{
int retval;
BLOCKING_INIT_NOTIFIER_HEAD(&bus->bus_notifier);
//初始化struct bus_type->bus_notifier(struct blocking_notifier_head类型)
retval = kobject_set_name(&bus->subsys.kobj, "%s", bus->name);
//调用kmalloc申请内核空间,为struct bus_type->subsys(kset类型).kobj(kobject类型).k_name赋值
//这里赋值为"usb"
if (retval)
goto out;
bus->subsys.kobj.kset = &bus_subsys;
//指定struct bus_type->subsys(kset类型).kobj(kobject类型).kset(kset类型)
//这里没有找到bus_subsys的定义位置
retval = subsystem_register(&bus->subsys);
//调用kset_register
//调用(kset_init & kset_add &kobject_uevent(kset->kobj, KOBJ_ADD))
//kset_init就是kobject_init、kset->list(struct list_head类型)、kset->list_lock(spinlock_t类型)初始化
//kset_add,调用kobject_add(&kset->kobj)
//kobject_add:
//如果kobject->kset存在:
//将kobject->entry(list_head类型)添加到kobject->kset->list中
//如果kobject->parent不存在,设置parent就是kobject->kset->kobject
//这里就是bus_subsys->kobject
//create_dir(kobject)->sysfs_create_dir(kobject)
//创建sysfs目录,如果存在parent就在该parent目录下创建,如果不存在就在sys根下创建
//这里就是在bus_subsys下创建
//kobject_uevent通知用户空间uevent,调用kobject_uevent_env(kobject,kobject_action,NULL)
//后者发送uevent附带environmental data,单独分析
//这里分析时注意
//struct bus_type->subsys(kset类型).kobj(kobject类型).kset(kset类型)
//并不是subsys(kset类型)自身,而是刚才设置bus_subsys
if (retval)
goto out;
retval = bus_create_file(bus, &bus_attr_uevent);
//->sysfs_create_file(bus_type->subsys.kobj, &bus_attr_uevent->attr)
//在kobject目录下创建bus_attr_uevent属性文件
if (retval)
goto bus_uevent_fail;
kobject_set_name(&bus->devices.kobj, "devices");
//设置bus_type->devices(kset类型).kobj(kobject类型).k_name为"devices"
bus->devices.kobj.parent = &bus->subsys.kobj;
//设置parent为bus_type->subsys.kobj,就是刚才创建的名字为usb的kobject
retval = kset_register(&bus->devices);
//过程同上文分析,核心就是初始化bus_type->devices(kset类型).kobject对象,这里没有设置该kobject对象的kset,但是已经设置过了parent,调用sysfs_create_file进行文件创建
if (retval)
goto bus_devices_fail;
kobject_set_name(&bus->drivers.kobj, "drivers");
//设置bus_type->drivers(kset类型).kobj(kobject类型).k_name为"drivers"
bus->drivers.kobj.parent = &bus->subsys.kobj;
//设置parent为bus_type->subsys.kobj,就是刚才创建的名字为usb的kobject
bus->drivers.ktype = &driver_ktype;
//设置bus_type->drivers(kset类型).ktype(kobj_type)为预定义的driver_ktype
retval = kset_register(&bus->drivers);
//过程同上文分析,核心就是初始化bus_type->drivers(kset类型).kobject对象,这里没有设置该kobject对象的kset,但是已经设置过了parent,调用sysfs_create_file进行文件创建
if (retval)
goto bus_drivers_fail;
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&bus->klist_drivers, NULL, NULL);
//初始化bus_type->klist_devices(klist类型)和bus_type->klist_drivers(klist类型)
bus->drivers_autoprobe = 1;
//设置bus_type->drivers_autoprobe=1,字面含义自动检测
retval = add_probe_files(bus);
//调用bus_create_file(bus_type, bus_attr_drivers_probe)
//和bus_create_file(bus_type, bus_attr_drivers_autoprobe)
//核心就是调用sysfs_create_file,为bus_type->subsys.kobj创建属性文件probe和autoprobe
if (retval)
goto bus_probe_files_fail;
retval = bus_add_attrs(bus);
//如果bus_type->bus_attrs(struct bus_attribute类型,第一个成员是attribute attr)不为空,则调用sysfs_create_file,在subsys.kobject下创建每个属性文件
if (retval)
goto bus_attrs_fail;
pr_debug("bus type '%s' registered\n", bus->name);
return 0;
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(&bus->drivers);
bus_drivers_fail:
kset_unregister(&bus->devices);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
subsystem_unregister(&bus->subsys);
out:
return retval;
}
其中bus_attr_uevent定义如下
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->subsys.kobj, action);
return count;
}
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
//BUS_ATTR是一个宏定义,如下
#define BUS_ATTR(_name,_mode,_show,_store) \
struct bus_attribute bus_attr_##_name = __ATTR(_name,_mode,_show,_store)
//bus_attribute定义如下
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};
//attribute定义如下
struct attribute {
const char *name;
struct module *owner;
mode_t mode;
};
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);
//show_drivers_autoprobe展示的就是bus_type->driver_autoprobe的值
//通知用户层一个uevent结束
//action表示正在发生的行为
//kobj表示正在发生行为的对象
//返回0表示完成,获取返回错误码
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
return kobject_uevent_env(kobj, action, NULL);
}
//其中kobject_action定义如下
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
下面来单独分析一下kobject_uevent_env函数实现
//发送一个uevent,附带环境数据
//action表示正在发生的事件
//kobj表示正在发生事件的对象
//envp_ext指向环境数据的指针
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
struct kobj_uevent_env *env;
const char *action_string = kobject_actions[action];//见下文
const char *devpath = NULL;
const char *subsystem;
struct kobject *top_kobj;
struct kset *kset;
struct kset_uevent_ops *uevent_ops;
u64 seq;
int i = 0;
int retval = 0;
pr_debug("%s\n", __FUNCTION__);
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
//找到当前kobject所属的kset
if (!top_kobj->kset) {
pr_debug("kobject attempted to send uevent without kset!\n");
return -EINVAL;
}
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
//获取所属kset及其uevent_ops
/*定义如下:
struct kset_uevent_ops {
int (*filter)(struct kset *kset, struct kobject *kobj);
const char *(*name)(struct kset *kset, struct kobject *kobj);
int (*uevent)(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env);
};
三个函数指针作用依次为:
filter:决定是否将事件传递到用户空间
name:负责将相应的字符串传递给用户空间的热插拔处理程序
uevent:将用户空间需要的参数添加到环境变量中*/
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject filter function caused the event to drop!\n");
return 0;
}
//调用所属kset附带的filter过滤函数,判断是否要放弃该事件
/* originating subsystem */
if (uevent_ops && uevent_ops->name)
subsystem = uevent_ops->name(kset, kobj);
else
subsystem = kobject_name(&kset->kobj);
if (!subsystem) {
pr_debug("unset subsystem caused the event to drop!\n");
return 0;
}
//调用所属kset附带的name函数获取子系统名称,或者使用所属kset->kobj(kobject类型).k_name
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/*开辟空间struct kobj_uevent_env
struct kobj_uevent_env {
char *envp[UEVENT_NUM_ENVP];
int envp_idx;
char buf[UEVENT_BUFFER_SIZE];
int buflen;
};*/
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
//按照kobject->parent(kobject)父子关系进行遍历
//利用每一级的kobject->k_name,中间用/分割,得到对象的路径
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
//调用了三个一样的函数add_uevent_var:ACTION、DEVPATH、SUBSYSTEM
//将键值信息添加到kobj_uevent_env的buf中
//envp是每个键值的单独索引,实际字符串还是只想buf中
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, envp_ext[i]);
if (retval)
goto exit;
}
}
//将参数中环境变量数据,添加到kobj_uevent_env中
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug ("%s - uevent() returned %d\n",
__FUNCTION__, retval);
goto exit;
}
}
//调用所属kset附带的uevent函数,添加所属kset特有的数据到kobj_uevent_env中
/* we will send an event, so request a new sequence number */
spin_lock(&sequence_lock);
seq = ++uevent_seqnum;
spin_unlock(&sequence_lock);
retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
if (retval)
goto exit;
//利用全局变量uevent_seqnum获取新的序列号,SEQNUM=%llu 添加到kobj_uevent_env中
#if defined(CONFIG_NET)
/* send netlink message */
if (uevent_sock) {
struct sk_buff *skb;
size_t len;
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
}
}
#endif
//调用netlink_broadcast,socket通信,暂没有详细分析????
/* call uevent_helper, usually only enabled during early boot */
if (uevent_helper[0]) {
char *argv [3];
argv [0] = uevent_helper;
argv [1] = (char *)subsystem;
argv [2] = NULL;
retval = add_uevent_var(env, "HOME=/");
if (retval)
goto exit;
retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
if (retval)
goto exit;
call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
}
//uevent_helper,处理uevent的用户空间程序???
exit:
kfree(devpath);
kfree(env);
return retval;
}
其中kobject_action是一个字符串数组,定义如下:
static const char *kobject_actions[] = {
[KOBJ_ADD] = "add",
[KOBJ_REMOVE] = "remove",
[KOBJ_CHANGE] = "change",
[KOBJ_MOVE] = "move",
[KOBJ_ONLINE] = "online",
[KOBJ_OFFLINE] = "offline",
};
这里用到了C语言的数组初始化器(C99):可以初始化指定的数组元素,传统C初始化语法必须初始化最后一个元素之前的所有元素,才能初始化它:
int arr[6] = {0,0,0,0,0,212}; //传统的语法
C99新特性可以在初始化列表中使用带方括号的下标指明待初始化的元素:
int arr[6] = {[5] = 212}; //把arr[5]初始化为212,其他未初始化元素都会被设置为0
如果指定初始化器后面有更多的值,例如[4]=31,30,31,那么后面这些值将被用于初始化指定元素后面的元素。如果再次初始化指定的元素,那么最后初始化将会取代之前的初始化。
TODO:关于uevent事件机制的详细分析,需要后续继续梳理
usb_bus_type 的match函数实现如下:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return 0;
/* TODO: Add real matching code */
return 1;
} else {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
参数就是设备和驱动,对应的就是总线两条链表里的设备和驱动。总线上有新设备或驱动添加时,这个函数总是会被调用。
上来就是一个判断,判断该device是否是usb设备(else表示usb接口)
is_usb_device实现如下:
static inline int is_usb_device(const struct device *dev)
{
return dev->type == &usb_device_type;
}
//usb_device_type定义如下
struct device_type usb_device_type = {
.name = "usb_device",
.release = usb_release_dev,
.uevent = usb_dev_uevent,
};
//device_type表示设备类型,嵌在struct device中,一个class或者bus可能包含不同的设备类型,例如partitions和disks、mouse和event。等价于kobject的kobj_type,如果name指定了,uevent会在DEVTYPE变量中包含它
struct device_type {
const char *name;
struct attribute_group **groups;
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*release)(struct device *dev);
int (*suspend)(struct device * dev, pm_message_t state);
int (*resume)(struct device * dev);
};
如果是usb_device,那么驱动必须是USB设备驱动,is_usb_device_dirver实现如下:
static inline int is_usb_device_driver(struct device_driver *drv)
{
return container_of(drv, struct usbdrv_wrap, driver)->
for_devices;
}
//container_of表示转换一个结构体成员到所在的结构体,就是将指针drv转换为struct usbdrv_wrap
//usbdrv_wrap定义如下
struct usbdrv_wrap {
struct device_driver driver;
int for_devices;
};
//成员for_devices,0表示接口驱动,非0表示设备驱动
下面就得看接口相关的实现
Linux设备模型中的device落实在USB子系统,成了两个结构:一个是struct usb_device,一个是struct usb_interface。
struct usb_interface {
/* array of alternate settings for this interface,
* stored in no particular order */
struct usb_host_interface *altsetting;
//可选设置
struct usb_host_interface *cur_altsetting; /* the currently
* active alternate setting */
//当前正在使用的设置
unsigned num_altsetting; /* number of alternate settings */
//可选设置的数量
/* If there is an interface association descriptor then it will list
* the associated interfaces */
struct usb_interface_assoc_descriptor *intf_assoc;
/*接口关联描述符 USB_DT_INTERFACE_ASSOCIATION: groups interfaces
struct usb_interface_assoc_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bFirstInterface;
__u8 bInterfaceCount;
__u8 bFunctionClass;
__u8 bFunctionSubClass;
__u8 bFunctionProtocol;
__u8 iFunction;
} __attribute__ ((packed));*/
int minor; /* minor number this interface is
* bound to */
//分配给接口的次设备号
//Linux下所有硬件设备都是用文件来表示,/dev目录下每个设备文件都会有主设备号和次设备号
//一般来说主设备号表明设备种类,表明设备对应哪个驱动程序
//次设备号是为了让驱动程序区分自己支持的多个设备
enum usb_interface_condition condition; /* state of binding */
/*接口和驱动的绑定状态
enum usb_interface_condition {
USB_INTERFACE_UNBOUND = 0,
USB_INTERFACE_BINDING,
USB_INTERFACE_BOUND,
USB_INTERFACE_UNBINDING,
};
*/
unsigned is_active:1; /* the interface is not suspended */
//表示接口是否处于挂起状态
//协议规定所有USB设备必须支持挂起状态,为了达到节电的目的
//当设备在指定时间(约3ms)内没有发生总线传输,就要进入挂起状态
//当它收到一个non-idle信号时就会被唤醒
unsigned sysfs_files_created:1; /* the sysfs attributes exist */
unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */
//是否需要打开远程唤醒功能。
//远程唤醒功能允许挂起的设备给主机发信号,通知主机它将从挂起状态恢复。
//如果主机处于挂起状态,就会唤醒主机,不然主机仍然在休眠
//协议中并没有要求USB设备一定要实现远程唤醒功能,即使实现了,主机也可以打开或关闭它
struct device dev; /* interface specific device info */
//Linux通用的设备模型
struct device *usb_dev; /* pointer to the usb class's device, if any */
//当接口使用USB_MAJOR作为主设备号时,usb_dev才会用到
//而且只在usb_register_dev和usb_deregister_dev两个函数中用到
//usb_dev指向的就是usb_register_dev函数创建的USB class device
int pm_usage_cnt; /* usage counter for autosuspend */
//pm表示电源管理,usage_cnt就是使用计数
//当计数为0时,接口允许autosuspend,休眠
};
这里提到的设置和之前说的配置不同,configuration是配置,setting是设置;配置是不同功能的选择,例如是手机作为摄像机还是作为U盘,接口对应功能,设置是具体功能内的设置,例如声音的大小。
Linux为USB设备预留了主设备号,include/linux/usb.h中
#define USB_MAJOR 180
#define USB_DEVICE_MAJOR 189
其中USB_DEVICE_MAJOR是用于usbfs的,USB_MAJOR是Linux为USB设备预留的主设备号。
不过USB设备有很多种,并不都会用这个预留的主设备号,比如移动硬盘显示的主设备号是8,,大多数USB设备都会与input、video等子系统关联,并不单单只作为USB设备而存在。
USB接口对应一个USB驱动,当USB接口关联有其他子系统,则需要在对应驱动程序的probe函数中使用相应的注册函数,USB_MAJOR也就用不到了,struct usb_interface的字段minor可以忽略,minor只在USB_MAJOR起作用时来起作用。
例如,USB键盘关联了input子系统,驱动对应drivers/hid/usbhid目录下的usbkbd.c文件,在它的probe函数中可以看到使用了input_register_device来注册一个输入设备。
TODO usb_register_dev device_class
下面来看一下接口设置struct usb_host_interface
/* host-side wrapper for one interface setting's parsed descriptors */
struct usb_host_interface {
struct usb_interface_descriptor desc;
//接口描述符,不同的设置会有不同的接口描述符,所以该结构体放在设置中
/* array of desc.bNumEndpoint endpoints associated with this
* interface setting. these will be in no particular order.
*/
struct usb_host_endpoint *endpoint;
//表示这个设置所用到的端点
char *string; /* iInterface string, if present */
//保存从设备中取出来的字符串描述符信息,字符串描述符可有可无,该指针可能为空
unsigned char *extra; /* Extra descriptors */
int extralen;
//有关额外的描述符(其他描述符或者厂商为设备特别定义的描述符)
};
USB描述符是一个带有预定义格式的数据结构,里面保存了USB设备的各种属性还有相关信息,可以通过向设备请求获取他们的内容来了解和感知一个USB设备。分为四种:设备描述符、配置描述符、接口描述符和端点描述符,协议中规定一个USB设备必须支持这四种描述符。
这些描述符都放在USB设备的EEPROM内,EEPROM用来存储设备本身信息,即电可擦写可编程ROM,与Flash虽说都是要电擦除的,但它可以按字节擦除,Flash只能一次擦除一个block,如果要修改比较少的数据的话使用它还是比较合适的,但EEPROM成本较高,所以一般USB设备只拿它存储一些本身特有的信息,数据的存储还是用Flash。
接口描述符定义如下:
struct usb_interface_descriptor {
__u8 bLength;
//描述符的字节长度。协议中规定,每个描述符必须以一个字节打头来标明描述符的长度。接口描述符的bLength是9。
__u8 bDescriptorType;
//描述符的类型,对于接口描述符来说,值为USB_DT_INTERFACE
__u8 bInterfaceNumber;
//接口号,每个配置可以包含多个接口,这个值是他们的索引
__u8 bAlternateSetting;
//接口使用的是哪个可选设置,协议中规定,接口默认使用的设置总为0号设置
__u8 bNumEndpoints;
//接口拥有的端点数量,这里并不包含端点0,端点0是所有设备必须提供的
__u8 bInterfaceClass;
//USB设备类
__u8 bInterfaceSubClass;
//USB设备子类
__u8 bInterfaceProtocol;
//所遵循的不同通信协议
//USB设备分为很多类、类下面又分了子类、每个子类又按各种设备所遵循的不同的通信协议继续细分
__u8 iInterface;
//接口对应的字符串描述符的索引值,字符串描述符不是必须的,是可选的
//字符串描述符可以有多个,这里就是它们的索引
} __attribute__ ((packed));
//该__attribute__告诉编译器这个结构的元素都是1字节对齐的,不要再添加填充位
#define USB_DT_INTERFACE_SIZE 9
//接口描述符长度
//描述符类型
/*
* Descriptor types ... USB 2.0 spec table 9.5
*/
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
#define USB_DT_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_INTERFACE_POWER 0x08
/* these are from a minor usb 2.0 revision (ECN) */
#define USB_DT_OTG 0x09
#define USB_DT_DEBUG 0x0a
#define USB_DT_INTERFACE_ASSOCIATION 0x0b
/* these are from the Wireless USB spec */
#define USB_DT_SECURITY 0x0c
#define USB_DT_KEY 0x0d
#define USB_DT_ENCRYPTION_TYPE 0x0e
#define USB_DT_BOS 0x0f
#define USB_DT_DEVICE_CAPABILITY 0x10
#define USB_DT_WIRELESS_ENDPOINT_COMP 0x11
#define USB_DT_WIRE_ADAPTER 0x21
#define USB_DT_RPIPE 0x22
lsusb打印出来的设备名称就是字符串描述符提供的,例如:
$lsusb
Bus 001 Device 013: ID 04b4:1081 Cypress Semiconductor Corp.
Bus 001 Device 001: ID 0000:0000
其中Cypress Semiconductor Corp.就是字符串描述符提供的,字符串描述符可以有多个,提供一些设备接口相关的描述性信息,比如厂商的名字,产品序列号等,接口描述符中的索引值就是用来区分它们的。
端点是USB数据传输的终点,看一下内核定义:
struct usb_host_endpoint {
struct usb_endpoint_descriptor desc;
//端点描述符
struct list_head urb_list;
//端点要处理的urb队列,urb包含了执行USB传输所需的全部信息
//如果你想要和USB通信,就得创建一个urb,并且为它赋值,交给USB core
//然后USB core会找到合适的主机控制器,从而进行具体的数据传输
void *hcpriv;
//提供给HCD(Host Control Driver)用的,比如等时端点会在里边放一个ehci_iso_stream
struct ep_device *ep_dev; /* For sysfs info */
//共sysfs用的,可以到/sys下看一下
// ls /sys/bus/usb/devices/usb1/ep_00/
// bEndpointAddress bmAtributes direction subsystem wMaxpacketSize
// bInterval dev intervaltype
// bLength device power uevent
// ep_00端点目录下的文件就是在usb_create_ep_files函数中使用ep_dev创建的
/*
struct ep_device {
struct usb_endpoint_descriptor *desc;
struct usb_device *udev;
struct device dev;
int minor;
};
*/
unsigned char *extra; /* Extra descriptors */
int extralen;
//额外扩展的描述符
int enabled;
//URBs may be submitted to this endpoint
};
/* USB_DT_ENDPOINT: Endpoint descriptor */
struct usb_endpoint_descriptor {
__u8 bLength;
//描述符字节长度,7或者9,多出的两个字节针对音频设备扩展的
__u8 bDescriptorType;
//描述符类型,USB_DT_ENDPOINT 0x05
__u8 bEndpointAddress;
//bits0-bits3 表示的就是端点号,&0x0f可以得到端点号
//bit8表示方向,&0x80可以得到
//#define USB_DIR_OUT 0
//#define USB_DIR_IN 0x80
__u8 bmAttributes;
//属性:bit1和bit0共同称为TransferType,传输类型
//00表示控制传输、01表示等时传输、10表示批量传输、11表示中断传输
__le16 wMaxPacketSize;
//端点一次可处理的最大字节数
__u8 bInterval;
//USB是轮询式总线,该值表示希望主机轮询自己的时间间隔
/* NOTE: these two are _only_ in audio endpoints. */
/* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
__u8 bRefresh;
__u8 bSynchAddress;
//音频端点特有
} __attribute__ ((packed));
#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
struct usb_device {
int devnum; /* Address on USB bus */
//设备地址,只是USB设备在一条USB总线上的编号
//总线为了记录树上的每个叶子节点,设有一个地址映射表,即struct usb_bus结构体内的成员struct usb_devmap devmap
/*
struct usb_devmap {
unsigned long devicemap[128 / (8*sizeof(unsigned long))];
};
数组大小依赖于unsigned long的大小,但是总共肯定是128位
这里用sizeof就是为了跨平台应用,也就是说每条总线最多可以连接128个设备
*/
//这里提出了usb_bus,前面注册的usb_bus_type,让系统知道有这么一个类型的总线
//一个总线要有一个struct usb_bus结构体变量
char devpath [16]; /* Use in messages: /port/port/... */
//顶级设备的devpath就是其连在Root Hub上的端口号,例如4-0:1.0中的0,次级的设备就是其父Hub的devpath后面加上其端口号,例如4-0.1:1.0中的01
enum usb_device_state state; /* configured, not attached, etc */
//设备状态详见下文
enum usb_device_speed speed; /* high/full/low (or error) */
/*设备的速度
enum usb_device_speed {
USB_SPEED_UNKNOWN = 0, enumerating
USB_SPEED_LOW, USB_SPEED_FULL, usb 1.1
USB_SPEED_HIGH, usb 2.0
USB_SPEED_VARIABLE, wireless (usb 2.5)
};*/
struct usb_tt *tt; /* low/full speed dev, highspeed hub */
int ttport; /* device port on that tt hub */
//transaction translator,负责高速和低速/全速的数据转换,如果高速设备中有TT就可以连接低速/全速设备
unsigned int toggle[2]; /* one bit for each endpoint
* ([0] = IN, [1] = OUT) */
//分别对应IN端点和OUT端点,表示每个端点当前发送或接收的数据包是DATA0还是DATA1
struct usb_device *parent; /* our hub, unless we're the root */
//父节点,hub
struct usb_bus *bus; /* Bus we're part of */
//设备所在总线
struct usb_host_endpoint ep0;
//端点0,在usb_device对象产生时就要初始化
struct device dev; /* Generic device interface */
//嵌入的struct device结构
struct usb_device_descriptor descriptor;/* Descriptor */
//设备描述符,见下文
struct usb_host_config *config; /* All of the configs */
//设备拥有的所有配置
struct usb_host_config *actconfig;/* the active configuration */
//当前激活的配置
struct usb_host_endpoint *ep_in[16];
struct usb_host_endpoint *ep_out[16];
//除了端点0,一个设备即使在高速模式下最多也只能有15个IN端点和15个OUT端点
char **rawdescriptors; /* Raw descriptors for each config */
//字符指针数组,每一项都指向一个使用GET_DESCRIPTOR请求去获得配置描述符时所得结果
//返回的不是配置描述符,而是包含的接口及接口内端点的描述符一起返回
unsigned short bus_mA; /* Current available from the bus */
//在主机控制器驱动程序中设置,通常USB端口可提供500mA电流
u8 portnum; /* Parent port number (origin 1) */
//不管是Root Hub还是一般Hub,USB设备必要要插在Hub端口上,该值表示端口号
u8 level; /* Number of USB hub ancestors */
//层次,也可以说级别,表示USB设备树的级联关系,Root Hub是level0,下面一层就是level1
unsigned can_submit:1; /* URBs may be submitted */
unsigned discon_suspended:1; /* Disconnected while suspended */
unsigned have_langid:1; /* whether string_langid is valid */
//说明string_langid是否有效
unsigned authorized:1; /* Policy has determined we can use it */
unsigned wusb:1; /* Device is Wireless USB */
int string_langid; /* language ID for strings */
//USB设备中的字符串描述符使用的是UNICODE,支持多语言,该值用来指定使用哪种语言
/* static strings from the device */
char *product; /* iProduct string, if present */
char *manufacturer; /* iManufacturer string, if present */
char *serial; /* iSerialNumber string, if present */
//分别用来保存产品、厂商和序列号对应的字符串描述符信息
struct list_head filelist;
#ifdef CONFIG_USB_DEVICE_CLASS
struct device *usb_classdev;
#endif
#ifdef CONFIG_USB_DEVICEFS
struct dentry *usbfs_dentry; /* usbfs dentry entry for the device */
#endif
//usbfs相关
/*
* Child devices - these can be either new devices
* (if this is a hub device), or different instances
* of this same device.
*
* Each instance needs its own set of data structures.
*/
int maxchild; /* Number of ports if hub */
//Hub的端口数,不包含上行端口
struct usb_device *children[USB_MAXCHILDREN];
//USB_MAXCHILDREN 31,端口对应的设备对象,一个Hub可以接255个端口,不过实际上遇到的Hub最多可以支持10个端口,所以31端口基本上够用了
int pm_usage_cnt; /* usage counter for autosuspend */
//pm表示电源管理,usage_cnt就是使用计数
//当计数为0时,设备允许autosuspend,休眠
u32 quirks; /* quirks of the whole device */
atomic_t urbnum; /* number of URBs submitted for the whole device */
//电源管理相关
#ifdef CONFIG_PM
struct delayed_work autosuspend; /* for delayed autosuspends */
struct mutex pm_mutex; /* protects PM operations */
unsigned long last_busy; /* time of last use */
int autosuspend_delay; /* in jiffies */
unsigned auto_pm:1; /* autosuspend/resume in progress */
unsigned do_remote_wakeup:1; /* remote wakeup should be enabled */
unsigned reset_resume:1; /* needs reset instead of resume */
unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */
unsigned autosuspend_disabled:1; /* autosuspend and autoresume */
unsigned autoresume_disabled:1; /* disabled by the user */
unsigned skip_sys_resume:1; /* skip the next system resume */
#endif
};
设备状态信息如下:
enum usb_device_state {
USB_STATE_NOTATTACHED = 0,
//NOTATTACHED isn't in the USB spec,
//and this state acts the same as ATTACHED ... but it's clearer this way
//表示设备没有Attached
//chapter 9 and authentication (wireless) device states
USB_STATE_ATTACHED, //规范中的6个状态之一
//表示设备已经连接到USB接口上,Hub检测到设备时的初始化状态
USB_STATE_POWERED, //wired //规范中的6个状态之一
//加电状态,USB设备电源可以来自外部电源,self-powered,也可以来自Hub,bus-powered
USB_STATE_UNAUTHENTICATED, //auth
USB_STATE_RECONNECTING, //auth
USB_STATE_DEFAULT, //limited function //规范中的6个状态之一
//默认状态,Powered之后,设备必须收到一个复位(reset)信号并成功复位后
//才能使用默认地址回应主机发过来的设备和配置描述符的请求
USB_STATE_ADDRESS, //规范中的6个状态之一
//主机分配一个唯一的地址给设备,此时设备可以使用默认管道响应主机请求
USB_STATE_CONFIGURED, //most functions //规范中的6个状态之一
//表示设备已经被主机配置过,就是协议中说的处理一个带非0值的SetConfiguration请求
//此时主机可以使用设备提供的所有功能
USB_STATE_SUSPENDED //规范中的6个状态之一
//挂起状态,为了省电,设备在指定时间内(约3ms)没有发生总线传输则进入挂起状态
//此时设备要自己维护包括地址、配置在内的信息
/* NOTE: there are actually four different SUSPENDED
* states, returning to POWERED, DEFAULT, ADDRESS, or
* CONFIGURED respectively when SOF tokens flow again.
*
};
USB设备描述符如下所示
struct usb_device_descriptor {
__u8 bLength;//描述符长度
//USB_DT_DEVICE_SIZE 18
__u8 bDescriptorType;
//bDescriptorType,USB_DT_DEVICE 0x01
__le16 bcdUSB;
//USB spec版本号,如果一个设备能够告诉传输,这一项就应该是0200H
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
//设备类、子类、子类下不同协议
__u8 bMaxPacketSize0;
//端点0一次可以处理的最大字节数,端点0的属性放在设备描述符中,不需要自己专门的端点描述符
__le16 idVendor;
__le16 idProduct;
//厂商、产品ID
__le16 bcdDevice;
//设备版本号
__u8 iManufacturer;
__u8 iProduct;
__u8 iSerialNumber;
//厂商、产品和序列号对应的字符串描述符索引值
__u8 bNumConfigurations;
//当前速度模式下支持的配置数量
} __attribute__ ((packed));
struct usb_host_config {
struct usb_config_descriptor desc;
//配置描述符,见下文
char *string; /* iConfiguration string, if present */
//保存了配置描述符iConfiguration字段对应的字符串描述符信息
/* List of any Interface Association Descriptors in this
* configuration. */
struct usb_interface_assoc_descriptor *intf_assoc[USB_MAXIADS];
/* the interfaces associated with this configuration,
* stored in no particular order */
struct usb_interface *interface[USB_MAXINTERFACES];
//配置包含的接口,该数组顺序未必是按配置里接口号的顺序
//想要某个接口号对应的struct usb_interface结构对象
//必须使用drivers/usb/usb.c中定义的usb_ifnum_to_if函数
/* Interface information available even when this is not the
* active configuration */
struct usb_interface_cache *intf_cache[USB_MAXINTERFACES];
//USB接口缓存,缓存是为了配置被取代后仍然能够获取它的一些信息
//就把日后可以能会需要的一些东西放在intf_cache数组的struct usb_interface_cache对象里。
//sysfs中显示的只是当前的配置信息,usbfs里显示的系统中所有USB设备的可选配置和端口信息,就是利用intf_cache这个数组里的缓存实现的
unsigned char *extra; /* Extra descriptors */
int extralen;
//额外扩展的描述符,使用GET_DESCRIPTOR请求后,会在标准配置描述符后面返回给你
};
配置描述符
struct usb_config_descriptor {
__u8 bLength;
//长度,9
__u8 bDescriptorType;
//描述符类型,USB_DT_CONFIG 0x02
//还可以为USB_DT_OTHER_SPEED_CONFIG 0x07,描述的是告诉设备操作在低速或全速模式时的配置
__le16 wTotalLength;
//使用GET_DESCRIPTOR请求返回的数据长度,对配置、接口、端点、class-或vendor-specific描述符在内的所有描述符的统计
__u8 bNumInterfaces;
//配置包含的接口数目
__u8 bConfigurationValue;
//指明了将要激活哪个配置(SET_CONFIGURATION)
__u8 iConfiguration;
//配置信息的字符串描述符的索引值
__u8 bmAttributes;
//配置的一些特点,bit6 self-powered、bit5 支持远程唤醒、bit7必须为1、bit4 battery powered
__u8 bMaxPower;
//设备正常运转时,从总线那里分得的最大电流值,以2mA为单位
} __attribute__ ((packed));
#define USB_DT_CONFIG_SIZE 9
TODO Interface Association Descriptors,前面接口usb_interface中也有
每个接口对应一个独立的功能,需要专门的驱动进行交流,但是接口隶属于一个USB设备,设备还可以有不同的配置,可以为设备指定特定的配置,就需要USB设备驱动来完成相关处理,struct usb_device_dirver,下面分别说一下usb_device_driver(设备驱动)和usb_driver(接口驱动)
struct usb_driver {
const char *name;
//驱动名字,对应/sys/bus/usb/drivers/下面子目录的名称,这里所有USB驱动名称必须唯一
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
//判断该驱动是否愿意接受某个接口,一个驱动往往可以支持多个接口
void (*disconnect) (struct usb_interface *intf);
//接口失去联系,或使用rmmod卸载驱动将它和接口强行分开时调用
int (*ioctl) (struct usb_interface *intf, unsigned int code,
void *buf);
//驱动通过usbfs和用户空间交流时使用ioctl
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_device_id {
/* which fields to match against?
__u16 match_flags;
/* Used for product specific matches; range is inclusive
__u16 idVendor;
__u16 idProduct;
__u16 bcdDevice_lo;
__u16 bcdDevice_hi;
/* Used for device class matches
__u8 bDeviceClass;
__u8 bDeviceSubClass;
__u8 bDeviceProtocol;
/* Used for interface class matches
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
__u8 bInterfaceProtocol;
/* not matched against
kernel_ulong_t driver_info;
};
*/
struct usb_dynids dynids;
/*动态id
每个驱动诞生时它的id在id_table中已经确定了,通过动态id可以添加新的id给他
只要新id代表的设备存在,就会和它绑定起来
struct usb_dynids {
spinlock_t lock;
struct list_head list;
};
通过sysfs可以添加新id,在/sys/bus/usb/drivers目录下,选择对应驱动进去,可以看到new_id文件
使用echo将厂商和产品id写进去就可以了,例如:
echo 0557 2008 > /sys/bus/usb/drivers/foo_driver/new_id
*/
struct usbdrv_wrap drvwrap;
/*嵌入struct device_driver,但是多了一层包装
struct usbdrv_wrap {
struct device_driver driver;
int for_devices; //非0表示device drivers,0表示interface drivers
};
*/
unsigned int no_dynamic_id:1;
//禁止动态id
unsigned int supports_autosuspend:1;
//对autosuspend的支持,设置为0表示不再允许绑定这个驱动的接口autosuspend
};
#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
设备驱动定义如下,除了少了很多参数外,基本和接口驱动一样,函数参数用的是usb_device,而不是usb_interface,再有probe函数没有device_id列表,表示接受所有的USB设备
struct usb_device_driver {
const char *name;
int (*probe) (struct usb_device *udev);
void (*disconnect) (struct usb_device *udev);
int (*suspend) (struct usb_device *udev, pm_message_t message);
int (*resume) (struct usb_device *udev);
struct usbdrv_wrap drvwrap;
unsigned int supports_autosuspend:1;
};
#define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \
drvwrap.driver)
内核定义了一个usb_device_driver,在drivers/usb/core/generic.c中,如下
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
};
这个对象在usb_init中调用usb_register_device_driver进行了注册
usb_bus_type 的match函数usb_device_match实现如下:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
//前面分析过,通过device->type(device_type类型)来判断
//总线类型bus_type,设备类型device_type
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return 0;
//这个函数用来判断是不是usb device driver,usb device driver说明见下文
/* TODO: Add real matching code */
return 1;
} else { //接口
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
return 0;
//如果驱动时usb_device_driver直接返回
intf = to_usb_interface(dev);
//将struct device转换为struct usb_interface
usb_drv = to_usb_driver(drv);
//将struct device_driver转换为struct usb_driver
id = usb_match_id(intf, usb_drv->id_table);
//遍历usb_driver->id_table(usb_device_id *类型,数组)
//调用usb_match_one_id(usb_interface, usb_device_id),见下文
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
/*匹配动态id
该函数遍历usb_driver->dynids(usb_dynids类型),得到usb_dynid
struct usb_dynid {
struct list_head node;
struct usb_device_id id;
};
得到usb_dynid->id(usb_device_id类型)
调用usb_match_one_id(usb_interface, usb_device_id),见下文
*/
if (id)
return 1;
}
return 0;
}
下面看一下usb_match_one_id的实现
int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_host_interface *intf;
struct usb_device *dev;
// proc_connectinfo in devio.c may call us with id == NULL. */
if (id == NULL)
return 0;
intf = interface->cur_altsetting;
//获得当前激活的设置usb_host_interface
dev = interface_to_usbdev(interface);
//利用device->parent获得usb_interface所属的usb_device
if (!usb_match_device(dev, id))
return 0;
//设备匹配,见下文
/* The interface class, subclass, and protocol should never be
* checked for a match if the device class is Vendor Specific,
* unless the match record specifies the Vendor ID. */
if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&
!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |
USB_DEVICE_ID_MATCH_INT_SUBCLASS |
USB_DEVICE_ID_MATCH_INT_PROTOCOL)))
return 0;
//如果usb_device->descriptor.bDeviceClass是Vendor指定的
//并且usb_device_id的match_flags没有指定要匹配VENDOR,但是要匹配Class、SubClass、Protocol,则直接返回0,表示不匹配
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
(id->bInterfaceClass != intf->desc.bInterfaceClass))
return 0;
//usb_device_id和接口描述符中的bInterfaceClass匹配
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
(id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
return 0;
//usb_device_id和接口描述符中的bInterfaceSubClass匹配
if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
(id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
return 0;
//usb_device_id和接口描述符中的bInterfaceProtocol匹配
return 1;
}
usb_match_device实现如下:
如果usb_device_id->match_flags & USB_DEVICE_ID_MATCH_VENDOR
usb_device_id->idVendor等于usb_device->descriptor.idVendor
如果usb_device_id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT
usb_device_id->idProduct等于usb_device->descriptor.idProduct
如果usb_device_id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO
usb_device_id->bcdDevice_lo不大于usb_device->descriptor.bcdDevice
如果usb_device_id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI
usb_device_id->bcdDevice_hi不小于usb_device->descriptor.bcdDevice
如果usb_device_id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS
usb_device_id->bDeviceClass不小于usb_device->descriptor.bDeviceClass
如果usb_device_id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS
usb_device_id->bDeviceSubClass不小于usb_device->descriptor.bDeviceSubClass
如果usb_device_id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL
usb_device_id->bDeviceProtocol不小于usb_device->descriptor.bDeviceProtocol
见5.4.1.bus_register(&usb_bus_type)
static struct class *usb_host_class;
int usb_host_init(void)
{
int retval = 0;
usb_host_class = class_create(THIS_MODULE, "usb_host");
//字面意思,创建一个struct class结构体,见下文
if (IS_ERR(usb_host_class))
retval = PTR_ERR(usb_host_class);
return retval;
}
这里涉及了一个概念device class是设备的分类,定义在include/linux/device.h中如下
/*
* device classes
*/
struct class {
const char * name;
struct module * owner;
struct kset subsys;
struct list_head children;
struct list_head devices;
struct list_head interfaces;
struct kset class_dirs;
struct semaphore sem; /* locks both the children and interfaces lists */
struct class_attribute * class_attrs;
struct class_device_attribute * class_dev_attrs;
struct device_attribute * dev_attrs;
int (*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*release)(struct class_device *dev);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *, pm_message_t state);
int (*resume)(struct device *);
};
class_create函数实现如下:
struct class *class_create(struct module *owner, const char *name)
{
struct class *cls;
int retval;
cls = kzalloc(sizeof(*cls), GFP_KERNEL);
if (!cls) {
retval = -ENOMEM;
goto error;
}
//开辟空间,以0初始化的kmalloc
cls->name = name;
cls->owner = owner;
cls->class_release = class_create_release;
cls->release = class_device_create_release;
//成员赋值
//本例class->name = "usb_host"
//本例class->owner = THIS_MODULE,见下文
retval = class_register(cls);
//注册device class,见下文
if (retval)
goto error;
return cls;
error:
kfree(cls);
return ERR_PTR(retval);
}
这里简单说一下THIS_MODULE和Linux的模块
结构体struct module在内核中代表一个内核模块,通过insmod(实际执行init_module系统调用)把自己编写的内核模块插入内核时,模块便与一个 struct module结构体相关联,并成为内核的一部分。
insmod调用了系统调用init_module,在该系统调用函数中,首先调用 load_module,把用户空间传入的整个内核模块文件创建成一个内核模块,返回一个struct module结构体。内核中便以这个结构体代表这个内核模块。
state是模块当前的状态。它是一个枚举型变量
可取的值为:MODULE_STATE_LIVE,MODULE_STATE_COMING,MODULE_STATE_GOING。
load_module函数中完成模块的部分创建工作后,把状态置为 MODULE_STATE_COMING,sys_init_module函数中完成模块的全部初始化工作后(包括把模块加入全局的模块列表,调用模块本身的初始化函数),把模块状态置为MODULE_STATE_LIVE。
最后,使用rmmod工具卸载模块时,会调用系统调用 delete_module,会把模块的状态置为MODULE_STATE_GOING。这是模块内部维护的一个状态。
下面看一下module_init的实现,include/linux/init.h
#ifndef MODULE
//见下文
#ifndef __ASSEMBLY__
...
/*
* A "pure" initcall has no dependencies on anything else, and purely
* initializes variables that couldn't be statically initialized.
*
* 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)
#define __initcall(fn) device_initcall(fn)
#define module_init(x) __initcall(x);
#else /* MODULE */
...
#endif
MODULE表示是否编译为模块:在make xxxconfig的时候,每一个模块都有三种选择:Y M N。选N不编译模块。选Y模块编译进内核。选M,就会在编译这个模块时候定义MODULE了。是在Makefile里面定义的。
在3.1章的时候,做过分析,显然module_init就是将该函数指针放在.initcall6.init子节,start_kernel的最后会调用这些函数
宏THIS_MODULE表示当前模块,用来引用模块的struct module结构,下面看一下THIS_MODULE的实现
#ifdef MODULE
extern struct module __this_module;
#define THIS_MODULE (&__this_module)
#else /* !MODULE */
#define THIS_MODULE ((struct module *)0)
#endif
指向的就是__this_module,编译模块后,会生成*.mod.c这样的文件,打开文件后,出现如下类似的定义:
struct module __this_module
__attribute__((section(".gnu.linkonce.this_module"))) = {
.name = KBUILD_MODNAME,
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
};
这个文件是调用modpost生成,所以生成模块的后就会出现__this_module这个变量,表示当前模块
最后分析一下class_register
int class_register(struct class * cls)
{
int error;
pr_debug("device class '%s': registering\n", cls->name);
INIT_LIST_HEAD(&cls->children);
INIT_LIST_HEAD(&cls->devices);
INIT_LIST_HEAD(&cls->interfaces);
kset_init(&cls->class_dirs);
init_MUTEX(&cls->sem);
//相关成员的初始化
error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name);
//设置class->subsys(kset类型).kobj(kobject类型).k_name
//本例为"usb_host"
if (error)
return error;
cls->subsys.kobj.kset = &class_subsys;
//指定class->subsys(kset类型).kobj(kobject类型)的所属kset,class_subsys为预定义的
//暂未找到定义
error = subsystem_register(&cls->subsys);
//调用subsystem_register进行kset的注册,在3.4.1分析过,总结来说就是:
//进行对应kset及包含kobject的初始化,将该kset->kobj添加到kset->kobj.kset的list中
//设置kset->kobj.parent = kset->kobj.kset->kobj
//创建该kset的sysfs,就是在parent目录下
//kobject_uevent通知用户空间uevent
//这里注意kset->kobj.kset并不是自己,而是刚才设置的class_subsys
if (!error) {
error = add_class_attrs(class_get(cls));
class_put(cls);
}
return error;
}
一个总线也是一个设备,必须单独注册,USB是通过快速串行通信来读写数据的,这里把它当作字符设备来注册
int usb_major_init(void)
{
int error;
error = register_chrdev(USB_MAJOR, "usb", &usb_fops);
//字面理解注册一个字符设备,详见下文
if (error)
err("unable to get major %d for usb devices", USB_MAJOR);
return error;
}
其中usb_fops定义如下,是字符设备的文件操作函数指针
static const struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
};
下面看一下register_chrdev的实现
//字符设备结构体
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
//为字符设备注册一个major number
//@major:主设备号,如果是0则动态分配
//@name:设备名称
//@fops:设备相关的文件操作
int register_chrdev(unsigned int major, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
char *s;
int err = -ENOMEM;
cd = __register_chrdev_region(major, 0, 256, name);
//注册一个major,并指定minor范围,详见下文
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc();
//开辟一个cdev结构体,主要是开辟cdev空间,初始化kobject
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
for (s = strchr(kobject_name(&cdev->kobj),'/'); s; s = strchr(s, '/'))
*s = '!';
//cdev结构体赋值
err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
//在系统中添加一个字符设备,详见下文
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, 0, 256));
return err;
}
主设备号注册函数实现如下
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct file_operations *fops;
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
#define CHRDEV_MAJOR_HASH_SIZE 255
//全局的数组chrdevs,最多255个
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
int minorct, const char *name)
{
struct char_device_struct *cd, **cp;
int ret = 0;
int i;
cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
//开辟struct char_device_struct空间
if (cd == NULL)
return ERR_PTR(-ENOMEM);
mutex_lock(&chrdevs_lock);
/* temporary */
if (major == 0) {
for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {
if (chrdevs[i] == NULL)
break;
}
if (i == 0) {
ret = -EBUSY;
goto out;
}
major = i;
ret = major;
}
//如果参数major为0,那么需要动态开辟,寻找数组chrdevs中目前没有被占用的最小索引作为major
cd->major = major;
cd->baseminor = baseminor;
cd->minorct = minorct;
strncpy(cd->name,name, 64);
//设置struct char_device_struct的成员
i = major_to_index(major);
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
if ((*cp)->major > major ||
((*cp)->major == major &&
(((*cp)->baseminor >= baseminor) ||
((*cp)->baseminor + (*cp)->minorct > baseminor))))
break;
//首先获取当前major在chrdevs数组的位置,chrdevs[major%255]
//每个chrdevs[i]里面也是一个链表,next指针来遍历
//如果该链表中出现major大于当前major
//或者 major等于当前major并且(baseminor或者最大minor>=当前baseminor)
//意味着major相同,并且minor范围有交叉 或者 minor比当前minor范围整体大
//则停止遍历,得到对应的struct char_device_struct
//例如,当前major为258,chardevs[3]的链表中major为3,
/* Check for overlapping minor ranges. */
if (*cp && (*cp)->major == major) {
int old_min = (*cp)->baseminor;
int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
int new_min = baseminor;
int new_max = baseminor + minorct - 1;
/* New driver overlaps from the left. */
if (new_max >= old_min && new_max <= old_max) {
ret = -EBUSY;
goto out;
}
/* New driver overlaps from the right. */
if (new_min <= old_max && new_min >= old_min) {
ret = -EBUSY;
goto out;
}
}
//如果存在major相同的项,并且minor范围有交叉,则跳转到out,错误
//最终得到的合适的插入位置
//将新创建的struct char_device_struct添加到chrdevs[major%255]的next链表中
cd->next = *cp;
*cp = cd;
mutex_unlock(&chrdevs_lock);
return cd;
out:
mutex_unlock(&chrdevs_lock);
kfree(cd);
return ERR_PTR(ret);
}
下面我们通过例子来进行模拟,已加深理解
init:
数组每一项指针都为空
step1:
增加major=3,minor=15-20
结果chrdevs[3]
major=3(minor=15-20)
step2:
增加major=3,minor=0-10
结果chardevs[3]
major=3(minor=0-10)->major=3(minor=15-20)
step3:
增加major=3,minor=25-30
结果chardevs[3]
major=3(minor=0-10)->major=3(minor=15-20)->major=3(minor=25-30)
step4:
增加major=258,minor=10-30
结果chardevs[3]
major=3(minor=0-10)->major=3(minor=15-20)->major=3(minor=25-30) ->major=258(minor=10-30)
就是一个插入排序。
这里专门说一下
cd->next = *cp;
*cp = cd;
是如何进行链表插入的?
chrdevs数组元素就是指针,这里的cp是双层指针,&chardevs[i],chardevs[i]->next指向的就是一个char_device_struct结构体,而cp取得就是next指针的地址,所以这里就是在为next指针赋值
下面看一下cdev_add的实现,代码如下
err = cdev_add(cdev, MKDEV(cd->major, 0), 256);
//MKDEV实现如下
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
//后20位是次设备号,前面是主设备号,sizeof(unsinged long)-20
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
//核心调用kobj_map,分析见下文
}
首先看一下kobj_map的调用
kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
其中cdev_map是一个全局变量,定义如下
static struct kobj_map *cdev_map;
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
函数具体实现如下:
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
//表示设备号范围(dev, dev+range)中不同的主设备号的个数。通常n的值为1。
unsigned index = MAJOR(dev);
//主设备号
unsigned i;
struct probe *p;
if (n > 255)
n = 255;
//主设备号最大也就是255
p = kmalloc(sizeof(struct probe) * n, GFP_KERNEL);
//开辟主设备数量的struct probe
if (p == NULL)
return -ENOMEM;
for (i = 0; i < n; i++, p++) {
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;
p->range = range;
p->data = data;
}
//进行struct probe初始化
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++) {
//kobj_map中的probes数组中每个元素为一个struct probe链表的头指针。
struct probe **s = &domain->probes[index % 255];
//每个链表中的probe对象有(MAJOR(probe.dev) % 255)值相同的关系
//若主设备号小于255, 则每个链表中的probe都有相同的主设备号。
while (*s && (*s)->range < range)
s = &(*s)->next;
p->next = *s;
*s = p;
//链表中的元素是按照range值从小到大排列的。while循环即是找出该将p插入的位置。
}
//针对本例就是在全局kobj_map:cdev_map中添加对应major的struct probe结构
mutex_unlock(domain->lock);
return 0;
}
总的来说就是在两个全局变量struct char_device_struct *chrdevs[255] 和struct kobj_map *cdev_map中添加该major对应的结构体
usb_register_usbfs、usb_devio_init、usbfs_init
int usb_hub_init(void)
{
if (usb_register(&hub_driver) < 0) {
//注册usb接口驱动:hub_driver,见下文
printk(KERN_ERR "%s: can't register hub driver\n",
usbcore_name);
return -1;
}
khubd_task = kthread_run(hub_thread, NULL, "khubd");
//创建线程,名字为khubd,执行hub_thread函数,见下文
if (!IS_ERR(khubd_task))
return 0;
/* Fall through if kernel_thread failed */
usb_deregister(&hub_driver);
printk(KERN_ERR "%s: can't start khubd\n", usbcore_name);
return -1;
}
首先分析一下usb_register函数,就是用来注册接口驱动的,相关实现如下:
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
//hub的usb_driver接口驱动定义
static inline int usb_register(struct usb_driver *driver)
{
return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}
//真正的实现如下
int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
const char *mod_name)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_driver->drvwrap.for_devices = 0;
new_driver->drvwrap.driver.name = (char *) new_driver->name;
new_driver->drvwrap.driver.bus = &usb_bus_type;
new_driver->drvwrap.driver.probe = usb_probe_interface;
new_driver->drvwrap.driver.remove = usb_unbind_interface;
new_driver->drvwrap.driver.owner = owner;
new_driver->drvwrap.driver.mod_name = mod_name;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
//初始化struct usb_driver成员,主要是struct usbdrv_wrap中的device_driver
retval = driver_register(&new_driver->drvwrap.driver);
//通用的驱动注册函数,见下文
if (!retval) {
pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
usbfs_update_special();
usb_create_newid_file(new_driver);
} else {
printk(KERN_ERR "%s: error %d registering interface "
" driver %s\n",
usbcore_name, retval, new_driver->name);
}
return retval;
}
下面分析一下通用的驱动注册函数
int driver_register(struct device_driver * drv)
{
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
klist_init(&drv->klist_devices, NULL, NULL);
//初始化device_driver的klist_devices链表,用于添加匹配的设备
return bus_add_driver(drv);
//在总线中添加驱动,见下文
}
int bus_add_driver(struct device_driver *drv)
{
struct bus_type * bus = bus_get(drv->bus);
//获取所属总线
int error = 0;
if (!bus)
return -EINVAL;
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
//设置驱动内核对象的名字
if (error)
goto out_put_bus;
drv->kobj.kset = &bus->drivers;
//设置驱动内核对象所属kset,bus_type->drivers(kset类型)
error = kobject_register(&drv->kobj);
//依次调用kobject_init、kobject_register和kobject_uevent
//核心实现为,kobject初始化
//kobject->parent=所属kset.kobj(kobject类型)
//kobject添加到所属kset的list中
//sysfs操作:在parent下添加kobject文件夹
if (error)
goto out_put_bus;
if (drv->bus->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
//如果所属bus支持自动检测,则调用driver_attach进行驱动匹配,见下文
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
//将struct device_driver添加到bus_type->klist_drivers链表中
module_add_driver(drv->owner, drv);
//将驱动模块添加到系统模块系统中
error = driver_create_file(drv, &driver_attr_uevent);
//sysfs操作:创建驱动对象的属性文件uevent
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__FUNCTION__, drv->name);
}
error = driver_add_attrs(bus, drv);
//sysfs操作:遍历bus_type->drv_attrs[]数组,创建驱动对象的属性文件
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__FUNCTION__, drv->name);
}
error = add_bind_files(drv);
//sysfs操作:创建驱动对象的属性文件bind和unbind
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__FUNCTION__, drv->name);
}
return error;
out_unregister:
kobject_unregister(&drv->kobj);
out_put_bus:
bus_put(bus);
return error;
}
driver_attach实现如下:
int driver_attach(struct device_driver * drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
int bus_for_each_dev(struct bus_type * bus, struct device * start,
void * data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device * dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->klist_devices, &i,
(start ? &start->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
//就是遍历bus_type->klist_devices中每一个device,调用__driver_attach函数,如下
static int __driver_attach(struct device * dev, void * data)
{
struct device_driver * drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem);
if (!dev->driver)
driver_probe_device(drv, dev);
//核心实现,见下文
up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0;
}
int driver_probe_device(struct device_driver * drv, struct device * dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
if (drv->bus->match && !drv->bus->match(dev, drv))
goto done;
//如果所属总线有match函数,则调用match函数进行驱动和设备的匹配
//本例就是调用usb_bus_type的match函数usb_device_match,前文分析过
pr_debug("%s: Matched Device %s with Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
ret = really_probe(dev, drv);
//检测函数,详见下文
done:
return ret;
}
总的来说driver_attach函数就是遍历bus_type的klist_devices中每个device,调用bus_type的match函数(本例usb_device_match)进行驱动和设备的匹配
really_probe函数实现如下:
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("%s: Probing driver %s with device %s\n",
drv->bus->name, drv->name, dev->bus_id);
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
//设置sturct device->driver指针
if (driver_sysfs_add(dev)) {
//sysfs建立对象和驱动链接
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__FUNCTION__, dev->bus_id);
goto probe_failed;
}
if (dev->bus->probe) {
//如果总线存在probe函数,则调用总线的probe函数
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
//如果总线没有probe函数,但是驱动有probe函数,就调用驱动的probe函数
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
//本例中usb_bus_type没有probe函数,那就是调用驱动的probe函数hub_probe
driver_bound(dev);
//将struct device添加到匹配的struct device_driver->klist_devices中
ret = 1;
pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev->bus_id, ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
hub_thread函数实现如下:
static int hub_thread(void *__unused)
{
set_freezable();
do {
hub_events();
wait_event_freezable(khubd_wait,
!list_empty(&hub_event_list) ||
kthread_should_stop());
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
pr_debug("%s: khubd exiting\n", usbcore_name);
return 0;
}
这里不进行深入分析,总述功能:其中的hub_events()函数处理hub上面发生的事件。wait_event_freezable会在那边等待hub总线上面的事件,如果一直没有事件则函数不返回。hub_thread第一次执行时会第一次调用hub_events,发现无事可做,执行空,退出去执行wait_event_freezable函数,该函数等待usb总线上的事件,如usb设备插入、拔出等。没有事件不返回。
调用如下,其中usb_generic_driver之前已经简述过了,是一个来者不拒的设备驱动
usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
};
下面看一下usb_register_device_driver的实现,可以类比上文中usb_register_driver的实现
int usb_register_device_driver(struct usb_device_driver *new_udriver,
struct module *owner)
{
int retval = 0;
if (usb_disabled())
return -ENODEV;
new_udriver->drvwrap.for_devices = 1;
new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
new_udriver->drvwrap.driver.bus = &usb_bus_type;
new_udriver->drvwrap.driver.probe = usb_probe_device;
new_udriver->drvwrap.driver.remove = usb_unbind_device;
new_udriver->drvwrap.driver.owner = owner;
//初始化usb_device_driver中成员,主要是struct usbdrv_wrap的driver成员
retval = driver_register(&new_udriver->drvwrap.driver);
//调用通用的驱动注册函数,实现见上文3.3.6
if (!retval) {
pr_info("%s: registered new device driver %s\n",
usbcore_name, new_udriver->name);
usbfs_update_special();
} else {
printk(KERN_ERR "%s: error %d registering device "
" driver %s\n",
usbcore_name, retval, new_udriver->name);
}
return retval;
}