700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > USB总线-Linux内核USB3.0设备控制器之dwc3 gadget驱动初始化过程分析(五)

USB总线-Linux内核USB3.0设备控制器之dwc3 gadget驱动初始化过程分析(五)

时间:2022-10-10 22:25:11

相关推荐

USB总线-Linux内核USB3.0设备控制器之dwc3 gadget驱动初始化过程分析(五)

1.概述

USB设备控制器(UDC)驱动的框图如下图所示,由三部分组成。第一部分是UDC驱动核心层,在drivers/usb/gadget/udc/core.c文件中实现,该层是一个兼容层,将USB Function驱动和具体的USB gadget驱动隔离开,抽象了统一的接口和数据结构,向USB Function驱动提供了统一且稳定的接口,同时完成USB Function驱动和USB gadget驱动的匹配。第二部分是gadget driver层,负责驱动硬件工作,和具体的USB设备控制器硬件相关,dwc3的gadget driver驱动在drivers/usb/dwc3/gadget.c文件中实现。第三部分是USB设备控制器硬件。

USB gadget驱动描述了USB设备控制器的硬件操作方法,不同的USB控制器实现不同。有的USB控制器只能作为设备控制器,如ompa、pxa2等USB设备控制器,其驱动在drivers/usb/gadget/udc文件夹中。有的USB控制器即可做主机控制器,也可做设备控制器,具有OTG功能,可以在两种模式中切换,如dwc3 USB控制器,其驱动在drivers/usb/dwc3文件中。RK3399的USB3.0控制器采用dwc3 USB控制器,具有OTG功能。

2.控制器模式

USB控制器切换为设备模式后使用UDC驱动,因此先从USB设备控制器的初始化过程开始分析,并对关键的数据结构做出说明。

2.1.初始化

在设备树中,设置dr_mode = "otg"属性,则dwc3控制器初始化的时候会将控制器设置为USB_DR_MODE_OTG模式,同时调用dwc3_host_initdwc3_gadget_init函数初始化主机模式和设备模式所需的资源,控制器后续可以动态切换为主机模式和设备模式。dwc3 USB3.0控制器的初始化过程如下图所示,重点分析初始化设备模式的过程,主要的工作如下:

(1)将控制器设置为USB_DR_MODE_OTG模式。

(2)初始化主机模式所需资源,具体过程在分析主机驱动的时候分析。

(3)初始化设备模式所需资源。

(a)获取中断号和分配端点0传输所需的内存,端点0在设备枚举的时候使用,需要响应主机端的请求,因此需要提前分配好内存。

(b)设置dwc3设备控制器的操作函数集合为dwc3_gadget_ops,只涉及硬件的控制,不涉及I/O操作。

(c)初始化硬件端点。先初始化输出端点,后初始化输入端点。端点0的最大包长为512字节,其他端点的最大包长为1024字节。端点0的操作函数为dwc3_gadget_ep0_ops,其他端点的操作函数为dwc3_gadget_ep0_ops,端点的操作函数主要描述I/O操作。非端点0都会挂到gadget.ep_list链表。端点0支持控制传输,其他端点支持等时、批量、中断传输。

(d)添加udc驱动。首先分配usb_udc数据结构,接着将其挂到udc_list链表,最后设置udc驱动状态为USB_STATE_NOTATTACHED

初始化完成后的数据结构如下图所示。最重要的还是跟端点相关的内容。端点0用于控制器传输,如设备枚举,响应主机发送的setup等请求,资源需要提前分配好。端点0与其他端点有本质的区别,因此其操作函数都是特有的。其他端点主要用于传输数据,操作函数共用。

2.2.模式切换

在设备树里面,将dwc3 USB控制器配置成peripheral模式,系统启动的时候会将USB控制器设置为设备模式,并初始化gadget相关资源,若配置成了otg模式,则只会初始化gadget相关资源,不会将dwc3控制器切换为设备模式,此时dwc3控制器处于otg模式,需要切换为设备模式(只有处于otg模式才可以切换为主机或设备)。

