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_init
和dwc3_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_mode
echo otg > /sys/devices/platform/usb0/dwc3_mode
// 切换为host模式
echo 1 > /sys/devices/platform/usb0/dwc3_mode
echo host > /sys/devices/platform/usb0/dwc3_mode
// 切换为device模式
echo 2 > /sys/devices/platform/usb0/dwc3_mode
echo 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_struct otg_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_gadget
的ep_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_list
和started_list
用于存放I/O请求数据结构struct usb_request
,前者存放pending的I/O请求,暂时还不能处理,后者存放已经开始处理的I/O请求。trb_pool
是一个trb组成的数组,由硬件自动处理,里面存放传输缓冲区的地址、长度及标志,非端点0分配256个trb,trb_pool_dma
保存trb_pool
的物理地址。trb_enqueue
和trb_dequeue
是trb_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_head ep_list; // 端点的链表节点struct usb_ep_caps caps; // 端点支持的传输类型bool claimed;bool enabled; // 端点是否使能unsigned maxpacket:16; // 最大包长,由端点描述符配置unsigned maxpacket_limit:16; // 端点硬件能处理的最大包长unsigned max_streams:16; // 流的最大数量,范围0-16unsigned mult:2; // multiplier, 'mult' value for SS Isoc EPs// 端点支持的最大burst,范围0-15,USB3.0支持该选项unsigned maxburst:5; u8 address; // 端点地址,用于区分不同的端点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_ep endpoint; // 通用的设备端点数据结构struct list_head pending_list; // pending的IO requestsstruct list_head started_list; // started的IO requestsspinlock_t lock; // 自旋锁void __iomem *regs; // 该端点的寄存器基地址struct dwc3_trb *trb_pool; // 该端点的trb数组,用于DMA传输数据dma_addr_t trb_pool_dma; // 该端点的trb数组物理地址const struct usb_ss_ep_comp_descriptor *comp_desc; // USB3.0伴侣描述符struct dwc3 *dwc; // 指向dwc3 ctrlu32 saved_state;unsigned flags; // 该端点的标志,由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)u8 trb_enqueue; // trb数组入队索引u8 trb_dequeue; // trb数组出队索引u8 number; // endpoint number (1 - 15)// set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASKu8 type;u8 resource_index;u32 allocated_requests; // 已经分配的IO requestsu32 queued_requests; // 入队准备传输的IO requests数量// the interval on which the ISOC transfer is startedu32 interval;// a human readable name e.g. ep1out-bulkchar name[20];unsigned direction:1; // true for TX, false for RXunsigned stream_capable:1;
};
struct usb_ep_ops
描述端点的操作函数,主要和I/O操作相关。这些函数和硬件紧密相关,USB设备控制器需要实现这些函数,端点0和非端点0的函数实现也不一致。enable
使能端点,disable
禁止端点,alloc_request
分配I/O请求数据结构usb_request
,free_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
表示有多少个scatterlist
,complete
是该usb_request
传输完成后的回调函数,不能睡眠,由dwc3控制器的下半部分(中断线程)调用,context
是complete
回调函数的参数,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; // 发送或接收数据的缓冲区unsigned length; // 缓冲区数据长度dma_addr_t dma; // buf的物理地址struct scatterlist *sg; // a scatterlist for SG-capable controllersunsigned num_sgs; // number of SG entriesunsigned num_mapped_sgs; // number of SG entries mapped to DMA// The stream id, when USB3.0 bulk streams are being usedunsigned stream_id:16;/* If true, hints that no completion irq is needed.Helpful sometimes with deep request queues that are handleddirectly by DMA controllers. */unsigned no_interrupt:1;/* If true, when writing data, makes the last packet be "short"by adding a zero length packet as needed; */unsigned zero:1;/* When reading data, makes short packets betreated as errors (queue stops advancing till cleanup). */unsigned short_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_head list; // 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. */int status;/* 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. */unsigned actual;
};
[drivers/usb/dwc3/core.h]
struct dwc3_request { // 描述dwc3控制器的一次I/O传输struct usb_request request; // 通用的I/O请求struct list_head list; // 请求队列链表struct dwc3_ep *dep; // 该请求所属的端点u8 first_trb_index; // index to first trb used by this requestu8 epnum; // 该请求对应的端点编号struct dwc3_trb *trb; // 所属trb的地址dma_addr_t trb_dma; // 所属trb的DMA地址unsigned direction:1; // IN or OUT direction flagunsigned mapped:1; // true when request has been dma-mappedunsigned started:1; // true when request has been queued to HW
};
2.3.TRB
TRB(transfer request block)传输请求块是一种硬件格式,由端点硬件自动处理。bpl
和bph
是分别是64位缓冲区DMA地址的低32位和高32位,size
是缓冲区的长度,占23位,其余为控制位。dwc3控制器设备驱动会将dwc3_request
和dwc3_trb
进行绑定,并设置TRB中各个位,然后将TRB的DMA地址写到控制器中,最后使能传输,控制器会自动的将TRB传输到端点中,然后将TRB指定缓冲区中的数据发送出去。
[drivers/usb/dwc3/core.h]
struct dwc3_trb {u32 bpl; // 缓冲区低32地址 DW0-3u32 bph; // 缓冲区高32地址 DW4-7u32 size; // 缓冲区长度[23:0] DW8-Bu32 ctrl; // 控制位 DWC-F
} __packed;
TRB的详细位域如下图所示,总共16字节。蓝色区域软件设置,绿色区域软件设置,硬件更新。详细信息参考下表。
位域 | 全称 | 说明 | 硬件如何访问 |
---|---|---|---|
BPTRL | Buffer Pointer Low | 64位缓冲区DMA地址的低32位 | R_W |
BPTRH | Buffer Pointer High | 64位缓冲区DMA地址的高32位 | R_W |
BUFSIZ | Buffer Size | 缓冲区的大小,范围0-(16 MB - 1 byte),传输完成后硬件会递减此区域 | R_W |
PCM1 | Packet Count M1 | USB2.0等时传输的输入端点,支持一个微帧传输多个数据包,该区域就是设置一个微帧传输几包数据,在准备第一个trb时需要设置,USB2.0 125微妙内最多可以传输3包数据 | R_W |
TRBSTS | TRB Status |
传输状态,由硬件设置 0-成功 1-MissedIsoc 2-SetupPending 4-TransferInProgress 4-ZLP_PENDING |
R_W |
HWO | Hardware Owner of Descriptor | 软件准备TRB时设置为1,表示该trb属于控制器,在该位由硬件清0之前,软件不能修改此trb | R_W |
LST | Last TRB | 标识最后一个TRB, 通常一个TRB并不能传输完所有数据, 比如超长的配置描述符,此时需要将TRB组织成链表方式, 将CHN位置成1, 最后一个TRB需要将CHN置成0, LST置成1 | R |
CHN | Chain Buffers | 通常一个TRB并不能传输完所有数据,需要将CHN设置为1,将TRB组织成链表形式,控制器会将这些TRB看成一个事务进行传输,最后一个TRB需要将CHN置成0, LST置成1 | R |
TRBCTL | TRB Control |
指出TRB的类型 1-Control-Data-2+/Bulk/Interrupt 2-Control-Setup 3-Control-Status-2 4-Control-Status-3 5-Control-Data 6-Isochronous-First 7-Isochronous 8-Link TRB,Normal-ZLP (Bulk-IN) |
R |
ISP/IMI | Interrupt on Short Packet / Interrupt on Missed ISOC | ISP-当输出端点收到一个short packet,同时CSP=1且LST=0,则控制器产生XferInProgress事件,IMI-对于ISOC端点,如果该位置为1, 且ISOC传输的时间过期,则控制器会产生一个XferInProgress的事件。(ISOC传输注重实时性, 对时间要求比较敏感。设备端ep_queue一包数据时都需要带一个预期发送数据的微帧号, 如果微帧号过期了, 即当前微帧号大于给定的微帧号, 则会发生Missed Isoc事件, 同时将该数据包丢弃) | R |
IOC | Interrupt on Complete | 当IOC=1时,一但TRB中的数据完成传输后控制器会产生一个XferInProgress?XferComplete?事件 | R |
参考资料
- Rockchip RK3399TRM V1.3 Part1
- Rockchip RK3399TRM V1.3 Part2
- Linux内核4.4.179版本源码
USB总线-Linux内核USB3.0设备控制器之dwc3 gadget驱动初始化过程分析(五)相关推荐
- USB总线-Linux内核USB3.0设备控制器之UDC驱动分析(六)
1.概述 UDC驱动的接口都定义在drivers/usb/gadget/udc/core.c文件中.USB Function驱动通过调用这些接口匹配及访问USB设备控制器,而底层USB控制器驱动要实现 ...
- USB总线-Linux内核USB3.0设备控制器中断处理程序分析(九)
1.概述 USB设备枚举.请求处理.数据交互都涉及USB设备控制器中断.当有事件发生时,USB设备控制器首先将事件信息通过DMA写入到事件缓冲区中,然后向CPU发出中断,随后CPU调用中断处理函数开始 ...
- USB总线-Linux内核USB3.0设备控制器复合设备之USB gadget configfs分析(七)
1.简介 configfs是基于ram的文件系统,与sysfs的功能有所不同.sysfs是基于文件系统的kernel对象视图,虽然某些属性允许用户读写,但对象是在kernel中创建.注册.销毁,由ke ...
- USB总线-Linux内核USB设备驱动之UAC2驱动分析(十)
1.概述 UVC(USB Audio Class)定义了使用USB协议播放或采集音频数据的设备应当遵循的规范.目前,UAC协议有UAC1.0和UAC2.0.UAC2.0协议相比UAC1.0协议,提供了 ...
- linux查看usb3.0还是2.0,Linux分辨电脑是否有USB 3.0接口的命令行 怎么看电脑用独立显卡还是集成显卡...
延伸:怎么看电脑用独立显卡还是集成显卡 描述:方法一.通过接口来判断我们在主机箱后面的接口上,看你的链接数据线的接口接上了哪个接口,如果是连接集成显卡的话那就是连接到竖的的接口上,因为集成显卡的VGA ...
- linux内核4.0,新闻|Linux内核4.0功能:实时内核补丁,支持PS3
Linux Torvalds 在Linux内核邮件列表里发布了Linux内核新的稳定版. Linux 4.0,代号为'Hurr durr I'm a sheep',带来了一小系列新硬件支持,驱动改进, ...
- 【usb】linux内核USB键盘驱动解析--普通键值上报及转化
一.概况 建议阅读前置文章[usb]linux内核USB键盘驱动解析–特殊键值上报及转化 以Linux5.10内核中USB键盘驱动为例进行解析:https://mirrors.edge.kernel. ...
- 如何在Ubuntu/CentOS上安装Linux内核4.0
如何在Ubuntu/CentOS上安装Linux内核4.0 大家好,今天我们学习一下如何从Elrepo或者源代码来安装最新的Linux内核4.0.代号为'Hurr durr I'm a sheep'的 ...
- 跟踪分析Linux内核5.0系统调用处理过程
跟踪分析Linux内核5.0系统调用处理过程 学号384 原创作业转载请注明出处+中国科学技术大学孟宁老师的Linux操作系统分析 https://github.com/mengning/linuxk ...
最新文章
- load balancer 配置参考
- 已知两点的经度和纬度,计算两点间的距离(php,javascript)
- 函数(一.return)
- 解决remove @override annotation
- RPM 软件包默认的安装路径
- 【OpenCV 例程200篇】26. 图像的旋转(以原点为中心)
- 功能暴强的页面验证js代码
- Spring Boot学习记之Maven
- mac系统 环境变量配置
- hibernate枚举类型注解 @Enumerated
- tp5 in_array 在 view页面用法
- NVIDIA系列显卡与AMD系列显卡性能对比,以及购买显卡的时候应该看哪些性能指标,NVIDIA显卡与AMD显卡的区别
- R语言 回归诊断几种方法
- mysql错误01000_错误 ORA-01000: maximum open cursors exceeded Exception
- 【BLE】CC2541之SBL
- Android tips(十二)--Android开发中使用矢量图
- SRS(简单实时视频服务) 笔记(3)- 配置文件和Http回调
- Silicon Labs EFR32 RF射频测试-RAILTEST
- LVGL 8.2 meter控件实现模拟时钟
- CSDN发表文章后待审核的原因
热门文章
- start with connect by prior的使用方法
- Oracle 10.2 流复制问题(二)—— C001: large txn detected
- Debezium 抽取oracle数据
- RPG Maker MV 踩坑一 新仙剑菜单
- 中国程序员独闯硅谷,逆袭成美国最佳 CEO,公司市值 160 亿美元!
- Java开发之路—Java反射机制
- oracle异地容灾备份 英文6,异地容灾备份的方案.doc
- 剑指offe——61序列化二叉树(Python)
- 微信公众号java开发沉淀(五)推送群发消息
- 浏览器指纹:原来我们一直被互联网巨头监视,隐私在网上裸奔、无处可藏