xhci 数据结构

xhci 数据结构

xhci 数据结构主要在手册上有详细的定义,本文根据手册进行归纳总结:

重点关注的包括:

  1. device context
  2. trb ring
  3. trb

device context设备上下文

设备上下文数据结构由xHC管理,用于向系统软件报告设备配置和状态信息。设备上下文数据结构由32个数据结构的数组组成。第一个上下文数据结构(索引="0")是Slot Context数据结构。其余上下文数据结构是"端点上下文"数据结构

在枚举USB设备的过程中,系统软件会在主机内存中为该设备分配设备上下文数据结构,并将其初始化为"0"。然后,使用地址设备命令将数据结构的所有权传递给xHC。xHC保留对设备上下文的所有权,直到使用"禁用插槽命令"禁用了设备插槽为止。设备上下文数据结构由xHC拥有时,应被系统软件视为只读

其中主要分为slot 上下文 , 和 endpoint 上下文,在host xhci 中有定义

 616 struct xhci_slot_ctx {
 617     __le32  dev_info;
 618     __le32  dev_info2;
 619     __le32  tt_info;
 620     __le32  dev_state;
 621     /* offset 0x10 to 0x1f reserved for HC internal use */
 622     __le32  reserved[4];
 623 };

 700 struct xhci_ep_ctx {
 701     __le32  ep_info;
 702     __le32  ep_info2;
 703     __le64  deq;
 704     __le32  tx_info;
 705     /* offset 0x14 - 0x1f reserved for HC internal use */
 706     __le32  reserved[3];
 707 };

slot 上下文

Slot主要是有关 包含与整个设备有关的信息,或影响USB设备的所有端点的信息。Slot Context提供的信息包括: 控制,状态,寻址和电源管理。

作为设备上下文成员,xHC使用插槽上下文数据结构将设备参数的当前值报告给系统软件。

xHC报告的插槽状态标识设备的当前状态,并与USB规范中描述的USB设备状态紧密对应。

设备上下文的Slot Context数据结构也称为"Output Slot Context"。 作为输入上下文成员,系统软件使用Slot

Context数据结构将命令参数传递给主机控制器。 输入上下文的Slot Context数据结构也称为"Input Slot Context"。

如果针对设备插槽的命令成功执行,则xHC将在生成Command Completion Event 之前更新输出插槽上下文,以反映其正在主动使用的参数值来管理设备。

插槽上下文的xHCI保留区域可用作xHC实现定义的暂存器。

插槽上下文中的所有保留字段仅供xHC使用,除非插槽处于"禁用"状态,否则不得由系统软件修改。

endpoint 上下文

重点参数:



针对iso通信:

在xhci.c 中进行了初始化:

1418 /* Set up an endpoint with one ring segment.  Do not allocate stream rings.
1419  * Drivers will have to call usb_alloc_streams() to do that.
1420  */
1421 int xhci_endpoint_init(struct xhci_hcd *xhci,
1422         struct xhci_virt_device *virt_dev,
1423         struct usb_device *udev,
1424         struct usb_host_endpoint *ep,
1425         gfp_t mem_flags)
1426 {
...

1494     /* Set up the endpoint ring */
1495     virt_dev->eps[ep_index].new_ring =
1496         xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags);
1497     if (!virt_dev->eps[ep_index].new_ring)
1498         return -ENOMEM;
...
1503     /* Fill the endpoint context */
1504     ep_ctx->ep_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_HI(max_esit_payload) |
1505                       EP_INTERVAL(interval) |
1506                       EP_MULT(mult));
1507     ep_ctx->ep_info2 = cpu_to_le32(EP_TYPE(endpoint_type) |
1508                        MAX_PACKET(max_packet) |
1509                        MAX_BURST(max_burst) |
1510                        ERROR_COUNT(err_count));
1511     ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma |
1512                   ep_ring->cycle_state);
1513 
1514     ep_ctx->tx_info = cpu_to_le32(EP_MAX_ESIT_PAYLOAD_LO(max_esit_payload) |
1515                       EP_AVG_TRB_LENGTH(avg_trb_len));

...

ring

