USB 协议Audio应用

usb作为目前最为通用的接口,为提高产品用户体验,减小产品的设计复杂度,立下了悍马功劳,但是也因其通用的特性,其相对于其他接口,协议更为复杂,同时也在不断发生演进,充实目前的协议规范,带宽有了很大的提升,同时尺寸也在不断缩小。

最近因为使用usb 进行音频数据的采集,对usb 协议做了一次系统性了解,同时以usb audio class 协议作为范本,进一步了解usb 协议规范。

USB 连接拓扑结构

usb 拓扑结构可以简单描述为主从模式,进一步扩展,可通过usb Hub扩展为树状结构。

Host<==>Slave

USB 速度

我们选择USB,其中很重要一个评价指标即是其速度是否能达到我们的应用需求。目前在Audio应用中,主要还是以USB2.0为主,这边以USB 2.0为例:

  • High Speed - 480Mbits/s

High speed data is clocked at 480.00Mb/s with a data signalling tolerance of ± 500ppm.

  • Full Speed - 12Mbits/s
    Full speed data is clocked at 12.000Mb/s with a data signalling tolerance of ±0.25% or 2,500ppm.

  • Low Speed - 1.5Mbits/s
    Low speed data is clocked at 1.50Mb/s with a data signalling tolerance of ±1.5% or 15,000ppm.

Slave 所支持的速度模式,Host 端可通过硬件电路的上拉电压进行识别,具体如图所示:

这里写代码片

USB packet 类型

Token packet

三种类型的token,其中in和out指示数据方向,setup指示配置的开始

  • In:slave->host
  • out: host->slave
  • setup:control transfer begin

Data packet

  • Data 0
  • Data 1

Handshake Packet

  • ACK
  • NAK
  • STALL

SOF (start of frame)packet

An packet exchange example:
这里写图片描述

这里写图片描述

USB 描述符

以下是USB 描述符的层级结构,如图所示:

描述符的作用在于Slave向Host 声明设备的属性和接口以及相应参数,在linux 端通过lsusb command 我们可以看到一个usb 设备的完整描述符结构。

usb设备在pc 上也是挂载在pci 总线上,因此也服从pci设备的地址标示方式。

查找pc 端所有usb 设备
>>lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 095: ID 0483:3748 STMicroelectronics ST-LINK/V2
Bus 001 Device 005: ID 1a40:0101 Terminus Technology Inc. Hub
Bus 001 Device 096: ID 0483:5734 STMicroelectronics 
Bus 001 Device 003: ID 413c:301a Dell Computer Corp. 
Bus 001 Device 002: ID 413c:2107 Dell Computer Corp. 
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

