第四十六篇:Linux中的USB XHCI HOST TRANSFER RING的相关数据结构 (1)

本文介绍了USB XHCI控制器中的数据传输机制,包括COMMAND、EVENT与TRANSFER三种RING结构,以及TRB(Transmission Request Block)的不同类型,如SETUP TRB、DATA TRB等,并详细解释了TD(Transfer Descriptor)与TRB之间的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

记录相关数据结构之前,先上一张图,这样就基本一目了然了



USB XHCI HOST有三种RING

分别为COMMAND, EVENT 与TRANSFER


这里所说的是TRANSFER RING

一个RING可能由一个或者多个SEGMENT构成

每一个SEGMENT则有多个TRB构成

多个SEGMENT的链接是由LINK TRB来实现的,LINK就像一个单链表,最后一个LINK TRB指向第一个SEGMENT,由此,形成了一个环,RING


TRANSFER TRB有以下几种类型:

针对USB CONTROL TRANSFER的SETUP, DATA, STATUS TRBs

针对BULK, INTERRUPT, ISO TRANSFER的NORMAL TRB

针对ISO TRANSFER的ISOCH TRB (ISO TD由一个ISOCH TRB与0个或多个NORMAL TRB构成)


SW将需要硬件完成的USB传输,通过TRB的形式,将信息提交给硬件,放入RING当中,放入的位置为当前ENQUEUE PTR的位置,每放一个,ENQUEUE PTR向前跨一步,遇到LINK TRB,则跳转到LINK TRB指向的位置

而硬件则按DEQUEUE PTR指向的位置,取出TRB到CACHE当中,执行该TRB,同样,每执行一个,则ADVANCE 该 DEQUEUE PTR,遇LINK TRB,跳转。


TD表示一个USB TRANSFER(不同于USB TRANSACTION)

在TRB当中,有一个CH BIT,如果一处TD由多个TRB构成,则软件需要将除最后一个TRB的所有CH BIT置位。


