5.USB驱动--二:USB总线驱动程序的流程
想大概看 USB 总线驱动程序的工作过程:(所做的事件)
USB 总线驱动程序的作用:(从代码里分析)
1. 识别 USB 设备。
1.1 分配地址。(刚上来时就用默认地址 0 。)
1.2 并告诉 USB 设备(set address)。
1.3 发出命令获取描述符。
描述符的信息可以在 include\linux\usb\Ch9.h 看到。
2. 查找并安装对应的设备驱动程序
3. 提供 USB 读写函数
开始分析USB总线驱动,如何识别USB设备:
把 USB 设备接到开发板上,看输出信息:
usb 1-1: new full speed USB device using s3c2410-ohci and address 2
usb 1-1: configuration #1 chosen from 1 choice
scsi0 : SCSI emulation for USB Mass Storage devices (刚接上 USB 设备时弹出信
息)
scsi 0:0:0:0: Direct-Access HTC Android Phone 0100 PQ: 0 ANSI: 2
sd 0:0:0:0: [sda] Attached SCSI removable disk (安装驱动程序)
接着拔掉:
usb 1-1: USB disconnect, address 2
再接上:
usb 1-1: new full speed USB device using s3c2410-ohci and address 3
usb 1-1: configuration #1 chosen from 1 choice
scsi1 : SCSI emulation for USB Mass Storage devices (刚接上 USB 设备时弹出信息)
scsi 1:0:0:0: Direct-Access HTC Android Phone 0100 PQ: 0 ANSI: 2
sd 1:0:0:0: [sda] Attached SCSI removable disk (这是在安装驱动程序)
分析这些信息:
1: new full speed USB device using s3c2410-ohci and address 2
新的 全速 USB 设备 使用“s3c2410-ohci”和分配的地址 是2.
2.
在内核驱动目录下搜:
grep "USB device using" * -nR
drivers/usb/core/hub.c:2186: "%s %s speed %sUSB device using %s and
address %d\n
找到第一段话是位于drivers/usb/core/hub.c的第2186行
它又是被谁调用的,如下图所示,我们搜索到它是通过hub_thread()函数调用的
问:hub_thread 线程平时是休眠的,是谁唤醒的?
答:hub_thread在khubd_wait队列中休眠,通过查找“khubd_wait”队列是谁唤醒的,可找到kick_khubd ==》wake_up(&khubd_wait);(kick“踢”相当于把这个“khubd”线程踢醒)。
继续搜索kick_khubd,发现被hub_irq()函数中调用。
显然,就是当USB设备插入后,D+或D-就会被拉高,然后USB主机控制器就会产生一个hub_irq中断.
总结:
从“hub_port_connect_change()”开始分析如下的过程:
a. 分配地址。(刚上来时就用默认地址 0 。)
b. 并告诉 USB 设备(set address)。
c. 发出命令获取描述符
1.我们进入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个设备.
2.我们再来看看hub_port_connect_change()->hub_port_init()函数是如何来实现连接USB设备的
static int hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
{
... ...
for (j = 0; j < SET_ADDRESS_TRIES; ++j)
{
retval = hub_set_address(udev); //(1)设置地址,告诉USB设备新的地址编号
if (retval >= 0)
break;
msleep(200);
}
retval = usb_get_device_descriptor(udev, 8); //(2)获得USB设备描述符前8个字节
... ...
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE); //重新获取设备描述符信息
... ...
}
(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()重读一次目标设备的设备描述结构.
3.我们来看看hub_port_connect_change()->usb_new_device()函数是如何来创建USB设备的
int usb_new_device(struct usb_device *udev)
{
... ...
err = usb_get_configuration(udev); //(1)获取配置描述块
... ...
err = device_add(&udev->dev); // (2)把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(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); //最后在解析每个配置块
}
... ...
}
(2)其中device_add ()函数如下
int usb_get_configuration(struct usb_device *dev) {
dev = get_device(dev); //使dev等于usb_device下的device成员 ... ...
if ((error = bus_add_device(dev))) // 把这个设备添加到dev->bus的device表中
goto BusError; ... ...
bus_attach_device(dev); //来匹配对应的驱动程序 ... ... }
当bus_attach_device()函数匹配成功,就会调用驱动的probe函数
4.我们再来看看usb_bus_type这个的成员usb_device_match函数,看看是如何匹配的
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
if (is_usb_device(dev)) { //判断是不是USB设备
if (!is_usb_device_driver(drv))
return 0;
return 1;
}
else { //否则就是USB驱动或者USB设备的接口
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
if (is_usb_device_driver(drv)) //如果是USB驱动,就不需要匹配,直接return
return 0;
intf = to_usb_interface(dev); //获取USB设备的接口
usb_drv = to_usb_driver(drv); //获取USB驱动
id = usb_match_id(intf, usb_drv->id_table); //匹配USB驱动的成员id_table
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
```显然就是匹配USB驱动的id_table
总结:
下一篇: linux 字符设备驱动模板