指定目标设备,查看描述符信息
>> lsusb -s 096 -v 
Bus 001 Device 096: ID 0483:5734 STMicroelectronics 
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x0483 STMicroelectronics
  idProduct          0x5734 
  bcdDevice            2.00
  iManufacturer           1 
  iProduct                2 
  iSerial                 3 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength          129
    bNumInterfaces          3
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0xa0
      (Bus Powered)
      Remote Wakeup
    MaxPower              200mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0  
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass         1 Audio
      bInterfaceSubClass      1 Control Device
      bInterfaceProtocol      0 
      iInterface              0 
      AudioControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      1 (HEADER)
        bcdADC               1.00
        wTotalLength           41
        bInCollection           1  //total 1 audio stream interface
        baInterfaceNr( 0)       1  //audio stream id 1 belongs to audio control interface
      AudioControl Interface Descriptor:
        bLength                12
        bDescriptorType        36  
        bDescriptorSubtype      2 (INPUT_TERMINAL)
        bTerminalID             4
        wTerminalType      0x0205 Microphone Array
        bAssocTerminal          0
        bNrChannels             4  // 4 channels
        wChannelConfig     0x0003  
          Left Front (L)
          Right Front (R)
        iChannelNames           0 
        iTerminal               0 
      AudioControl Interface Descriptor:
        bLength                11
        bDescriptorType        36
        bDescriptorSubtype      6 (FEATURE_UNIT)
        bUnitID                 5
        bSourceID               4  //point to input terminal id 4
        bControlSize            2
        bmaControls( 0)      0x00
        bmaControls( 0)      0x01
          Bass Boost Control
        bmaControls( 1)      0x00
        bmaControls( 1)      0x02
          Loudness Control
        iFeature                0 
      AudioControl Interface Descriptor:
        bLength                 9
        bDescriptorType        36
        bDescriptorSubtype      3 (OUTPUT_TERMINAL)
        bTerminalID             6  
        wTerminalType      0x0101 USB Streaming
        bAssocTerminal          0
        bSourceID               5  //point to unit id 5
        iTerminal               0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       1
      bNumEndpoints           1
      bInterfaceClass         1 Audio
      bInterfaceSubClass      2 Streaming
      bInterfaceProtocol      0 
      iInterface              0 
      AudioStreaming Interface Descriptor:
        bLength                 7
        bDescriptorType        36
        bDescriptorSubtype      1 (AS_GENERAL)
        bTerminalLink           6
        bDelay                  0 frames
        wFormatTag              1 PCM
      AudioStreaming Interface Descriptor:
        bLength                11
        bDescriptorType        36
        bDescriptorSubtype      2 (FORMAT_TYPE)
        bFormatType             1 (FORMAT_TYPE_I)
        bNrChannels             4
        bSubframeSize           2
        bBitResolution         16
        bSamFreqType            1 Discrete
        tSamFreq[ 0]        16000
      Endpoint Descriptor:
        bLength                 9
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes           13
          Transfer Type            Isochronous
          Synch Type               Synchronous
          Usage Type               Data
        wMaxPacketSize     0x0200  1x 512 bytes
        bInterval               1
        bRefresh                0
        bSynchAddress           0
        AudioControl Endpoint Descriptor:
          bLength                 7
          bDescriptorType        37
          bDescriptorSubtype      1 (EP_GENERAL)
          bmAttributes         0x00
          bLockDelayUnits         0 Undefined
          wLockDelay              0 Undefined
	  
	  
	  Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        2
      bAlternateSetting       0
      bNumEndpoints           0
      bInterfaceClass       254 Application Specific Interface
      bInterfaceSubClass      1 Device Firmware Update
      bInterfaceProtocol      2 
      iInterface              6 
      Device Firmware Upgrade Interface Descriptor:
        bLength                             9
        bDescriptorType                    33
        bmAttributes                       11
          Will Detach
          Manifestation Intolerant
          Upload Supported
          Download Supported
        wDetachTimeout                    255 milliseconds
        wTransferSize                    1024 bytes
        bcdDFUVersion                   1.1a

IAD(interface associated descriptor)

通过描述符,我们可以声明设备的所从属的类信息,如果一个设备只有单一功能,比如单纯的audio 设备,那描述符只需要包含audio class 类信息即可。但是如果一个设备既是audio 设备,又是 Hid 设备,那么其就是一个复合设备(composite device),对于从属于一个类中的多个interface,需要借助于IAD进行表示,以下的一幅图很好的描述了iad 的作用。

这里写图片描述

USB protocol Framework

USB 协议栈框架中需要了解的几个重要概念:

  • Interface
  • Terminal
  • Endpoint

如描述符所述,其中包含interface, terminal和endpoint 的声明,这三类都是逻辑上的接口,那么三者是何种关系,为什么需要这样声明。

interface 所描述的是slave开放给host的一个几口,此接口可以包含若干个terminal,其中in-terminal 和out-terminal 成对出现,其标示数据流的方向,最后terminal与endpoint对接。

下图描述了一个麦克风的定义,数据流通过IT(in terminal)进入interface所定义的audio function,通过OT(out terminal)流出,最后送到endpoint In 节点。

这里写图片描述

其中还有一个数据流方向问题需要明确:
如图所示,endpoint In 和out方向是以host为参考系,即In 为host Tx data,out为host Rx data,而terminal的方向是以slave 端数据流的方向为参考系。

这里写图片描述

代码示例

以stm32 usb protocol 的代码为例, 描述在usb slave端数据处理流程

数据处理callback 函数注册
USBD_DCD_INT_cb_TypeDef USBD_DCD_INT_cb = 
{
  USBD_DataOutStage,
  USBD_DataInStage,
  USBD_SetupStage,
  USBD_SOF,
  USBD_Reset,
  USBD_Suspend,
  USBD_Resume,   
};

