Linux-USB总线驱动分析
2017-10-06 18:43
393 查看
为什么一插上就有会提示信息?
是因为windows自带了USB总线驱动程序,
USB总线驱动程序负责:
识别USB设备,给USB设备找到对应的驱动程序
新接入的USB设备的默认地址(编号)是0,在未分配新编号前,PC主机使用0地址和它通信。
然后USB总线驱动程序都会给它分配一个地址(编号)
PC机想访问USB总线上某个USB设备时,发出的命令都含有对应的地址(编号)
USB是一种主从结构。主机叫做Host,从机叫做Device,所有的USB传输,都是从USB主机这方发起;USB设备没有"主动"通知USB主机的能力。
例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动地等得PC机来读。
USB可以热插拔的硬件原理
在USB集线器(hub)的每个下游端口的D+和D-上,分别接了一个15K欧姆的下拉电阻到地。这样,在集线器的端口悬空时,就被这两个下拉电阻拉到了低电平。
而在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和高速设备,上拉电阻是接在D+上;而低速设备则是上拉电阻接在D-上。这样,当设备插入到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的一条拉高了。集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上一层的集线器报告给USB主控制器),这样就检测到设备的插入了。USB高速设备先是被识别为全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到高速模式的。在高速模式下,是电流传输模式,这时将D+上的上拉电阻断开。
USB的4大传输类型:
控制传输
是每一个USB设备必须支持的,通常用来获取设备描述符、设置设备的状态等等。一个USB设备从插入到最后的拔出这个过程一定会产生控制传输(即便这个USB设备不能被这个系统支持)。
中断传输
支持中断传输的典型设备有USB鼠标、 USB键盘等等。中断传输不是说我的设备真正发出一个中断,然后主机会来读取数据。它其实是一种轮询的方式来完成数据的通信。USB设备会在设备驱动程序中设置一个参数叫做interval,它是endpoint的一个成员。 interval是间隔时间的意思,表示我这个设备希望主机多长时间来轮询自己,只要这个值确定了之后,我主机就会周期性的来查看有没有数据需要处理
批量传输
支持批量传输最典型的设备就是U盘,它进行大数量的数据传输,能够保证数据的准确性,但是时间不是固定的。
实时传输(也叫同步传输)
USB摄像头就是实时传输设备的典型代表,它同样进行大数量的数据传输,数据的准确性无法保证,但是对传输延迟非常敏感,也就是说对实时性要求比较高
USB端点:
USB设备与主机会有若干个通信的”端点”,每个端点都有个端点号,除了端点0外,每一个端点只能工作在一种传输类型(控制传输、中断传输、批量传输、实时传输)下,一个传输方向下
传输方向都是基于USB主机的立场说的,
比如:鼠标的数据是从鼠标传到PC机, 对应的端点称为"中断输入端点"
其中端点0是设备的默认控制端点, 既能输出也能输入,用于USB设备的识别过程
同样linux内核也自带了USB总线驱动程序,框架如下:
要想成为一个USB主机,硬件上就必须要有USB主机控制器才行,USB主机控制器又分为4种接口:
OHCI(Open Host Controller Interface): 微软主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),OHCI接口的软件简单,硬件复杂
UHCI(Universal Host Controller Interface): Intel主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps), 而UHCI接口的软件复杂,硬件简单
EHCI(Enhanced Host Controller Interface):高速USB2.0(480Mbps),
xHCI(eXtensible Host Controller Interface):USB3.0(5.0Gbps),采用了9针脚设计,同时也支持USB2.0、1.1等
接下来进入正题,开始分析USB总线驱动,如何识别USB设备
由于内核自带了USB驱动,所以我们先插入一个USB键盘到开发板上看打印信息
发现以下字段:
如下图,找到第一段话是位于drivers/usb/core/hub.c的第2186行
这个hub其实就是我们的USB主机控制器的集线器,用来管理多个USB接口
1. drivers/usb/core/hub.c的第2186行位于hub_port_init()函数里
它又是被谁调用的,如下图所示,我们搜索到它是通过hub_thread()函数调用的
hub_thread()函数如下:
复制代码
static int hub_thread(void *__unused)
{
do {
hub_events(); //执行一次hub事件函数
wait_event_interruptible(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());
//(1).每次执行一次hub事件,都会进入一次等待事件中断函数
try_to_freeze();
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
pr_debug("%s: khubd exiting\n", usbcore_name);
return 0;
}
复制代码
从上面函数中得到, 要想执行hub_events(),都要等待khubd_wait这个中断唤醒才行
2.我们搜索”khubd_wait”,看看是被谁唤醒
找到该中断在kick_khubd()函数中唤醒,代码如下:
复制代码
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
spin_lock_irqsave(&hub_event_lock, flags);
if (list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait); //唤醒khubd_wait这个中断
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
复制代码
3.继续搜索kick_khubd,发现被hub_irq()函数中调用
显然,就是当USB设备插入后,D+或D-就会被拉高,然后USB主机控制器就会产生一个hub_irq中断.
4.接下来我们直接分析hub_port_connect_change()函数,如何连接端口的
复制代码
static void hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)
{
... ...
udev = usb_alloc_dev(hdev, hdev->bus, port1); //(1)注册一个usb_device,然后会放在usb总线上
usb_set_device_state(udev, USB_STATE_POWERED); //设置注册的USB设备的状态标志
... ...
choose_address(udev); //(2)给新的设备分配一个地址编号
status = hub_port_init(hub, udev, port1, i); //(3)初始化端口,与USB设备建立连接
... ...
status = usb_new_device(udev); //(4)创建USB设备,与USB设备驱动连接
... ...
}
复制代码
所以最终流程图如下:
5.我们进入hub_port_connect_change()->usb_alloc_dev(),来看看它是怎么放在usb总线上
复制代码
1 usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
2
3 {
4
5 struct usb_device *dev;
6
7
8
9 dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配一个usb_device设备结构体
10
11 ... ...
12
13
14
15 device_initialize(&dev->dev); //初始化usb_device
16
17 dev->dev.bus = &usb_bus_type; //(1)放在usb_bus总线上
18
19 dev->dev.type = &usb_device_type; //放在usb_bus总线上的device目录下
20
21 ... ...
22
23 return dev; //返回一个usb_device结构体
24
25 }
复制代码
(1)在第17行上的usb_bus_type是一个全局变量, 它和我们之前学的platform平台总线相似,属于USB总线, 是Linux中bus的一种.
如下图所示,每当创建一个USB设备,或者USB设备驱动时,USB总线都会调用match成员来匹配一次,使USB设备和USB设备驱动联系起来.
usb_bus_type结构体如下:
复制代码
struct bus_type usb_bus_type = {
.name = "usb", //总线名称,存在/sys/bus下
.match = usb_device_match, //匹配函数,匹配成功就会调用usb_driver驱动的probe函数成员
.uevent = usb_uevent, //事件函数
.suspend = usb_suspend, //休眠函数
.resume = usb_resume, //唤醒函数
};
复制代码
6.我们进入hub_port_connect_change()->choose_address(),来看看它是怎么分配地址编号的
复制代码
static void choose_address(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,bus->devnum_next);
//在bus->devnum_next~128区间中,循环查找下一个非0(没有设备)的编号
if (devnum >= 128) //若编号大于等于128,说明没有找到空余的地址编号,从头开始找
devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); //设置下次寻址的区间+1
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap); //设置位
udev->devnum = devnum;
}
}
复制代码
从上面代码中分析到每次的地址编号是连续加的,USB接口最大能接127个设备,我们连续插拔两次USB键盘,也可以看出,如下图所示:
7.我们再来看看hub_port_connect_change()->hub_port_init()函数是如何来实现连接USB设备的
复制代码
1 static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
2 {
3 ... ...
4 for (j = 0; j < SET_ADDRESS_TRIES; ++j)
5 {
6 retval = hub_set_address(udev); //(1)设置地址,告诉USB设备新的地址编号
7
8 if (retval >= 0)
9 break;
10 msleep(200);
11 }
12 retval = usb_get_device_descriptor(udev, 8); //(2)获得USB设备描述符前8个字节
13 ... ...
14
15 retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); //重新获取设备描述符信息
16 ... ...
17 }
复制代码
(1)上面第6行中,hub_set_address()函数主要是用来告诉USB设备新的地址编号, hub_set_address()函数如下:
复制代码
static int hub_set_address(struct usb_device *udev)
{
int retval;
... ...
retval = usb_control_msg(udev, usb_sndaddr0pipe(),USB_REQ_SET_ADDRESS,0, udev->devnum, 0,NULL, 0, USB_CTRL_SET_TIMEOUT);
//(1.1)等待传输完成
if (retval == 0) { //设置新的地址,传输完成,返回0
usb_set_device_state(udev, USB_STATE_ADDRESS); //设置状态标志
ep0_reinit(udev);
}
return retval;
}
复制代码
usb_control_msg()函数就是用来让USB主机控制器把一个控制报文发给USB设备,如果传输完成就返回0.其中参数udev表示目标设备;使用的管道为usb_sndaddr0pipe(),也就是默认的地址0加上控制端点号0; USB_REQ_SET_ADDRESS表示命令码,既设置地址; udev->devnum表示要设置目标设备的设备号;允许等待传输完成的时间为5秒,因为USB_CTRL_SET_TIMEOUT定义为5000。
2)上面第12行中,usb_get_device_descriptor()函数主要是获取目标设备描述符前8个字节,为什么先只开始读取8个字节?是因为开始时还不知道对方所支持的信包容量,这8个字节是每个设备都有的,后面再根据设备的数据,通过usb_get_device_descriptor()重读一次目标设备的设备描述结构.
其中USB设备描述符结构体如下所示:
复制代码
struct usb_device_descriptor {
__u8 bLength; //本描述符的size
__u8 bDescriptorType; //描述符的类型,这里是设备描述符DEVICE
__u16 bcdUSB; //指明usb的版本,比如usb2.0
__u8 bDeviceClass; //类
__u8 bDeviceSubClass; //子类
__u8 bDeviceProtocol; //指定协议
__u8 bMaxPacketSize0; //端点0对应的最大包大小
__u16 idVendor; //厂家ID
__u16 idProduct; //产品ID
__u16 bcdDevice; //设备的发布号
__u8 iManufacturer; //字符串描述符中厂家ID的索引
__u8 iProduct; //字符串描述符中产品ID的索引
__u8 iSerialNumber; //字符串描述符中设备序列号的索引
__u8 bNumConfigurations; //可能的配置的数目
} __attribute__ ((packed));
复制代码
8.我们来看看hub_port_connect_change()->usb_new_device()函数是如何来创建USB设备的
复制代码
int usb_new_device(struct usb_device *udev)
{
... ...
err = usb_get_www.kinyl.com configuration(udev); //(1)获取配置描述块
... ...
err = device_add(&udev->dev); // 把device放入bus的dev链表中,并寻找对应的设备驱动
}
复制代码
(1)其中usb_get_configuration()函数如下,就是获取各个配置
复制代码
int usb_get_configuration(struct usb_device *dev)
{
... ...
/* USB_MAXCONFIG 定义为8,表示设备描述块下有最多不能超过8个配置描述块 */
/*ncfg表示 设备描述块下 有多少个配置描述块 */
if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, "
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
... ...
for (cfgno = 0; cfgno < ncfg; cfgno++) //for循环,从USB设备里依次读入所有配置描述块
{
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE);
//每次先读取USB_DT_CONFIG_SIZE个字节,也就是9个字节,暂放到buffer中
... ...
length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);
//通过wTotalLength,知道实际数据大小
bigbuffer = kmalloc(length, GFP_KERNEL); //然后再来分配足够大的空间
... ...
result = usb_get_descriptor(www.wmyld11.cn dev, USB_DT_CONFIG, cfgno,bigbuffer, length);
//在调用一次usb_get_descriptor,把整个配置描述块读出来,放到bigbuffer中
... ...
dev->rawdescriptors[cfgno] = bigbuffer; //再将bigbuffer地址放在rawdescriptors所指的指针数组中
result = usb_parse_configuration(&dev->dev, cfgno,&dev->config[cfgno],
bigbuffer, length); //最后在解析每个配置块
是因为windows自带了USB总线驱动程序,
USB总线驱动程序负责:
识别USB设备,给USB设备找到对应的驱动程序
新接入的USB设备的默认地址(编号)是0,在未分配新编号前,PC主机使用0地址和它通信。
然后USB总线驱动程序都会给它分配一个地址(编号)
PC机想访问USB总线上某个USB设备时,发出的命令都含有对应的地址(编号)
USB是一种主从结构。主机叫做Host,从机叫做Device,所有的USB传输,都是从USB主机这方发起;USB设备没有"主动"通知USB主机的能力。
例子:USB鼠标滑动一下立刻产生数据,但是它没有能力通知PC机来读数据,只能被动地等得PC机来读。
USB可以热插拔的硬件原理
在USB集线器(hub)的每个下游端口的D+和D-上,分别接了一个15K欧姆的下拉电阻到地。这样,在集线器的端口悬空时,就被这两个下拉电阻拉到了低电平。
而在USB设备端,在D+或者D-上接了1.5K欧姆上拉电阻。对于全速和高速设备,上拉电阻是接在D+上;而低速设备则是上拉电阻接在D-上。这样,当设备插入到集线器时,由1.5K的上拉电阻和15K的下拉电阻分压,结果就将差分数据线中的一条拉高了。集线器检测到这个状态后,它就报告给USB主控制器(或者通过它上一层的集线器报告给USB主控制器),这样就检测到设备的插入了。USB高速设备先是被识别为全速设备,然后通过HOST和DEVICE两者之间的确认,再切换到高速模式的。在高速模式下,是电流传输模式,这时将D+上的上拉电阻断开。
USB的4大传输类型:
控制传输
是每一个USB设备必须支持的,通常用来获取设备描述符、设置设备的状态等等。一个USB设备从插入到最后的拔出这个过程一定会产生控制传输(即便这个USB设备不能被这个系统支持)。
中断传输
支持中断传输的典型设备有USB鼠标、 USB键盘等等。中断传输不是说我的设备真正发出一个中断,然后主机会来读取数据。它其实是一种轮询的方式来完成数据的通信。USB设备会在设备驱动程序中设置一个参数叫做interval,它是endpoint的一个成员。 interval是间隔时间的意思,表示我这个设备希望主机多长时间来轮询自己,只要这个值确定了之后,我主机就会周期性的来查看有没有数据需要处理
批量传输
支持批量传输最典型的设备就是U盘,它进行大数量的数据传输,能够保证数据的准确性,但是时间不是固定的。
实时传输(也叫同步传输)
USB摄像头就是实时传输设备的典型代表,它同样进行大数量的数据传输,数据的准确性无法保证,但是对传输延迟非常敏感,也就是说对实时性要求比较高
USB端点:
USB设备与主机会有若干个通信的”端点”,每个端点都有个端点号,除了端点0外,每一个端点只能工作在一种传输类型(控制传输、中断传输、批量传输、实时传输)下,一个传输方向下
传输方向都是基于USB主机的立场说的,
比如:鼠标的数据是从鼠标传到PC机, 对应的端点称为"中断输入端点"
其中端点0是设备的默认控制端点, 既能输出也能输入,用于USB设备的识别过程
同样linux内核也自带了USB总线驱动程序,框架如下:
要想成为一个USB主机,硬件上就必须要有USB主机控制器才行,USB主机控制器又分为4种接口:
OHCI(Open Host Controller Interface): 微软主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps),OHCI接口的软件简单,硬件复杂
UHCI(Universal Host Controller Interface): Intel主导的低速USB1.0(1.5Mbps)和全速USB1.1(12Mbps), 而UHCI接口的软件复杂,硬件简单
EHCI(Enhanced Host Controller Interface):高速USB2.0(480Mbps),
xHCI(eXtensible Host Controller Interface):USB3.0(5.0Gbps),采用了9针脚设计,同时也支持USB2.0、1.1等
接下来进入正题,开始分析USB总线驱动,如何识别USB设备
由于内核自带了USB驱动,所以我们先插入一个USB键盘到开发板上看打印信息
发现以下字段:
如下图,找到第一段话是位于drivers/usb/core/hub.c的第2186行
这个hub其实就是我们的USB主机控制器的集线器,用来管理多个USB接口
1. drivers/usb/core/hub.c的第2186行位于hub_port_init()函数里
它又是被谁调用的,如下图所示,我们搜索到它是通过hub_thread()函数调用的
hub_thread()函数如下:
复制代码
static int hub_thread(void *__unused)
{
do {
hub_events(); //执行一次hub事件函数
wait_event_interruptible(khubd_wait,!list_empty(&hub_event_list) ||kthread_should_stop());
//(1).每次执行一次hub事件,都会进入一次等待事件中断函数
try_to_freeze();
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
pr_debug("%s: khubd exiting\n", usbcore_name);
return 0;
}
复制代码
从上面函数中得到, 要想执行hub_events(),都要等待khubd_wait这个中断唤醒才行
2.我们搜索”khubd_wait”,看看是被谁唤醒
找到该中断在kick_khubd()函数中唤醒,代码如下:
复制代码
static void kick_khubd(struct usb_hub *hub)
{
unsigned long flags;
to_usb_interface(hub->intfdev)->pm_usage_cnt = 1;
spin_lock_irqsave(&hub_event_lock, flags);
if (list_empty(&hub->event_list)) {
list_add_tail(&hub->event_list, &hub_event_list);
wake_up(&khubd_wait); //唤醒khubd_wait这个中断
}
spin_unlock_irqrestore(&hub_event_lock, flags);
}
复制代码
3.继续搜索kick_khubd,发现被hub_irq()函数中调用
显然,就是当USB设备插入后,D+或D-就会被拉高,然后USB主机控制器就会产生一个hub_irq中断.
4.接下来我们直接分析hub_port_connect_change()函数,如何连接端口的
复制代码
static void hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)
{
... ...
udev = usb_alloc_dev(hdev, hdev->bus, port1); //(1)注册一个usb_device,然后会放在usb总线上
usb_set_device_state(udev, USB_STATE_POWERED); //设置注册的USB设备的状态标志
... ...
choose_address(udev); //(2)给新的设备分配一个地址编号
status = hub_port_init(hub, udev, port1, i); //(3)初始化端口,与USB设备建立连接
... ...
status = usb_new_device(udev); //(4)创建USB设备,与USB设备驱动连接
... ...
}
复制代码
所以最终流程图如下:
5.我们进入hub_port_connect_change()->usb_alloc_dev(),来看看它是怎么放在usb总线上
复制代码
1 usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
2
3 {
4
5 struct usb_device *dev;
6
7
8
9 dev = kzalloc(sizeof(*dev), GFP_KERNEL); //分配一个usb_device设备结构体
10
11 ... ...
12
13
14
15 device_initialize(&dev->dev); //初始化usb_device
16
17 dev->dev.bus = &usb_bus_type; //(1)放在usb_bus总线上
18
19 dev->dev.type = &usb_device_type; //放在usb_bus总线上的device目录下
20
21 ... ...
22
23 return dev; //返回一个usb_device结构体
24
25 }
复制代码
(1)在第17行上的usb_bus_type是一个全局变量, 它和我们之前学的platform平台总线相似,属于USB总线, 是Linux中bus的一种.
如下图所示,每当创建一个USB设备,或者USB设备驱动时,USB总线都会调用match成员来匹配一次,使USB设备和USB设备驱动联系起来.
usb_bus_type结构体如下:
复制代码
struct bus_type usb_bus_type = {
.name = "usb", //总线名称,存在/sys/bus下
.match = usb_device_match, //匹配函数,匹配成功就会调用usb_driver驱动的probe函数成员
.uevent = usb_uevent, //事件函数
.suspend = usb_suspend, //休眠函数
.resume = usb_resume, //唤醒函数
};
复制代码
6.我们进入hub_port_connect_change()->choose_address(),来看看它是怎么分配地址编号的
复制代码
static void choose_address(struct usb_device *udev)
{
int devnum;
struct usb_bus *bus = udev->bus;
devnum = find_next_zero_bit(bus->devmap.devicemap, 128,bus->devnum_next);
//在bus->devnum_next~128区间中,循环查找下一个非0(没有设备)的编号
if (devnum >= 128) //若编号大于等于128,说明没有找到空余的地址编号,从头开始找
devnum = find_next_zero_bit(bus->devmap.devicemap, 128, 1);
bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1); //设置下次寻址的区间+1
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap); //设置位
udev->devnum = devnum;
}
}
复制代码
从上面代码中分析到每次的地址编号是连续加的,USB接口最大能接127个设备,我们连续插拔两次USB键盘,也可以看出,如下图所示:
7.我们再来看看hub_port_connect_change()->hub_port_init()函数是如何来实现连接USB设备的
复制代码
1 static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
2 {
3 ... ...
4 for (j = 0; j < SET_ADDRESS_TRIES; ++j)
5 {
6 retval = hub_set_address(udev); //(1)设置地址,告诉USB设备新的地址编号
7
8 if (retval >= 0)
9 break;
10 msleep(200);
11 }
12 retval = usb_get_device_descriptor(udev, 8); //(2)获得USB设备描述符前8个字节
13 ... ...
14
15 retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); //重新获取设备描述符信息
16 ... ...
17 }
复制代码
(1)上面第6行中,hub_set_address()函数主要是用来告诉USB设备新的地址编号, hub_set_address()函数如下:
复制代码
static int hub_set_address(struct usb_device *udev)
{
int retval;
... ...
retval = usb_control_msg(udev, usb_sndaddr0pipe(),USB_REQ_SET_ADDRESS,0, udev->devnum, 0,NULL, 0, USB_CTRL_SET_TIMEOUT);
//(1.1)等待传输完成
if (retval == 0) { //设置新的地址,传输完成,返回0
usb_set_device_state(udev, USB_STATE_ADDRESS); //设置状态标志
ep0_reinit(udev);
}
return retval;
}
复制代码
usb_control_msg()函数就是用来让USB主机控制器把一个控制报文发给USB设备,如果传输完成就返回0.其中参数udev表示目标设备;使用的管道为usb_sndaddr0pipe(),也就是默认的地址0加上控制端点号0; USB_REQ_SET_ADDRESS表示命令码,既设置地址; udev->devnum表示要设置目标设备的设备号;允许等待传输完成的时间为5秒,因为USB_CTRL_SET_TIMEOUT定义为5000。
2)上面第12行中,usb_get_device_descriptor()函数主要是获取目标设备描述符前8个字节,为什么先只开始读取8个字节?是因为开始时还不知道对方所支持的信包容量,这8个字节是每个设备都有的,后面再根据设备的数据,通过usb_get_device_descriptor()重读一次目标设备的设备描述结构.
其中USB设备描述符结构体如下所示:
复制代码
struct usb_device_descriptor {
__u8 bLength; //本描述符的size
__u8 bDescriptorType; //描述符的类型,这里是设备描述符DEVICE
__u16 bcdUSB; //指明usb的版本,比如usb2.0
__u8 bDeviceClass; //类
__u8 bDeviceSubClass; //子类
__u8 bDeviceProtocol; //指定协议
__u8 bMaxPacketSize0; //端点0对应的最大包大小
__u16 idVendor; //厂家ID
__u16 idProduct; //产品ID
__u16 bcdDevice; //设备的发布号
__u8 iManufacturer; //字符串描述符中厂家ID的索引
__u8 iProduct; //字符串描述符中产品ID的索引
__u8 iSerialNumber; //字符串描述符中设备序列号的索引
__u8 bNumConfigurations; //可能的配置的数目
} __attribute__ ((packed));
复制代码
8.我们来看看hub_port_connect_change()->usb_new_device()函数是如何来创建USB设备的
复制代码
int usb_new_device(struct usb_device *udev)
{
... ...
err = usb_get_www.kinyl.com configuration(udev); //(1)获取配置描述块
... ...
err = device_add(&udev->dev); // 把device放入bus的dev链表中,并寻找对应的设备驱动
}
复制代码
(1)其中usb_get_configuration()函数如下,就是获取各个配置
复制代码
int usb_get_configuration(struct usb_device *dev)
{
... ...
/* USB_MAXCONFIG 定义为8,表示设备描述块下有最多不能超过8个配置描述块 */
/*ncfg表示 设备描述块下 有多少个配置描述块 */
if (ncfg > USB_MAXCONFIG) {
dev_warn(ddev, "too many configurations: %d, "
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
... ...
for (cfgno = 0; cfgno < ncfg; cfgno++) //for循环,从USB设备里依次读入所有配置描述块
{
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,buffer, USB_DT_CONFIG_SIZE);
//每次先读取USB_DT_CONFIG_SIZE个字节,也就是9个字节,暂放到buffer中
... ...
length = max((int) le16_to_cpu(desc->wTotalLength),USB_DT_CONFIG_SIZE);
//通过wTotalLength,知道实际数据大小
bigbuffer = kmalloc(length, GFP_KERNEL); //然后再来分配足够大的空间
... ...
result = usb_get_descriptor(www.wmyld11.cn dev, USB_DT_CONFIG, cfgno,bigbuffer, length);
//在调用一次usb_get_descriptor,把整个配置描述块读出来,放到bigbuffer中
... ...
dev->rawdescriptors[cfgno] = bigbuffer; //再将bigbuffer地址放在rawdescriptors所指的指针数组中
result = usb_parse_configuration(&dev->dev, cfgno,&dev->config[cfgno],
bigbuffer, length); //最后在解析每个配置块
相关文章推荐
- Linux-USB总线驱动分析
- Linux-USB总线驱动分析
- Linux-USB总线驱动分析
- Linux下的USB总线驱动-u盘驱动分析(程序过程分析)
- Linux-USB总线驱动分析
- 学习笔记 --- LINUX USB总线驱动框架分析
- Linux-USB总线驱动分析
- Linux-USB总线驱动分析
- Linux-USB总线驱动分析
- Linux-USB总线驱动分析
- Linux-USB总线驱动分析
- linux下的总线设备驱动模型分析
- linux设备驱动之USB主机控制器驱动分析
- Linux中IIC总线驱动分析
- Linux-USB驱动(1)-USB总线介绍
- Linux-USB驱动(3)-USB协议深入分析
- Linux设备驱动---OMAP3630 Linux I2C总线驱动分析(1)
- OMAP3630 Linux I2C总线驱动分析
- linux IDE驱动分析之IDE总线、驱动注册(三)
- 《Linux总线、设备与驱动》ldd3中demo分析