UDC驱动的接口都定义在drivers/usb/gadget/udc/core.c文件中。USB Function驱动通过调用这些接口匹配及访问USB设备控制器,而底层USB控制器驱动要实现这些接口定义的功能。下面分析一下主要的UDC驱动接口调用流程。
Composite层通过调用UDC core层的usb_udc_attach_driver
和usb_gadget_probe_driver
接口将Function驱动和UDC驱动绑定。前者通过UDC设备的名称匹配,通常是configfs配置的USB Function驱动使用,后者直接匹配udc_list
链表中的第一个UDC驱动,通常是legacy类型的USB Function驱动使用,如g_audio驱动。使用usb_gadget_unregister_driver
函数解除Function驱动和UDC驱动的绑定。具体的绑定过程,在分析具体的Function驱动时说明。
[drivers/usb/gadget/udc/core.c]
int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver);
int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
底层的USB设备控制器驱动(dwc3 gadget)使用usb_add_gadget_udc
函数将自身加入到Core UDC Framework中,使用usb_del_gadget_udc
从Core UDC Framework删除。加入过程在UDC驱动初始化的时候已经分析过了,这里不再赘述。
[include/linux/usb/gadget.h]
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
void usb_del_gadget_udc(struct usb_gadget *gadget);
下面两个函数用来开启和停止UDC,Function驱动和UDC驱动绑定的时候通过调用usb_udc_attach_driver
或usb_gadget_probe_driver
间接调用usb_gadget_udc_start
开启UDC,Function驱动和UDC驱动解除绑定的时候通过调用usb_gadget_unregister_driver
间接调用usb_gadget_udc_stop
停止UDC。
[drivers/usb/gadget/udc/core.c]
static inline int usb_gadget_udc_start(struct usb_udc *udc);
static inline void usb_gadget_udc_stop(struct usb_udc *udc);
[include/linux/usb/gadget.h]
int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver);
int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
USB Function驱动和UDC绑定成功后,通过调用usb_gadget_udc_start
接口开启USB设备控制器,接收主机发送的请求,其内部调用的dwc3设备控制器驱动的dwc3_gadget_start
函数。主要的工作是设置相关参数并使能USB设备控制器,具体如下:
usb_gadget_driver
结构体地址保存到dwc3
结构体中。legacy Function驱动使用的usb_gadget_driver
结构体是composite_driver_template
,configfs Function驱动使用的usb_gadget_driver
结构体是configfs_driver_template
。DWC3_DEPCMD_DEPSTARTCFG
(初始化的时候需要发送此命令),所有端点设置端点传输资源配置命令DWC3_DEPCMD_SETTRANSFRESOURCE
。向端点发送命令需要通过USB3_DEPnCMD
、USB3_DEPnCMDPAR0
、USB3_DEPnCMDPAR1
、USB3_DEPnCMDPAR2
寄存器,第一个寄存器保存命令,后面三个寄存器保存命令的参数。向端点发送命令都是通过dwc3_send_gadget_ep_cmd
函数完成,后面会经常看到。DWC3_DEPCFG_XFER_COMPLETE_EN
和传输未准备好事件DWC3_DEPCFG_XFER_NOT_READY_EN
。dep->number
。dep->number >> 1
,控制端点的输入和输出端点其FIFO编号相同,其他输出端点FIFO编号为0。DWC3_DEPCMD_SETEPCONFIG
命令设置。ctrl_req_addr
地址,最后向控制器发送开始传输命令DWC3_DEPCMD_STARTTRANSFER
,参数为TRB自身的DMA地址。USB Function驱动和UDC解除绑定成功后会调用usb_gadget_udc_stop
函数停止USB设备控制器。主要的工作是清除USB控制器上的USB请求,然后关闭端点,具体如下:
DWC3_DEPCMD_ENDTRANSFER
。端点0的端点描述符定义在dwc3的设备控制器驱动中,Function驱动无需定义,具体如下。
[drivers/usb/dwc3/gadget.c]
/* 端点0的描述符在dwc3 gadget驱动中定义 */
static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE, // 端点描述符长度
.bDescriptorType = USB_DT_ENDPOINT, // 描述符类型为端点描述符
.bmAttributes = USB_ENDPOINT_XFER_CONTROL, // 端点0使用控制传输
};
从上面的分析中可以看出,软件通过向USB设备控制器端点USB3_DEPnCMD
寄存器写入命令来驱动USB端点工作,命令可以携带参数,参数写到USB3_DEPnCMDPAR1
、USB3_DEPnCMDPAR2
、USB3_DEPnCMDPAR3
寄存器。命令的类型由USB3_DEPnCMD
寄存器的bit[0:3]位决定,bit[10]控制命令是否执行,软件设置为1则端点开始执行命令,执行完毕,端点自动清零,软件可以根据此位判断端点是否执行完命令。USB3_DEPnCMD
寄存器的低11bit意义如下图所示。在执行命令之前,需要提前把命令参数写到参数寄存器中,若不需要参数,则写入0即可。
下面是参数寄存器USB3_DEPnCMDPAR0
、USB3_DEPnCMDPAR1
、USB3_DEPnCMDPAR2
的具体意义。
位域 | 说明 |
---|---|
31:30 | 配置行为。 0-初始化端点状态,用于第一次配置端点 1-恢复端点状态,用于休眠唤醒后恢复端点状态,需要恢复的状态保存到DEPCMDPAR2寄存器中 2-修改端点状态,用于修改已经配置好的端点配置,例如修改DEPEVTEN使能位、interrupt number、MaxPacketSize。 |
29:26 | Reserved |
25:22 | 设置突发传输数值Burst Size,即一个微帧内传输多少包数据,只有USB3.0及以上支持此特性。 0:Burst length = 1 1:Burst length = 2… 15:Burst length = 16 |
21:17 | 分配给此端点的FIFO编号。对于控制端点,输出端点和输入端点的值应该相等,即ep0_in和ep0_out的FIFO编号相等。对于输出端点此值为0。尽管在DRD模式中TxFIFOs数量超过16个,但在设备模式中必须使用较低的16个。 |
16:14 | Reserved |
13:3 | 输入输出端点支持的最大包长。USB3.0支持的最大包长为1024字节。 |
2:1 | 端点类型 2’b00: Control 2’b01: Isochronous 2’b10: Bulk 2’b11: Interrupt |
0 | Reserved |
位域 | 说明 |
---|---|
31 | FIFO-based |
30 | Reserved |
29:25 | 设置USB端点编号。 bit[29:26]: Endpoint number bit[25]: Endpoint direction,0-OUT,1-IN 物理端点0必须分配给控制器输出端点 物理端点1必须分配给控制器输入端点 |
24 | Stream的能力,指出此端点具有流传输功能(MaxStreams != 0) |
23:16 | 设置bInterval的值,端点描述符中设置了该值(写入寄存器的值为真实bInterval-1)。即轮训数据传送端点的时间间隔,对于批量传送和控制传送的端点忽略,对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255。dwc3控制器支持的有效值范围为0-13 |
15 | 如果bulk端点使用了 External Buffer Control (EBC)模式 |
14 | 如果bit15设置了该位才能设置,否则不要设置此位。 1:控制器不会更新TRB的HWO位域 0:控制器会更新TRB的HWO位域 |
13:8 | 使能设备端点的特殊事件DEPEVTEN,写入0关闭。 Bit 13: Stream Event Enable (StreamEvtEn) Bit 12: Reserved Bit 11: Reserved Bit 10: XferNotReady Enable (XferNRdyEn) Bit 9: XferInProgress Enable (XferInProgEn) Bit 8: XferComplete Enable (XferCmplEn) |
7:5 | Reserved |
4:0 | 输入和输出端点的中断编号。指示此端点产生的与端点相关的中断的中断/事件缓冲区号。如果没有使能multiple Interrupter配置,则此位域必须设置为0 |
位域 | 说明 |
---|---|
31:0 | 当USB3_DEPnCMDPAR0寄存器的bit[31:30]为1时,该寄存器中保存的是需要恢复端点状态的值,否则为Reserved |
USB Function驱动通过调用usb_ep_enable
打开除端点0以外的端点。端点0打开逻辑由dwc3驱动自身处理,不需要USB Function驱动直接参与。
[include/linux/usb/gadget.h]
int usb_ep_enable(struct usb_ep *ep);
usb_ep_enable
函数的工作流程如下图所示,和usb_gadget_udc_start
函数一样,最终通过调用__dwc3_gadget_ep_enable
实现。__dwc3_gadget_ep_enable
函数的执行流程见2.2.1.节,下面主要说不同的地方:
DWC3_DEPCFG_XFER_IN_PROGRESS_EN
事件trb_dequeue
和trb_enqueue
清零,接着将trb_pool
全部清零,最后设置link TRB,即trb_pool
最后一个TRB中保存第一个TRB的地址,并且设置DWC3_TRBCTL_LINK_TRB
和DWC3_TRB_CTRL_HWO
标志。true
。USB Function驱动通过调用usb_ep_disable
关闭除端点0以外的端点。端点0关闭逻辑由dwc3驱动自身处理,不需要USB Function驱动直接参与。
[include/linux/usb/gadget.h]
int usb_ep_disable(struct usb_ep *ep);
usb_ep_disable
函数的工作流程如下图所示,和usb_gadget_udc_stop
函数一样,最终通过调用__dwc3_gadget_ep_disable
实现。__dwc3_gadget_ep_disable
函数的执行流程见2.2.1.节,最后设置端点状态关闭。
USB Function驱动可以通过gadget_find_ep_by_name
和usb_gadget_ep_match_desc
函数来匹配要使用的端点。前者实现较为简单,通过端点的名称进行匹配,使用的不多。后者通过端点描述符和USB3.0端点伴侣描述符进行匹配,使用较多,返回值为1时表示匹配成功,返回值为0表示匹配失败,其匹配的流程如下:
ep->claimed
字段为true说明该端点已经被使用,则直接返回0。maxpacket_limit
是否大于真实端点,若大于则返回0。[include/linux/usb/gadget.h]
// name为要匹配端点的名称
struct usb_ep *gadget_find_ep_by_name(
struct usb_gadget *g, const char *name);
// ep为要匹配端点的数据结构,desc匹配端点的端点描述符
// ep_comp匹配端点的USB3.0端点伴侣描述符
int usb_gadget_ep_match_desc(struct usb_gadget *gadget,
struct usb_ep *ep, struct usb_endpoint_descriptor *desc,
struct usb_ss_ep_comp_descriptor *ep_comp);
USB Function驱动通过调用usb_ep_alloc_request
和usb_ep_free_request
分配和释放USB I/O请求。
[include/linux/usb/gadget.h]
struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req);
dwc3所有端点都是通过dwc3_gadget_ep_alloc_request
分配USB请求,实质上分配的是dwc3_request
数据结构,内部包含了通用的USB请求数据结构usb_request
。
dwc3所有端点都是通过dwc3_gadget_ep_free_request
释放USB请求。
USB Function驱动通过调用usb_ep_queue
和usb_ep_dequeue
将USB I/O请求提交到发送队列和从发送队列取消。
[include/linux/usb/gadget.h]
int usb_ep_queue(struct usb_ep *ep,
struct usb_request *req, gfp_t gfp_flags);
int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req);
向端点0和非端点0提交USB请求,usb_ep_queue
内部调用的函数不同。端点0调用dwc3_gadget_ep0_queue
函数,非端点0调用dwc3_gadget_ep_queue
函数。
如下图所示,向端点0提交USB请求时,会调用dwc3 gadget驱动的dwc3_gadget_ep0_queue
函数,其主要的执行流程为:
pending_list
链表不为空,说明还有未完成的request,则直接退出。端点0的request传输完成后,在其回调函数中会将request从pending_list
移除。actual
、status
字段,保存端点0的编号,将request放入pending_list
链表。usb_gadget_map_request
映射request的缓冲区。dwc->ep0_trb[0]
。初始化的时候端点0分配了2个TRB。dwc3_ep0_start_trans
开始传输request,该函数前面已经介绍过了。usb_ep_queue
向非端点0提交USB请求的过程如下图所示,最终通过__dwc3_gadget_ep_queue
函数提交。下面分析一下向非端点0提交USB请求的主要工作内容:
actual
、status
、direction
、epnum
和length
等字段,对于输出端点,length
必须是MaxPacketSize的整数倍。kmalloc
等函数分配,DMA不能直接使用,需要进行DMA映射,具体的映射过程在2.7小结介绍。pending_list
链表。__dwc3_gadget_kick_transfer
函数驱动USB设备控制器发送USB请求。这里有几个特殊情况,图里面没有画出,下面简要说明一下。
__dwc3_gadget_kick_transfer
发送USB请求。dwc3_prepare_trbs
函数遍历pending_list
将TRB和request绑定,执行完后pending_list
链表上的request都会被放到started_list
链表上。该函数会根据DMA缓冲区的使用形式做不同的处理,若DMA支持Scatter-gather,则调用dwc3_prepare_one_trb_sg
函数,否则调用dwc3_prepare_one_trb_linear
函数,最终都是通过dwc3_prepare_one_trb
将TRB和request绑定。started_list
链表中获取一个USB请求。若端点空闲,将其绑定的TRB DMA地址设置到param0和param1寄存器中,命令设置为DWC3_DEPCMD_STARTTRANSFER
,开始传输。若端点忙碌,则将命令设置为DWC3_DEPCMD_UPDATETRANSFER
,只更新传输,则不发送本次的USB请求。端点是否忙碌通过DWC3_EP_BUSY
标志判断。上面说过了,TRB和request最终通过dwc3_prepare_one_trb
实现,该函数的执行过程如下图所示,主要的工作内容为:
trb_pool
获取一个空闲的TRB,trb_pool
的索引为dep->trb_enqueu
。started_list
链表上。dep->trb_enqueu
增大。端点0和非端点0取消USB请求的过程是一样的,最终都是通过调用dwc3 gadget驱动的dwc3_gadget_ep_dequeue
实现,其主要的工作内容如下:
pending_list
链表,查找要取消的request是否在该链表中,若在,则调用dwc3_gadget_giveback
函数回调request的回调函数,将其从pending_list
链表中删除。pending_list
链表,则遍历started_list
链表查找,若找不到,直接返回-EINVAL
错误,反之调用dwc3_stop_active_transfer
函数向控制发送DWC3_DEPCMD_ENDTRANSFER
命令停止传输,最后再调用dwc3_gadget_giveback
函数回调request的回调函数,将其从pending_list
链表中删除。dwc3驱动在发送USB请求的时候需要调用usb_gadget_map_request
映射缓冲区,获取缓冲区的DMA地址。USB请求发送完或dequeue USB请求的时候,需要调用usb_gadget_unmap_request
将缓冲区进行反向映射。
[include/linux/usb/gadget.h]
int usb_gadget_map_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
void usb_gadget_unmap_request(struct usb_gadget *gadget,
struct usb_request *req, int is_in);
usb_gadget_map_request
的执行流程如下图所示,dma_map_sg
适用于支持SG的DMA,dma_map_single
适用于不支持SG的DMA,底层映射的机制是相同的,这里只说明dma_map_single
。
dma_map_ops
,RK3399上使用swiotlb_dma_ops
。在drivers/iommu/rockchip-iommu.c文件中调用arch_setup_dma_ops
设置。DMA_BIDIRECTIONAL
(数据可双向移动)、DMA_TO_DEVICE
(数据传输到设备中)、DMA_FROM_DEVICE
(数据从设备中传出)其中之一。swiotlb_map_page
进行映射,将缓冲区地址转换为DMA地址,若不是coherent缓冲区,且是DMA_TO_DEVICE
方向,则需要clean cache,若是DMA_FROM_DEVICE
,则需要invalidate cache。这里只是简单的说明一下,关于DMA映射的代码,后面专门介绍。usb_gadget_unmap_request
的执行流程如下图所示,dma_unmap_sg
适用于支持SG的DMA,dma_unmap_single
适用于不支持SG的DMA,底层映射的机制是相同的,这里只说明dma_unmap_single
。
dma_map_ops
,rk3399上使用swiotlb_dma_ops
。在drivers/iommu/rockchip-iommu.c文件中调用arch_setup_dma_ops
设置。DMA_BIDIRECTIONAL
(数据可双向移动)、DMA_TO_DEVICE
(数据传输到设备中)、DMA_FROM_DEVICE
(数据从设备中传出)其中之一。DMA_FROM_DEVICE
,则需要invalidate cache,接着调用swiotlb_unmap_page
解除缓冲区映射。swiotlb_dma_ops
是arm64平台上使用软件实现的smmu/iommu,定义如下。
[arch/arm64/mm/dma-mapping.c]
void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
struct iommu_ops *iommu, bool coherent);
static struct dma_map_ops swiotlb_dma_ops = {
.alloc = __dma_alloc,
.free = __dma_free,
.mmap = __swiotlb_mmap,
.get_sgtable = __swiotlb_get_sgtable,
.map_page = __swiotlb_map_page,
.unmap_page = __swiotlb_unmap_page,
.map_sg = __swiotlb_map_sg_attrs,
.unmap_sg = __swiotlb_unmap_sg_attrs,
.sync_single_for_cpu = __swiotlb_sync_single_for_cpu,
.sync_single_for_device = __swiotlb_sync_single_for_device,
.sync_sg_for_cpu = __swiotlb_sync_sg_for_cpu,
.sync_sg_for_device = __swiotlb_sync_sg_for_device,
.dma_supported = swiotlb_dma_supported,
.mapping_error = swiotlb_dma_mapping_error,
};
当USB请求传输完成后,可通过usb_gadget_giveback_request
函数回调USB请求的回调函数。USB请求的回调函数一般由Function驱动设置。
[include/linux/usb/gadget.h]
void usb_gadget_giveback_request(struct usb_ep *ep, struct usb_request *req)
{
if (likely(req->status == 0))
usb_led_activity(USB_LED_EVENT_GADGET);
trace_usb_gadget_giveback_request(ep, req, 0);
req->complete(ep, req);
}
USB协议定义了一组联络代码,表明传输的状态。联络代码会在数据信息包或联络信息包中传输。联络代码有ACK、NAK、STALL、NYET和ERR。具体如下:
UDC驱动提供了设置端点状态的函数,如下所示。usb_ep_set_halt
可以将端点特性设置为Halt,此时端点将处于STALL状态,不发送数据也不接收数据,除非主机发送CLEAR_FEATURE请求。usb_ep_clear_halt
函数用于清除端点的Halt特性,此时端点将从STALL状态中恢复。usb_ep_set_wedge
将端点特性设置为Halt并且会忽略CLEAR_FEATURE请求,只有Function驱动才可以清除Halt特性。
[include/linux/usb/gadget.h]
int usb_ep_set_halt(struct usb_ep *ep);
int usb_ep_clear_halt(struct usb_ep *ep);
int usb_ep_set_wedge(struct usb_ep *ep);
端点0设置和清除halt特性的执行流程如下图所示。usb_ep_set_halt
和usb_ep_clear_halt
函数最终都会调到dwc3 gadget驱动的dwc3_gadget_ep0_set_halt
函数,当第二个参数value=1
时表示usb_ep_set_halt
,value=0
时表示usb_ep_clear_halt
。虽然上层区t分了se halt和clear halt,但对于端点0,底层都是进行stall and restart操作,流程如下:
DWC3_EP_ENABLED
。pending_list
上所有USB请求。EP0_SETUP_PHASE
,准备被主机枚举。非端点0设置和清除halt特性的执行流程如下图所示。usb_ep_set_halt
和usb_ep_clear_halt
函数最终都会调到dwc3 gadget驱动的dwc3_gadget_ep_set_halt
函数,当第二个参数value=1
时表示usb_ep_set_halt
,value=0
时表示usb_ep_clear_halt
。蓝色表示usb_ep_set_halt
函数执行路径,橙黄色表示usb_ep_clear_halt
函数执行路径。绿色两者都会执行。需要注意的是isoc端点无法设置和清除halt特性。
usb_ep_set_halt
执行流程如下:
DWC3_EP_STALL
。usb_ep_clear_halt
执行流程如下:
DEPCMD_CLEARSTALL
命令,清除端点的stall状态。DWC3_EP_STALL
和DWC3_EP_WEDGE
标志,DWC3_EP_WEDGE
标志设置时主机无法清除设备端点的stall状态,但设备驱动可以自己清除。usb_ep_set_wedge
函数将端点的状态设置为stall,并且会忽略主机发送的CLEAR_FEATURE
请求,也就是说主机无法清除带有DWC3_EP_WEDGE
标志的端点的stall状态,只有设备驱动自己可以清除。
软件可以通过usb_gadget_connect
和usb_gadget_disconnect
函数控制设备连接主机和设备和主机断开。usb_gadget_connect
调用后,设备会将D+上拉,主机识别D+上拉后开始枚举设备。
[include/linux/usb/gadget.h]
int usb_gadget_connect(struct usb_gadget *gadget);
int usb_gadget_disconnect(struct usb_gadget *gadget);
usb_gadget_connect
和usb_gadget_disconnect
的执行流程如下图所示。将DWC3_DCTL寄存器第31位设置为1时,USB设备控制器开始运行,会将D+上拉,主机识别D+上拉后开始枚举设备。将DWC3_DCTL寄存器第31位设置为0时,USB设备停止运行,会和主机断开连接,在将第31位设置为0之前,需要将所有传输的USB请求清空,将31位设置为0之后,需要等待设备和主机断开连接的操作完成,通过读取DWC3_DSTS寄存器的第22位判断,为0时表示已断开连接。
Function驱动和UDC驱动绑定的时候,实质上是通过usb_udc_connect_control
函数控制设备和主机连接和断开。主机通过vbus向设备供电,若设备不需要主机供电,则udc->vbus=true
。若需要,则通过usb_udc_vbus_handler
函数更新vbus的状态,像U盘、移动硬盘这类设备,必须要主机供电,供电以后才能开始工作和被主机枚举。
[include/linux/usb/gadget.h]
static void usb_udc_connect_control(struct usb_udc *udc)
{
if (udc->vbus)
usb_gadget_connect(udc->gadget);
else
usb_gadget_disconnect(udc->gadget);
}
/* updates the udc core vbus status, and try to
* connect or disconnect gadget
*/
void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
{
struct usb_udc *udc = gadget->udc;
if (udc) {
udc->vbus = status;
usb_udc_connect_control(udc);
}
}
UDC驱动向/sys目录导出了一些属性文件,供使用者在用户空间查询和操作。下面以rk3399为例,进行介绍:
查看当前的USB控制器是否支持OTG
/sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/is_otg
查看当前的USB控制器是否处于设备模式
/sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/is_a_peripheral
查看当前的USB控制器是否是自供电
/sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/is_selfpowered
查看当前的USB控制器速度
/sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/current_speed
查看当前的USB控制器支持的最大速度
/sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/maximum_speed
USB控制器的速度定义如下:
"UNKNOWN","low-speed","full-speed","high-speed","wireless","super-speed","super-speed-plus"。
控制USB设备控制器连接主机,connect-连接,disconnect-断开连接
/sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/soft_connect
查看当前USB设备控制器的状态
/sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/state
USB设备控制器的状态定义如下:
"not attached","attached","powered","reconnecting","unauthenticated","default","addressed","configured","suspended"。
上面总结了常用的UDC驱动接口,这些接口大多数可被Function驱动直接调用,少部分经过封装被Function驱动调用。通过分析这些接口的调用流程,对认识USB控制器内部的工作流程有很大的帮助。其实最重要的是弄清楚USB设备控制器接收数据和发送数据的流程,发送数据通过usb_ep_queue
发送,上面已经介绍过了,但接收数据的流程牵扯到中断处理和中断处理线程,后面专门开一章节介绍USB设备控制器的中断处理过程和数据接收过程。