Ring是一个循环队列,xHC使用三种类型的Ring:

  • Command Ring:(每个XHC一个)软件使用Command Ring将命令发送给xHC。使系统软件能够发出命令以枚举USB设备,配置xHC以支持这些设备以及协调虚拟化功能。
  • Event Ring:(每个中断一个)每个中断器的一种循环队列,为xHC提供了一种向系统软件报告的方式:数据传输和命令完成状态,根集线器端口状态更改以及其他与xHC相关的事件。或者说:xHC使用事件环返回状态和命令结果,并将其传输到系统软件。
  • Transfer Ring:(每个Endpoint或Stream一个)Transfer Ring被用来在内存和设备Endpoint之间传输数据。

备注:每个设备,插入的过程中,会根据设备反馈的设备描述符,注册相应的多个endpoint,后面我单独写一篇,usb设备插入注册,解析设备描述符的过程。

1589 struct xhci_ring {
1590     struct xhci_segment *first_seg;
1591     struct xhci_segment *last_seg;
1592     union  xhci_trb     *enqueue;
1593     struct xhci_segment *enq_seg;
1594     union  xhci_trb     *dequeue;
1595     struct xhci_segment *deq_seg;
1596     struct list_head    td_list;
1597     /*
1598      * Write the cycle state into the TRB cycle field to give ownership of
1599      * the TRB to the host controller (if we are the producer), or to check
1600      * if we own the TRB (if we are the consumer).  See section 4.9.1.
1601      */
1602     u32         cycle_state;
1603     unsigned int        stream_id;
1604     unsigned int        num_segs;
1605     unsigned int        num_trbs_free;
1606     unsigned int        num_trbs_free_temp;
1607     unsigned int        bounce_buf_len;
1608     enum xhci_ring_type type;
1609     bool            last_td_was_short; 
1610     struct radix_tree_root  *trb_address_map;
1611 };

Transfer Ring

重点介绍每个endpoint 上的Transfer Ring

将需要硬件完成的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置位。

trb 的结构:

复制代码

xhci 发送数据分析:

概述:

从xhci_urb_enqueue 开始看:

1433 static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
1434 {
···
1519     case USB_ENDPOINT_XFER_CONTROL:
1520         ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
1521                      slot_id, ep_index);
···

3211 int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
3212         struct urb *urb, int slot_id, unsigned int ep_index)
3213 {
···
246     ret = prepare_transfer(xhci, xhci->devs[slot_id],
3247             ep_index, urb->stream_id,
3248             num_trbs, urb, 0, mem_flags);
····

3323         queue_trb(xhci, ring, more_trbs_coming | need_zero_pkt,
3324                 lower_32_bits(send_addr),
3325                 upper_32_bits(send_addr),
3326                 length_field,
3327                 field);
···

2828 static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring,
2829         bool more_trbs_coming,
2830         u32 field1, u32 field2, u32 field3, u32 field4)
2831 {
2832     struct xhci_generic_trb *trb;
2833 
2834     trb = &ring->enqueue->generic;
2835     trb->field[0] = cpu_to_le32(field1);
2836     trb->field[1] = cpu_to_le32(field2);
2837     trb->field[2] = cpu_to_le32(field3);
2838     /* make sure TRB is fully written before giving it to the controller */
2839     wmb();
2840     trb->field[3] = cpu_to_le32(field4);
2841 
2842     trace_xhci_queue_trb(ring, trb);
2843 
2844     inc_enq(xhci, ring, more_trbs_coming);
2845 }
2846 