所有stage的处理在USB interrupt 来的时候在driver 层通过函数:CTR() 进行处理:

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Correct Transfer interrupt's service
  * @param  None
  * @retval None
  */
void CTR(void)
{
...
 else
 {
    /* DIR = 1 */
    
    /* DIR = 1 & CTR_RX       => SETUP or OUT int */
    /* DIR = 1 & (CTR_TX | CTR_RX) => 2 int pending */
    ep = &((&USB_Device_dev)->dev.out_ep[0]);
    wEPVal = _GetENDPOINT(ENDP0);
    
    if ((wEPVal &EP_SETUP) != 0)
    {
      /* Get SETUP Packet*/
      ep->xfer_count = GetEPRxCount(ep->num);
      PMAToUserBufferCopy(&((&USB_Device_dev)->dev.setup_packet[0]),ep->pmaadress , ep->xfer_count);       
      /* SETUP bit kept frozen while CTR_RX = 1*/ 
      _ClearEP_CTR_RX(ENDP0); 
      
      /* Process SETUP Packet*/
      USBD_DCD_INT_fops->SetupStage(&USB_Device_dev);
    }
	else if ((wEPVal & EP_CTR_RX) != 0)
    {
       ...
       
       if (ep->xfer_count != 0)
       {
         PMAToUserBufferCopy(ep->xfer_buff, ep->pmaadress, ep->xfer_count);
         ep->xfer_buff+=ep->xfer_count;
       }
       
       /* Process Control Data OUT Packet*/
       USBD_DCD_INT_fops->DataOutStage(&USB_Device_dev, 0x00);
       
      ...
     }
     ...
 }
...
}

setup stage

其中setupstage 的处理如下:

/**
  * @brief  USBD_SetupStage 
  *         Handle the setup stage
  * @param  pdev: device instance
  * @retval status
  */
static uint8_t USBD_SetupStage(USB_CORE_HANDLE *pdev)
{
  USB_SETUP_REQ req;
  
  USBD_ParseSetupRequest(pdev , &req);
  
  switch (req.bmRequest & 0x1F) 
  {
  case USB_REQ_RECIPIENT_DEVICE:   
    USBD_StdDevReq (pdev, &req);
    break;
    
  case USB_REQ_RECIPIENT_INTERFACE:     
    USBD_StdItfReq(pdev, &req);
    break;
    
  case USB_REQ_RECIPIENT_ENDPOINT:        
    USBD_StdEPReq(pdev, &req);     // config ep
    break;
    
  default:           
    DCD_EP_Stall(pdev , req.bmRequest & 0x80);
    break;
  }  
  return USBD_OK;
}

/* AUDIO interface class callbacks structure */
USBD_Class_cb_TypeDef  AUDIO_cb =
{
  usbd_audio_Init,
  usbd_audio_DeInit,
  usbd_audio_Setup,
  EP0_TxSent,  // 
  NULL,//usbd_audio_EP0_RxReady, // 
  usbd_audio_DataIn,
  usbd_audio_DataOut,
  usbd_audio_SOF,
  USBD_audio_GetCfgDesc,
  USBD_DFU_GetUsrStringDesc,
};

USB Audio Class

USB Audio 设备最为常见的是microphone和Speaker,

USB CLOCK Model

  • Sample clock:Data source sample rate
  • Bus Clock:1KHz on full-speed and 8KHz on high-speed
  • Service Clock: Can be regard as the rate of application layer software

Isochronous Type

UAC 设备的同步类型通过端点描述符Bit Map进行指定:
这里写图片描述

三种等时传输模式的区别是:

  • Async

Asynchronous isochronous audio endpoints produce or consume data at a rate that is locked either to a
clock external to the USB or to a free-running internal clock. These endpoints cannot be synchronized to a
start of frame (SOF) or to any other clock in the USB domain.

  • Sync

The clock system of synchronous isochronous audio endpoints can be controlled externally through SOF
synchronization. Such an endpoint must do one of the following:
· Slave its sample clock to the 1ms SOF tick.
· Control the rate of USB SOF generation so that its data rate becomes automatically locked to SOF.

  • Adaptive

Adaptive isochronous audio endpoints are able to source or sink data at any rate within their operating
range. This implies that these endpoints must run an internal process that allows them to match their
natural data rate to the data rate that is imposed at their interface.