RK3399的USB3.0控制器可以切换为主机或设备模式。有两种切换方式,一种是使用fusb302芯片,底层的硬件感知到接入的设备,一般通过usb_id和vbus进行判断,然后通过中断的方式通知系统,最后系统根据接入设备的类型,将USB控制器切换为主机模式或设备模式。另一种是手动切换,向/sys/devices/platform/usb0/dwc3_mode文件中写入值进行切换,写入0或otg则切换为otg模式,写入1或host则切换为host模式,写入2或peripheral则切换为device模式。

// 切换为otg模式echo 0 > /sys/devices/platform/usb0/dwc3_modeecho otg > /sys/devices/platform/usb0/dwc3_mode// 切换为host模式echo 1 > /sys/devices/platform/usb0/dwc3_modeecho host > /sys/devices/platform/usb0/dwc3_mode// 切换为device模式echo 2 > /sys/devices/platform/usb0/dwc3_modeecho peripheral > /sys/devices/platform/usb0/dwc3_mode

上述对模式的切换,都通过调度工作队列otg_work完成,工作队列调用dwc3_rockchip_otg_extcon_evt_work函数进行切换。

[drivers\usb\dwc3\dwc3-rockchip.c]struct dwc3_rockchip {......struct work_structotg_work;......};static void dwc3_rockchip_otg_extcon_evt_work(struct work_struct *work){if (rockchip->force_mode ? dwc->dr_mode == USB_DR_MODE_PERIPHERAL :extcon_get_cable_state_(edev, EXTCON_USB)) {......spin_lock_irqsave(&dwc->lock, flags);// 设备模式dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);spin_unlock_irqrestore(&dwc->lock, flags);} else if (rockchip->force_mode ? dwc->dr_mode == USB_DR_MODE_HOST :extcon_get_cable_state_(edev, EXTCON_USB_HOST)) {......spin_lock_irqsave(&dwc->lock, flags);// 主机模式dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);spin_unlock_irqrestore(&dwc->lock, flags);......} else {......}......}

3.关键数据结构

2.1.端点

struct usb_ep是Linux内核描述USB设备控制器端点的通用数据结构。ops是端点对应的操作函数,主要用于描述I/O操作。ep_list是该端点的链表节点,通常情况下挂到usb_gadgetep_list链表上。maxpacket描述端点的最大包长,由端点描述符(软件)配置,如在USB3.0中,bulk传输最大包长为512,isoc最大包长为1024。maxpacket_limit描述端点硬件能处理的最大包长,如在USB3.0中,端点0最大能处理512字节,其他端点最大能处理1024字节。USB3.0支持在一个125微妙内burst传输多个数据包,最大值由maxburst设置,范围为0-15,0表示传输1包,15表示可以传输16包,对于端点0该值只能为0。每个端点都有不同的地址,使用addr描述。desc指向端点描述符。若使用USB3.0,则还需要设置comp_desc描述符。

struct usb_ep通常不直接使用,而是嵌入到一个大的数据结构中使用。在dwc3控制器中,嵌入到了struct dwc3_ep结构体中。pending_liststarted_list用于存放I/O请求数据结构struct usb_request,前者存放pending的I/O请求,暂时还不能处理,后者存放已经开始处理的I/O请求。trb_pool是一个trb组成的数组,由硬件自动处理,里面存放传输缓冲区的地址、长度及标志,非端点0分配256个trb,trb_pool_dma保存trb_pool的物理地址。trb_enqueuetrb_dequeuetrb_pool已使用和未使用的数组索引。allocated_requests表示已分配I/O请求的数量。

[include/linux/usb/gadget.h]struct usb_ep {// USB设备模式端点通用数据结构const char*name; // 名字const struct usb_ep_ops*ops; // 该端点对应的操作函数struct list_headep_list; // 端点的链表节点struct usb_ep_capscaps;// 端点支持的传输类型boolclaimed;boolenabled; // 端点是否使能unsignedmaxpacket:16; // 最大包长,由端点描述符配置unsignedmaxpacket_limit:16; // 端点硬件能处理的最大包长unsignedmax_streams:16;// 流的最大数量,范围0-16unsignedmult:2; // multiplier, 'mult' value for SS Isoc EPs// 端点支持的最大burst,范围0-15,USB3.0支持该选项unsignedmaxburst:5; u8address; // 端点地址,用于区分不同的端点const struct usb_endpoint_descriptor*desc; // 端点描述符const struct usb_ss_ep_comp_descriptor*comp_desc; // USB3.0伴侣描述符};[drivers/usb/dwc3/core.h]struct dwc3_ep {// dwc3 USB控制器设备模式端点数据结构struct usb_ependpoint;// 通用的设备端点数据结构struct list_headpending_list; // pending的IO requestsstruct list_headstarted_list; // started的IO requestsspinlock_tlock; // 自旋锁void __iomem*regs; // 该端点的寄存器基地址struct dwc3_trb*trb_pool;// 该端点的trb数组,用于DMA传输数据dma_addr_ttrb_pool_dma;// 该端点的trb数组物理地址const struct usb_ss_ep_comp_descriptor *comp_desc; // USB3.0伴侣描述符struct dwc3*dwc; // 指向dwc3 ctrlu32saved_state;unsignedflags; // 该端点的标志,由DWC3_EP开头的宏定义#define DWC3_EP_ENABLED(1 << 0)#define DWC3_EP_STALL(1 << 1)#define DWC3_EP_WEDGE(1 << 2)#define DWC3_EP_BUSY(1 << 4)#define DWC3_EP_PENDING_REQUEST(1 << 5)#define DWC3_EP_MISSED_ISOC(1 << 6)#define DWC3_EP0_DIR_IN(1 << 31)u8trb_enqueue; // trb数组入队索引u8trb_dequeue; // trb数组出队索引u8number; // endpoint number (1 - 15)// set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASKu8type;u8resource_index;u32allocated_requests; // 已经分配的IO requestsu32queued_requests;// 入队准备传输的IO requests数量// the interval on which the ISOC transfer is startedu32interval;// a human readable name e.g. ep1out-bulkcharname[20];unsigneddirection:1; // true for TX, false for RXunsignedstream_capable:1;};

struct usb_ep_ops描述端点的操作函数,主要和I/O操作相关。这些函数和硬件紧密相关,USB设备控制器需要实现这些函数,端点0和非端点0的函数实现也不一致。enable使能端点,disable禁止端点,alloc_request分配I/O请求数据结构usb_requestfree_request释放I/O请求,queue将I/O请求加入队列,dequeue将I/O请求移除队列,fifo_status获取fifo的状态,fifo_flush刷新fifo。

[include/linux/usb/gadget.h]struct usb_ep_ops {int (*enable) (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc);int (*disable) (struct usb_ep *ep);struct usb_request *(*alloc_request) (struct usb_ep *ep, gfp_t gfp_flags);void (*free_request) (struct usb_ep *ep, struct usb_request *req);int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);int (*dequeue) (struct usb_ep *ep, struct usb_request *req);int (*set_halt) (struct usb_ep *ep, int value);int (*set_wedge) (struct usb_ep *ep);int (*fifo_status) (struct usb_ep *ep);void (*fifo_flush) (struct usb_ep *ep);};

2.2.USB I/O请求

USB的I/O请求使用struct usb_request描述,functon驱动会将数据封装成usb_request的形式,然后发给udc驱动,udc驱动再将其转换为trb,最后将trb传给USB控制器端点,端点会自动处理。该结构体是一个通用的数据结构,底层驱动一般不直接使用,而是将其嵌入到另外一个结构体中。buf存放需要传输的数据,length保存数据的长度,dma保存buf的物理地址,sg是聚合DMA传输表项的地址,num_sgs表示有多少个scatterlistcomplete是该usb_request传输完成后的回调函数,不能睡眠,由dwc3控制器的下半部分(中断线程)调用contextcomplete回调函数的参数,status表示此次传输的结果,0表示传输完成,负数表示传输失败,-ESHUTDOWN错误码表示此次传输失败的原因是设备断开连接或者驱动关闭了端点,actual表示传输的字节数。

struct dwc3_request是dwc3控制器设备驱动描述I/O请求的数据结构,内部嵌入了通用I/O请求的数据结构usb_request

[include/linux/usb/gadget.h]struct usb_request {// 用于描述一个I/O请求void*buf; // 发送或接收数据的缓冲区unsignedlength; // 缓冲区数据长度dma_addr_tdma; // buf的物理地址struct scatterlist*sg; // a scatterlist for SG-capable controllersunsignednum_sgs; // number of SG entriesunsignednum_mapped_sgs; // number of SG entries mapped to DMA// The stream id, when USB3.0 bulk streams are being usedunsignedstream_id:16;/* If true, hints that no completion irq is needed.Helpful sometimes with deep request queues that are handleddirectly by DMA controllers. */unsignedno_interrupt:1;/* If true, when writing data, makes the last packet be "short"by adding a zero length packet as needed; */unsignedzero:1;/* When reading data, makes short packets betreated as errors (queue stops advancing till cleanup). */unsignedshort_not_ok:1;/* Function called when request completes, so this request andits buffer may be re-used. The function will always be called withinterrupts disabled, and it must not sleep.Reads terminate with a short packet, or when the buffer fills,whichever comes first. When writes terminate, some data byteswill usually still be in flight (often in a hardware fifo).Errors (for reads or writes) stop the queue from advancinguntil the completion function returns, so that any transfersinvalidated by the error may first be dequeued. */void(*complete)(struct usb_ep *ep, struct usb_request *req);void*context;// complete回调函数的参数struct list_headlist; // For use by the gadget driver./* Reports completion code, zero or a negative errno.Normally, faults block the transfer queue from advancing untilthe completion callback returns.Code "-ESHUTDOWN" indicates completion caused by device disconnect,or when the driver disabled the endpoint. */intstatus;/* Reports bytes transferred to/from the buffer. For reads (OUTtransfers) this may be less than the requested length. If theshort_not_ok flag is set, short reads are treated as errorseven when status otherwise indicates successful completion.Note that for writes (IN transfers) some data bytes may stillreside in a device-side FIFO when the request is reported ascomplete. */unsignedactual;};[drivers/usb/dwc3/core.h]struct dwc3_request {// 描述dwc3控制器的一次I/O传输struct usb_requestrequest; // 通用的I/O请求struct list_headlist;// 请求队列链表struct dwc3_ep*dep;// 该请求所属的端点u8first_trb_index; // index to first trb used by this requestu8epnum; // 该请求对应的端点编号struct dwc3_trb*trb;// 所属trb的地址dma_addr_ttrb_dma;// 所属trb的DMA地址unsigneddirection:1; // IN or OUT direction flagunsignedmapped:1;// true when request has been dma-mappedunsignedstarted:1; // true when request has been queued to HW};

2.3.TRB

TRB(transfer request block)传输请求块是一种硬件格式,由端点硬件自动处理。bplbph是分别是64位缓冲区DMA地址的低32位和高32位,size是缓冲区的长度,占23位,其余为控制位。dwc3控制器设备驱动会将dwc3_requestdwc3_trb进行绑定,并设置TRB中各个位,然后将TRB的DMA地址写到控制器中,最后使能传输,控制器会自动的将TRB传输到端点中,然后将TRB指定缓冲区中的数据发送出去。

[drivers/usb/dwc3/core.h]struct dwc3_trb {u32bpl; // 缓冲区低32地址 DW0-3u32bph; // 缓冲区高32地址 DW4-7u32size; // 缓冲区长度[23:0] DW8-Bu32ctrl; // 控制位 DWC-F} __packed;

TRB的详细位域如下图所示,总共16字节。蓝色区域软件设置,绿色区域软件设置,硬件更新。详细信息参考下表。

参考资料

Rockchip RK3399TRM V1.3 Part1Rockchip RK3399TRM V1.3 Part2Linux内核4.4.179版本源码

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。