<think>我们正在讨论XHCI驱动中的`xhci_queue_ctrl_tx`函数,用户现在想了解在这个函数中使用的transfer ring的初始化函数。 根据之前的讨论,我们知道transfer ring是每个端点(Endpoint)或流(Stream)一个的循环队列,用于在内存和设备端点之间传输数据。 在XHCI驱动中,transfer ring的初始化并不是在`xhci_queue_ctrl_tx`函数中进行的,而是在设备配置过程中初始化的。具体来说,当USB设备被枚举并配置时,XHCI驱动会为设备的每个端点(包括控制端点0)创建并初始化transfer ring。 回顾XHCI驱动代码(以Linux内核为例),transfer ring的初始化通常发生在以下几个步骤: 1. 当USB设备被分配一个slot时,会调用`xhci_alloc_virt_device`函数为设备分配虚拟设备结构(`struct xhci_virt_device`)。 2. 在该结构中,每个端点都有一个对应的`struct xhci_virt_ep`,其中包含一个`ring`成员,即transfer ring。 3. 在设备配置阶段(例如,在设置配置命令完成后),驱动会为每个端点初始化transfer ring,这通常是通过`xhci_add_endpoint`函数或类似函数完成的。 具体到控制端点(端点0),它的transfer ring初始化发生在设备枚举的早期,即在设备被分配slot之后,第一次控制传输之前。 因此,要找到控制端点transfer ring的初始化函数,我们应该关注: - `xhci_alloc_virt_device`:为设备分配虚拟设备结构,包括端点数组。 - `xhci_setup_addressable_virt_dev`:设置可寻址虚拟设备,其中会初始化控制端点的ring。 - 或者,在设备配置过程中调用的端点使能函数。 在Linux内核的XHCI驱动中,控制端点的transfer ring初始化实际上是在`xhci_setup_addressable_virt_dev`函数中完成的。这个函数会为控制端点0设置输入和输出的transfer ring(控制端点是双向的,但在XHCI中,控制端点0实际上有两个ring:一个用于输入,一个用于输出?实际上,在XHCI中,控制端点0使用一个ring,因为控制传输是双向的,但XHCI规范中,控制端点只有一个transfer ring,它通过不同的TRB类型来处理双向传输)。 然而,在XHCI驱动中,控制端点0的transfer ring是在设备初始化时(分配slot后)就创建的,具体在`xhci_alloc_virt_device`函数中就会为默认控制端点0分配ring。然后在`xhci_setup_addressable_virt_dev`中,会设置这个ring的上下文信息。 让我们具体看一下代码(以Linux内核5.x为例): 1. 在`xhci_alloc_virt_device`函数中(位于drivers/usb/host/xhci-mem.c): ```c struct xhci_virt_device *xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, struct usb_hcd *hcd, gfp_t flags) { ... dev->eps[0].ring = xhci_ring_alloc(xhci, 1, true, false, flags); ... } ``` 这里为端点0(索引0)分配了一个ring。 2. 在`xhci_setup_addressable_virt_dev`函数中(同样在xhci-mem.c): ```c void xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev, struct xhci_virt_device *dev) { ... /* set up the endpoint ring */ ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, 0); xhci_ring_ep_doorbell(xhci, slot_id, 0, 0); ... } ``` 这里会设置端点0的上下文,并将ring的地址等信息写入到上下文数据结构中。 因此,控制端点0的transfer ring初始化是在`xhci_alloc_virt_device`中通过`xhci_ring_alloc`函数创建的,然后在`xhci_setup_addressable_virt_dev`中配置到设备的上下文(Context)中。 总结:在`xhci_queue_ctrl_tx`接口中使用的transfer ring的初始化函数是`xhci_ring_alloc`,这个函数在设备初始化阶段(分配slot时)被调用,具体在`xhci_alloc_virt_device`函数中。 注意:`xhci_ring_alloc`函数用于分配并初始化一个ring(包括TRB数组、初始化环状态等)。 所以,回答用户问题:`xhci_queue_ctrl_tx`中使用的transfer ring的初始化函数是`xhci_ring_alloc`,但这个初始化并不是在`xhci_queue_ctrl_tx`函数内部完成的,而是在设备初始化阶段完成的。</think>### XHCI Transfer Ring 初始化机制 在 XHCI 驱动中,`xhci_queue_ctrl_tx` 使用的 Transfer Ring 初始化主要通过以下函数实现: --- ### 核心初始化函数:`xhci_ring_alloc()` 这是 Transfer Ring 的通用初始化函数,用于创建控制端点的传输队列[^1]: ```c // drivers/usb/host/xhci-mem.c struct xhci_ring *xhci_ring_alloc( struct xhci_hcd *xhci, unsigned int num_segs, // TRB 段数量 bool link_trbs, // 是否启用 TRB 链式连接 enum xhci_ring_type type, // 环形队列类型 gfp_t flags ) { struct xhci_ring *ring; ring = kzalloc(sizeof(*ring), flags); /* 关键初始化步骤 */ INIT_LIST_HEAD(&ring->td_list); // 传输描述符链表 ring->num_segs = num_segs; // 设置 TRB 段数 ring->type = type; // 指定为 TRANSFER_RING 类型 ring->enqueue = ring->first_seg->trbs; // 初始化入队指针 ring->dequeue = ring->first_seg->trbs; // 初始出队指针 ring->enq_updates = 0; ring->deq_updates = 0; /* 构建 TRB 链 */ for (i = 0; i < num_segs; i++) { struct xhci_segment *seg = ring->first_seg; xhci_link_segments(seg, seg->next, link_trbs); // 连接 TRB 段 } return ring; } ``` --- ### 控制端点的专属初始化 控制端点(EP0)的 Transfer Ring 在设备枚举阶段初始化: ```c // xhci-mem.c int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id) { struct xhci_virt_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL); /* 控制端点 Transfer Ring 初始化 */ dev->eps[0].ring = xhci_ring_alloc( xhci, 1, // 初始 1TRB 段 true, // 启用链式 TRB TYPE_CTRL, // 控制传输类型 GFP_KERNEL ); /* 设置端点上下文 */ ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, 0); ep_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma); ep_ctx->ep_info |= cpu_to_le32(EP_HAS_LSA); // 启用流数组 } ``` --- ### 初始化的触发时机 1. **设备枚举阶段** 当 USB 设备接入时,XHCI 控制器通过 `xhci_alloc_dev()` 分配设备槽(Slot),随后调用 `xhci_alloc_virt_device()` 初始化控制端点的 Transfer Ring[^1]。 2. **配置阶段** 在 `xhci_setup_addressable_virt_dev()` 中完成控制端点的上下文配置,将 Transfer Ring 的 DMA 地址写入端点上下文(EP Context)[^1]。 3. **传输前准备** 在 `xhci_queue_ctrl_tx()` 执行前,Transfer Ring 已通过 `xhci_ring_doorbell()` 激活,等待 TRB 入队。 --- ### 关键数据结构关联 ```mermaid graph LR A[xhci_virt_device] --> B[eps[0]] B --> C[xhci_ring] C --> D[xhci_segment] D --> E[TRB Array] C --> F[enqueue ptr] C --> G[dequeue ptr] ``` > ✅ **完整路径**: > `usb_hcd_probe()` → `xhci_setup()` → `xhci_mem_init()` → `xhci_alloc_dev()` → `xhci_alloc_virt_device()` → `xhci_ring_alloc()`[^1] --- ### 设计特点 1. **按需分配** - 初始仅分配最小 TRB 段(通常 1-2 个),动态扩展 2. **DMA 映射** - `xhci_segment->dma` 存储物理地址供控制器访问 3. **流上下文支持** - 控制端点使用 Primary Stream Context(无流 ID) 4. **错误隔离** - 每个端点独立 Transfer Ring,避免传输干扰
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值