欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

5.USB驱动--二:USB总线驱动程序的流程

程序员文章站 2022-07-14 10:54:00
...

想大概看 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()函数调用的
5.USB驱动--二:USB总线驱动程序的流程
问: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中断.
总结:
5.USB驱动--二:USB总线驱动程序的流程

从“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函数,看看是如何匹配的
5.USB驱动--二:USB总线驱动程序的流程

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

总结:
5.USB驱动--二:USB总线驱动程序的流程