这里写图片描述

Linux kernel sound USB

这里写图片描述

1.8. How to do isochronous (ISO) transfers?
For ISO transfers you have to fill a usb_iso_packet_descriptor
structure, allocated at the end of the URB by
usb_alloc_urb(n,mem_flags), for each packet you want to schedule.
You also have to set urb->interval to say how often to make transfers;
it’s often one per frame (which is once every microframe for highspeed
devices). The actual interval used will be a power of two that’s no
bigger than what you specify.

The usb_submit_urb() call modifies urb->interval to the implemented
interval value that is less than or equal to the requested interval
value. If ISO_ASAP scheduling is used, urb->start_frame is also
updated.

For each entry you have to specify the data offset for this frame
(base is transfer_buffer), and the length you want to write/expect to
read. After completion, actual_length contains the actual transferred
length and status contains the resulting status for the ISO transfer
for this frame. It is allowed to specify a varying length from frame
to frame (e.g. for audio synchronisation/adaptive transfer rates). You
can also use the length 0 to omit one or more frames (striping).

For scheduling you can choose your own start frame or ISO_ASAP. As
explained earlier, if you always keep at least one URB queued and your
completion keeps (re)submitting a later URB, you’ll get smooth ISO
streaming (if usb bandwidth utilization allows).

If you specify your own start frame, make sure it’s several frames in
advance of the current frame. You might want this model if you’re
synchronizing ISO data with some other event stream.

这里写图片描述

这里写图片描述
/kernel/sound/usb/card.h

struct audioformat {
	struct list_head list;
	u64 formats;			/* ALSA format bits */
	unsigned int channels;		/* # channels */
	unsigned int fmt_type;		/* USB audio format type (1-3) */
	unsigned int frame_size;	/* samples per frame for non-audio */
	int iface;			/* interface number */
	unsigned char altsetting;	/* corresponding alternate setting */
	unsigned char altset_idx;	/* array index of altenate setting */
	unsigned char attributes;	/* corresponding attributes of cs endpoint */
	unsigned char endpoint;		/* endpoint */
	unsigned char ep_attr;		/* endpoint attributes */
	unsigned char datainterval;	/* log_2 of data packet interval */
	unsigned char protocol;		/* UAC_VERSION_1/2 */
	unsigned int maxpacksize;	/* max. packet size */
	unsigned int rates;		/* rate bitmasks */
	unsigned int rate_min, rate_max;	/* min/max rates */
	unsigned int nr_rates;		/* number of rate table entries */
	unsigned int *rate_table;	/* rate table */
	unsigned char clock;		/* associated clock */
	struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */
	bool dsd_dop;			/* add DOP headers in case of DSD samples */
	bool dsd_bitrev;		/* reverse the bits of each DSD sample */
};

/kernel/sound/usb/usbaudio.h

/*
 *
 */

struct snd_usb_audio {
	int index;
	struct usb_device *dev;
	struct snd_card *card;
	struct usb_interface *pm_intf;
	u32 usb_id;
	struct mutex mutex;
	unsigned int autosuspended:1;	
	atomic_t active;
	atomic_t shutdown;
	atomic_t usage_count;
	wait_queue_head_t shutdown_wait;
	unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
	unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
	
	int num_interfaces;
	int num_suspended_intf;

	struct list_head pcm_list;	/* list of pcm streams */
	struct list_head ep_list;	/* list of audio-related endpoints */
	int pcm_devs;

	struct list_head midi_list;	/* list of midi interfaces */

	struct list_head mixer_list;	/* list of mixer interfaces */

	int setup;			/* from the 'device_setup' module param */
	bool autoclock;			/* from the 'autoclock' module param */

	struct usb_host_interface *ctrl_intf;	/* the audio control interface */
};

/sound/usb/endpoint.c

/*
 * Prepare a CAPTURE or SYNC urb for submission to the bus.
 */
static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
				       struct snd_urb_ctx *urb_ctx)
{
	int i, offs;
	struct urb *urb = urb_ctx->urb;

	urb->dev = ep->chip->dev; /* we need to set this at each time */