通过inc_enq 向xhci ring 中写数据,更新指针enqueue。

 204 static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
 205             bool more_trbs_coming)
 206 {
 207     u32 chain;
 208     union xhci_trb *next;
 209 
 210     chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN;
 211     /* If this is not event ring, there is one less usable TRB */
 212     if (!trb_is_link(ring->enqueue))
 213         ring->num_trbs_free--;
 214     next = ++(ring->enqueue);

xhci 接收数据分析:

xhci 的中断处理函数xhci_irq 调用函数 xhci_handle_event

2625 static int xhci_handle_event(struct xhci_hcd *xhci)
2626 {
2651     switch (le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) {
2652     case TRB_TYPE(TRB_COMPLETION):
2653         handle_cmd_completion(xhci, &event->event_cmd);
2654         break;
2655     case TRB_TYPE(TRB_PORT_STATUS):
2656         handle_port_status(xhci, event);
2657         update_ptrs = 0;
2658         break;
2659     case TRB_TYPE(TRB_TRANSFER):
2660         ret = handle_tx_event(xhci, &event->trans_event);
2661         if (ret >= 0)
2662             update_ptrs = 0;
2663         break;
2664     case TRB_TYPE(TRB_DEV_NOTE):
2665         handle_device_notification(xhci, event);
2666         break;
2667     default:
2668         if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
2669             TRB_TYPE(48))
2670             handle_vendor_event(xhci, event);
2671         else
2672             xhci_warn(xhci, "ERROR unknown event type %d\n",
2673                   TRB_FIELD_TO_TYPE(
2674                   le32_to_cpu(event->event_cmd.flags)));
2675     }

其中判断,trb 的类型:

#define TRB_TYPE(p)     ((p) << 10)

如果类型是#define TRB_COMPLETION 33, 则表示传输完成,调用handle_cmd_completion,

则会调用函数handle_cmd_completion -> xhci_handle_cmd_set_deq -> update_ring_for_set_deq_completion

在函数update_ring_for_set_deq_completion中更新指针dequeue

 975 static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
 976         struct xhci_virt_device *dev,
 977         struct xhci_ring *ep_ring,
 978         unsigned int ep_index)
 979 {
 980     union xhci_trb *dequeue_temp;
 981     int num_trbs_free_temp;
 982     bool revert = false;
 983 
 984     num_trbs_free_temp = ep_ring->num_trbs_free;
 985     dequeue_temp = ep_ring->dequeue;
 986 
 987     /* If we get two back-to-back stalls, and the first stalled transfer
 988      * ends just before a link TRB, the dequeue pointer will be left on
 989      * the link TRB by the code in the while loop.  So we have to update
 990      * the dequeue pointer one segment further, or we'll jump off
 991      * the segment into la-la-land.
 992      */
 993     if (trb_is_link(ep_ring->dequeue)) {
 994         ep_ring->deq_seg = ep_ring->deq_seg->next;
 995         ep_ring->dequeue = ep_ring->deq_seg->trbs;
 996     }
 997 
 998     while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
 999         /* We have more usable TRBs */
1000         ep_ring->num_trbs_free++;
1001         ep_ring->dequeue++;
1002         if (trb_is_link(ep_ring->dequeue)) {
1003             if (ep_ring->dequeue ==
1004                     dev->eps[ep_index].queued_deq_ptr)
1005                 break;
1006             ep_ring->deq_seg = ep_ring->deq_seg->next;
1007             ep_ring->dequeue = ep_ring->deq_seg->trbs;
1008         }
1009         if (ep_ring->dequeue == dequeue_temp) {
1010             revert = true;
1011             break;
1012         }
1013     }
1014 
1015     if (revert) {
1016         xhci_dbg(xhci, "Unable to find new dequeue pointer\n");
1017         ep_ring->num_trbs_free = num_trbs_free_temp;
1018     }
1019 }
相关推荐
LUCIAZZZ3 分钟前
弄懂Runable,Callable,Future之间的关系
java·开发语言
电脑玩家粉色男孩7 分钟前
八、Spring Boot 日志详解
java·spring boot·后端
一只自律的鸡41 分钟前
数据结构 树2
数据结构
ChinaRainbowSea1 小时前
八. Spring Boot2 整合连接 Redis(超详细剖析)
java·数据库·spring boot·redis·后端·nosql
MATLAB代码顾问1 小时前
MATLAB实现多种群遗传算法
开发语言·matlab
小咕聊编程1 小时前
【含文档+PPT+源码】基于小程序的智能停车管理系统设计与开发
java·spring boot·小程序
叫我DPT1 小时前
Go 中 defer 的机制
开发语言·后端·golang
幻想趾于现实2 小时前
C# 装箱和拆箱(以及 as ,is)
开发语言·c#
某个默默无闻奋斗的人2 小时前
三傻排序的比较(选择,冒泡,插入)
java·数据结构·算法
敲上瘾3 小时前
BFS(广度优先搜索)——搜索算法
数据结构·c++·算法·搜索引擎·宽度优先·图搜索算法