	switch (ep->type) {
	case SND_USB_ENDPOINT_TYPE_DATA:
		offs = 0;
		for (i = 0; i < urb_ctx->packets; i++) {
			urb->iso_frame_desc[i].offset = offs;
			urb->iso_frame_desc[i].length = ep->curpacksize;
			offs += ep->curpacksize;
		}

		urb->transfer_buffer_length = offs;
		urb->number_of_packets = urb_ctx->packets;
		break;

	case SND_USB_ENDPOINT_TYPE_SYNC:
		urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
		urb->iso_frame_desc[0].offset = 0;
		break;
	}
}

URB 的传输流程图:
这里写图片描述

urb 传输在内核中的调用流程

urb传输的代码分析
如需引用,请注明出处blog.csdn.net/zkami 作者ZhengKui

分配一个urb,并初始化之。返回这个urb的指针
usb_alloc_urb(int iso_packets, gfp_t mem_flags) (core/message.c)
    ->urb = kmalloc(...);  分配一个urb
    ->usb_init_urb(urb); 初始化这个urb:初始化个字段为0,增加引用计数

根据传输类型,填写urb的一些字段(usb.h)
static inline void usb_fill_control_urb (struct urb *urb,
                     struct usb_device *dev,
                     unsigned int pipe,
                     unsigned char *setup_packet,
                     void *transfer_buffer,
                     int buffer_length,
                     usb_complete_t complete_fn,
                     void *context)
static inline void usb_fill_int_urb (struct urb *urb,
                     struct usb_device *dev,
                     unsigned int pipe,
                     void *transfer_buffer,
                     int buffer_length,
                     usb_complete_t complete_fn,
                     void *context,
                     int interval)
static inline void usb_fill_bulk_urb (struct urb *urb,
                      struct usb_device *dev,
                      unsigned int pipe,
                      void *transfer_buffer,
                      int buffer_length,
                      usb_complete_t complete_fn,
                      void *context)
相 同:对于ctl/int/bulk这三种传输类型,在fill urb时都需要填充dev,pipe,transfer_buffer,transfer_buffer_length,complete, context 字段。其中pipe代表当前urb传输的管道,transfer_buffer
代表当前urb传输的数据的起始地址,transfer_buffer_length是当前urb传输的数据长度,complete是当前urb处理完后调用的回调函数。
不同:fill control urb时需要fill setup_packet字段,它指向一个setup包的起始地址
fill int urb时要根据传输速度来fill interval字段

提交urb。发出一个异步的传输请求,完成后将调用回调函数。在调用usb_submit_urb函数前必须正确的初始化urb, 最后urb的控制将返回给发出申请的dev driver。
usb_submit_urb(struct urb *urb, gfp_t mem_flags) (core/urb.c)
    ->ep = (usb_pipein(urb->pipe) ? dev->ep_in : dev->ep_out)[usb_pipeendpoint(urb->pipe)];
      根据pipe得到urb要连接到哪个ep的list中
    ->xfertype = usb_endpoint_type(&ep->desc);
      得到端点类型,并根据不同的类型进行设置,如填充urb的transfer_flags字段。
      如果是ISO传输,根据iso packet的数量(urb->number_of_packets),初始化每一个packet。     (urb->iso_frame_desc[n])
      如果是iso/int传输,根据端点速度类型设置urb->interval
    ->usb_hcd_submit_urb(urb, mem_flags)    (core/hcd.c)
      将提交的urb指派给合适的host controller driver,这里的HC遵守OHCI规范
        ->rh_urb_enqueue(hcd, urb); 如果是Root Hub,调用该函数
            ->rh_queue_status (hcd, urb); 如果是中断传输
                ->usb_hcd_link_urb_to_ep(hcd, urb); 把urb挂到ep上
                ->mod_timer(); 修改rh_timer polling的时间
            ->rh_call_control (hcd, urb); 如果是控制传输
                ->usb_hcd_link_urb_to_ep(hcd, urb); 把urb挂到ep上
                  然后根据不同的standard request(ch9.h) Setup Packet的request域来确定len
                ->ohci_hub_control(hcd,    typeReq, wValue, wIndex,tbuf, wLength)
                  通过操作根Hub寄存器来完成上层对根Hub发送的命令
                ->usb_hcd_unlink_urb_from_ep(hcd, urb); 出错的话把urb从ep上脱链
                ->usb_hcd_giveback_urb(hcd, urb, status); urb处理完后调用回调函数
        ->ohci_urb_enqueue(hcd, urb, mem_flags)    如果不是Root Hub,调用该函数
            -> ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval))) ohci-q.c
               如果ep上挂有ed,直接返回。如果没有,则用参数pipe、interval以及ep.desc加工一个ed,将其挂在ep上并返回
                -> ed_alloc (ohci, GFP_ATOMIC); (ohci-mem.c)分配一个struct ed
                -> td_alloc (ohci, GFP_ATOMIC); (ohci-mem.c)分配一个struct td
                -> ed_free(ohci, ed);     释放struct ed
                -> usb_calc_bus_time() 计算传输一个拥有最大字节数的数据包所需要的时间(ms)
                然后:设定info的各个域,(info其实就是OHCI Spec中定义的Endpoint Descriptor的Dword 0
                详见P16 OHCI spec figure4-1)再将info赋给ed->hwInfo
               然后: 根据端点的类型确定size(size决定该端点上挂载的td的数目,除实时端点上的td外,
                 其它端点上的td能够装载4K的数据)
               接着: 给urb_priv_t分配空间,并为其上所挂载的td指针数组分配空间

-> td_alloc (ohci, mem_flags); (ohci-mem.c)  分配ed中的每一个td
            -> usb_hcd_link_urb_to_ep(hcd, urb);    urb挂到ep的urb_list上
            -> ed_schedule (ohci, ed);
               根据ed的类型将ed插入到HC相应队列中,并读/写HC的寄存器
                -> balance (ohci, ed->interval, ed->load);
                -> periodic_link(ohci, ed);
            -> usb_hcd_unlink_urb_from_ep(hcd, urb);
            -> td_submit_urb (ohci, urb);    将urb需要发送的数据安排到相应ed下的td队列中
                -> td_fill(ohci, info, data, 4096, urb, cnt);

message.c
usb_interrupt_msg(...) 事实上调用的是usb_bulk_msg()
    ->usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout)
        -> usb_alloc_urb(0, GFP_KERNEL) 分配一个urb
        -> usb_fill_int_urb(urb, usb_dev, pipe, data, len,
                usb_api_blocking_completion, NULL,
                ep->desc.bInterval); 如果是int msg
        -> usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
                usb_api_blocking_completion, NULL); 如果是bulk msg
        -> usb_start_wait_urb(urb, timeout, actual_length);
               提交urb并等待完成或超时。将urb提交给usb core后就停在wait_for_completion_timeout()等待
           当这个urb完成后,会调用usb_api_blocking_completion()进而调用complete来通知不用再等了。
            ->usb_submit_urb(urb, GFP_NOIO) 提交urb
            ->wait_for_completion_timeout(&ctx.done, expire)) 等待。其中expire是等待的时间限
              &ctx.done是等到了的话,调用的回调函数
            ->usb_kill_urb(urb) 如果超时就kill这个urb
usb_control_msg(...)
    ->struct usb_ctrlrequest *dr = kmalloc(...)
        首先创建一个usb_ctrlrequest的数据结构(详见usb2.0 spec ch9),并初始化bRequestType,bRequest,wValue
      wIndex,wLength字段
    ->usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
        ->usb_alloc_urb(0, GFP_NOIO); 分配一个urb
        ->usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
                 len, usb_api_blocking_completion, NULL);
          填充这个ctl urb, usb_api_blocking_completion是回调函数
        ->usb_start_wait_urb(urb, timeout, &length);
              提交urb并等待完成或超时。将urb提交给usb core后就停在wait_for_completion_timeout()等待
          当这个urb完成后,会调用usb_api_blocking_completion()进而调用complete来通知不用再等了。
            ->usb_submit_urb(urb, GFP_NOIO) 提交urb
            ->wait_for_completion_timeout(&ctx.done, expire)) 等待。其中expire是等待的时间限
              &ctx.done是等到了的话,调用的回调函数
            ->usb_kill_urb(urb) 如果超时就kill这个urb

Reference:

  1. http://www.erji.net/forum.phpmod=viewthread&tid=1987631&extra=

猜你喜欢

转载自blog.csdn.net/huntershuai/article/details/79732248