CherryUSB 中的 XHCI 驱动分析

本文介绍USB基础知识,涵盖USB的设计理念、接口类型、主机控制器类型及其软件工具。解析USB2.0协议,探讨USB描述符、USBHUB特性及CherryUSB协议栈实现。

0. 参考链接

  • USB基础知识概论
  • 《USB Complete》,全面介绍了 USB 相关的知识
  • 《USB in a Nutshell》,介绍了 USB 的必要知识

1. 关于USB

1.1 USB 是什么

  • USB是Universal Serial Bus的缩写,USB是一种简易、双向、快速、同步、即插即用(Plug and Play,PnP)且支持热插拔功能的串行接口

  • USB出现之前,计算机领域中的接口太多太繁杂,普通用户使用起来难度较大,接口之间的兼容性也较差

  • USB设计理念是,实现计算机设备和通讯设备的完美融合,支持即插即用不需要用户手动配置,提高接口扩展性在不同应用上使用统一的接口,总的来说,USB的出现,是希望通过此单个的USB接口,同时支持多种不同的应用,而且用户用起来也很方便,直接插上就能用了,也方便不同的设备的之间的互联

  • USB的优点包括,

      1. 通用性,使用同样的线缆和接口,支持多种类型的设备
      1. 可扩展,通过USB端口,集线器可以添加更多的端口,连接最多127个设备
      1. 热插拔,从协议层面支持用户随时连接和断开USB设备
      1. 自动配置,USB设备连接后,可以上报设备类型等信息,支持自动配置
      1. 易设置,USB设备不需要用户配置中断线,断开地址等
      1. 多种速率,支持多种速率的设备
      1. 高可靠,在硬件和协议层面保证通信的可靠性
  • USB设备,从物理上的逻辑结构来说,包含了主机Host端和设备Device端, 其中,主机Host端,有对应的硬件的USB的主机控制器Host Controller,而设备端,连接的是对应的USB设备

在这里插入图片描述

1.2 USB 接口

  • 下面就来简单的介绍一下不同的USB接口类型,即各种不同的插头插座:

  • USB的接口类型,根据接口形状不同,主要可以分为三大类:

      1. 普通的硬件直接叫做Type
      1. 然后有小型版本的叫Mini迷你的
      1. 和更加小的,叫做Micro微小的
  • 其中每一种大类中,又都可以分为两类

      1. A类(Type A)
      1. B类(Type B)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xhgOFU4T-1665644089017)(figsif_types.png)]

  • 这里具体讨论的 XHCI 的主机接口,对应A类插座和插头

usb connect

[usb3.0,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFas9dCm-1665644089019)(figsif_tbl.png)]在这里插入图片描述

1.3 USB 主机有哪些

  • OHCI,Open Host Controller Interface,创立者是Compaq,Microsoft和National Semiconductor。只支持USB 1.1
  • UHCI,Universal Host Controller Interface,创立者是Intel。只支持USB 1.1
  • EHCI,Enhanced Host Controller Interface,创立者是Intel。支持 USB 2.0
  • xHCI,Extensible Host Controller Interface, 创立者是Intel。支持 USB 3.0 / USB 3.1
  • 更新的主机控制器这里就不讨论了

在这里插入图片描述

1.4 USB 相关的软件和工具

  • USB Device(如鼠标\键盘\U盘)的驱动和固件,USB Device需要相应标准的USB请求,完成数据的读取和写入,USB Device通常是由一个单片机完成,USB请求的响应由单片机的固件/裸机/RTOS完成
  • USB Host的驱动,USB Host需要按照标准,主动发起USB请求,接受USB Device返回的响应,常见的USB Host运行在Linux\Windows等大型操作系统上
  • USB 测试和协议分析软件,USB通信出现问题时,需要一些调试工具,常见的有以下几类,
      1. USB 硬件抓包工具,例如 Ellisys的USB Explorer 260硬件,配合Ellisys USB Analysis Software,可以实现USB数据抓包和分析,分析抓取出来的数据,判断通信过程是否符合期望,满足USB协议的规范定义。还有 Bus Hound,是一类软件总线协议分析器,通过传软件可以监视USB总线上的数据包
      1. USB 总线查看工具,例如 Windows 上的USBView, 可以浏览Windows上所有的USB主机控制器和USB设备,查看其详细信息
      1. USB 兼容性测试工具,例如 USB20CV,是一个USB标准化组织推荐的 USB 设备测试框架,运行在Windows上,可以测试USB Device是否符合规范

1.5 USB 协议

在这里插入图片描述

  • 这里主要讨论 USB 2.0协议,不同版本的 USB 协议,其最显著的差别在于通信速率的支持,USB 2.0可以支持Low,Full,High三种速率,3.0可以支持Super, 3.1可以支持 Super plus,2.0协议中主要有以下几部分
    1. 基本的电气规范,机械结构
    1. 主机和设备之间的拓扑关系
    1. 通信的数据流类型和速率
    1. 通信的机制,端点、管道的概念,控制、中断、批量和等时四种传输类型
    1. 数据包的具体定义,报告状态和握手协议
    1. USB HOST的驱动模型,USB总线枚举设备的过程,USB标准Request的定义
    1. USB HUB的规范,HUB的配置,Split传输等
    1. USB Device的规范

1.6 USB 协议细节

  • USB系统的核心是HOST,单个USB总线上,只能有一个HOST, 在支持OTG后,USB为了支持多个设备互联(比如一个数码相机和一个打印机),引入了HNP(Host Negotiation Protocol),主机协商协议,两个设备可以协商决定谁当HOST
  • 一个USB总线中,最多可以连接127个设备,设备的分类通过Class完成,常见的键盘鼠标(HID 0x3),U盘 (Mass Storage 0x8),集线器(Hub 0x9)

在这里插入图片描述

  • USB枚举,USB Emulation,对应的就是USB的Host和Device之间的对话,即Host根据Device所报告上来的参数,得知USB的device是啥类型的,具有啥功能,然后初始化相关参数,接下来,就USB Device就可以正常工作了,下面是关于windows下USB枚举的过程的总结:

      1. The host or hub detects the connection of a new device via the device’s pull up resistors on the data pair. The host waits for at least 100ms allowing for the plug to be inserted fully and for power to stabilise on the device.
      1. Host issues a reset placing the device is the default state. The device may now respond to the default address zero.
      1. The MS Windows host asks for the first 64 bytes of the Device Descriptor.
      1. After receiving the first 8 bytes of the Device Descriptor, it immediately issues another bus reset.
      1. The host now issues a Set Address command, placing the device in the addressed state.
      1. The host asks for the entire 18 bytes of the Device Descriptor.
      1. It then asks for 9 bytes of the Configuration Descriptor to determine the overall size.
      1. The host asks for 255 bytes of the Configuration Descriptor.
      1. Host asks for any String Descriptors if they were specified.
      1. At the end of Step 9, Windows will ask for a driver for your device. It is then common to see it request all the descriptors again before it issues a Set Configuration request.
  • USB协议中定义了四种传输(Transfer)类型

      1. 控制传输(Control Transfer)
      1. 中断传输(Interrupt Transfer)
      1. 批量传输(Bulk Transfer)
      1. 同步传输(Isochronous Transfer)
  • USB 描述符,USB协议本身很复杂,但方便在提供了统一的接口方式,这种方便的一个重要原因是 USB 协议规范了一套通信用的数据结构,也就是 USB 描述符,标准的USB设备有5种USB描述符:设备描述符,配置描述符,字符串描述符,接口描述符,端点描述符,USB标准化组织和各厂商根据特定的通信需要,还扩展了一系列描述符定义

      1. 设备描述符:一个设备只有一个设备描述符,由于描述当前设备的信息,如设备类型、产品ID,厂商ID等
      1. 配置描述符: 配置描述符定义了设备的配置信息,一个设备可以有多个配置描述符,每个配置代表了一种功能,有些设备支持多种功能,这样的设备就会提供多个配置描述符,不过主机在枚举USB设备的时候需要选择一个配置生效
      1. 接口描述符:接口描述符说明了接口所提供的配置,一个配置所拥有的接口数量通过配置描述符的bNumInterfaces决定,一个接口描述符对应一个特定功能,复合设备会支持多个功能,这时候会有多个接口描述符,例如一个带网卡的Hub, 会提供一个Class为0x9的接口描述符和一个0xa的接口描述符
      1. 端点描述符:USB设备中的每个端点都有自己的端点描述符,由接口描述符中的bNumEndpoint决定其数量,端点用于支持接口通信,一个双向通信的设备需要至少两个端点
  • USB 通信包,USB 2.0协议中,USB包由SOP,SYNC,Packet内容和EOP组成,SOP是起始包,SYNC是同步域,采用NRZI编码,Packet内容包括PID(包ID),地址,帧号,数据和CRC校验,最后EOP是结束包,不同的Packet内容构成令牌包、握手包和数据包等,多个包组合形成事务,是USB传输的基础

在这里插入图片描述

1.7 USB HUB

  • USB HUB用于设备扩展连接,所有USB DEVICE都连接在USB HUB的端口上。一个USB HOST总与一个根HUB (USB ROOT HUB)相连
  • USB HUB为其每个端口提供100mA电流供设备使用, 同时,USB HUB可以通过端口的电气变化诊断出设备的插拔操作,并通过响应USB HOST的数据包把端口状态汇报给USB HOST
  • 一般来说,USB设备与USB HUB间的连线长度不超过5m,USB系统的级联不能超过5级(包括ROOT HUB)

2. CherryUSB 分析

2.1 CherryUSB 框架

freamwork

  • CherryUSB 是一个用于嵌入式系统的 USB 主从协议栈,通过分析 CherryUSB 的框架结构,我们可以对USB 整体有一个感性认识
  • CherryUSB 作为主机协议栈,在中断模式下完成USB通信,可以支持 HUB、Mass Storage、HID类,实现USB 扩展、存储、人机交互等功能,支持 VIDEO、Remote NDIS 和 AxuNet类,实现视频传输、无线网络、有线以太网等功能
  • CherryUSB 作为从机协议栈,响应主机的USB请求,可以实现USB 扩展、存储、人机交互等设备功能,实现视频传输、无线网络、有线以太网等设备
  • 下面主要从主机协议栈出发进行介绍

2.2 CherryUSB 目录结构

  • core 目录实现了 USB HOST的功能
  • class 目录封装了一系列标准 USB 设备类型的接口
  • osal 目录针对不同的 RTOS 进行了适配
  • port 目录针对不同的 USB HOST控制器实现了功能
目录名 描述
class usb class 类主从驱动
common usb spec 定义、常用宏、标准接口定义
core usb 主从协议栈核心实现
demo 示例
docs 文档
osal os 封装层
packet capture 抓包文件(需要使用力科软件打开)
port usb 主从需要实现的 porting 接口
tools 工具链接
.
├── class
├── common
├── core
├── demo
├── docs
├── osal
├── packet capture
└── port
└── tools

在这里插入图片描述

2.3 CherryUSB 源码分析

2.3.1 USB HOST 初始化

  • usbh_initialize 是初始化主机协议栈
  • (1) 清零了 USB BUS 上挂的设备数据
    -(2)获取了链接脚本中定义的class段起止地址,所有的 USB Class 驱动入口函数都在这个class段里
  • (3) roothub 是不用初始化的,初始化从第一个外挂的 hub 开始
struct usbh_devaddr_map {
    /**
     * alloctab[0]:addr from 0~31
     * alloctab[1]:addr from 32~63
     * alloctab[2]:addr from 64~95
     * alloctab[3]:addr from 96~127
     *
     */
    uint8_t next;         /* Next device address */
    uint32_t alloctab[4]; /* Bit allocation table */
};

struct usbh_bus {
    struct usbh_devaddr_map devgen;
} g_usbh_bus;

int usbh_initialize(void)
{
    memset(&g_usbh_bus, 0, sizeof(struct usbh_bus)); (1)
    
    extern uint32_t __usbh_class_info_start__;
    extern uint32_t __usbh_class_info_end__;
    usbh_class_info_table_begin = (struct usbh_class_info *)&__usbh_class_info_start__;
    usbh_class_info_table_end = (struct usbh_class_info *)&__usbh_class_info_end__; (2)

    /* devaddr 1 is for roothub */
    g_usbh_bus.devgen.next = 2; (3)

    usbh_hub_initialize();
    return 0;
}
  • 创建一个任务/线程,usbh_hub,入口函数为 usbh_hub_thread
  • (1)调用 port 中 HOST 控制器驱动实现的初始化函数 usb_hc_init,初始化硬件驱动
  • (2)完成 HOST 控制器初始化后,usbh_hub 任务就死等上报的hub事件了
  • (3)如果收到了hub事件,在 usbh_hub_events 中依次处理各个事件
  • 到这里为止,HOST 控制器的硬件初始化完成了,但是USB BUS上还没有设备,后面通过处理hub事件,协议栈会逐步把连接的设备枚举起来
int usbh_hub_initialize(void)
{
   
   
    usbh_roothub_register();

    hub_event_wait = usb_osal_sem_create(0);
    if (hub_event_wait == NULL) {
   
   
        return -1;
    }

    hub_thread = usb_osal_thread_create("usbh_hub", CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_hub_thread, NULL);
    if (hub_thread == NULL) {
   
   
        return -1;
    }
    return 0;
}

static void usbh_hub_thread(void *argument)
{
   
   
    size_t flags;
    int ret = 0;

    usb_hc_init();1while (1) {
   
   
        ret = usb_osal_sem_take(hub_event_wait, 0xffffffff);2if (ret < 0) {
   
   
            continue;
        }

        while (!usb_slist_isempty(&hub_event_head)) {
   
   3struct usbh_hub *hub = usb_slist_first_entry(&hub_event_head, struct usbh_hub, hub_event_list);
            flags = usb_osal_enter_critical_section();
            usb_slist_remove(&hub_event_head, &hub->hub_event_list);
            usb_osal_leave_critical_section(flags);
            usbh_hub_events(hub);
        }
    }
}

2.3.2 USB 设备的发现和端口事件的处理

  • 设备的发现和端口事件的上报是在中断中完成的
  • 协议栈要求port的每个HOST控制器驱动都实现 中断响应函数 USBH_IRQHandler,在中断中检测端口连接状态的变化,并上报端口事件
  • 不同的HOST控制器检查端口连接状态的方法不一样,这里以XHCI为例,(1) 进入中断后,检查XHCI的EVENT TRB,如果EVENT TRB的类型是Port Status Change (2),读取对应端口寄存器的状态,是否为刚刚改变(3),如果是,调用 usbh_roothub_thread_wakeup 上报端口事件,唤醒端口
  • usbh_hub_thread 检测到端口事件,阻塞状态解除,调用 usbh_hub_events 处理端口事件
void USBH_IRQHandler(void)
{
   
   
    uint32_t reg, status;
    struct xhci_s *xhci = &xhci_host;
    struct xhci_ring *evts = xhci->evts;
    struct xhci_pipe *work_pipe = NULL;

...

    /* check and ack event */
    for (;;) {
   
    (1)
        uint32_t nidx = evts->nidx; /* index of dequeue trb */
        uint32_t cs = evts->cs; /* cycle state toggle by xHc */
        struct xhci_trb *etrb = evts->ring + nidx; /* current trb */
        uint32_t control = etrb->control; /* trb control field */

        if ((control & TRB_C) != (cs ? 1 : 0)) /* if cycle state not toggle, no events need to handle */
            break;

        /* process event on etrb */
        uint32_t evt_type = TRB_TYPE_GET(control);
        uint32_t evt_cc = TRB_CC_GET(etrb->status); /* bit[31:24] completion code */
    
        if (ER_PORT_STATUS_CHANGE == evt_type) {
   
   2/* bit[31:24] port id, the port num of root hub port that generated this event */
            uint32_t port = TRB_PORT_ID_GET(etrb->ptr_low) - 1;
            uint32_t portsc = xhci_readl_port(xhci, port, XHCI_REG_OP_PORTS_PORTSC); /* Read status */

            xhci_print_port_state(__func__, port, portsc);
            if (portsc & XHCI_REG_OP_PORTS_PORTSC_CSC) {
   
   3usbh_roothub_thread_wakeup(port + 1); /* wakeup when connection status changed */
            }
  • 进入 usbh_hub_events 后,协议栈会复查端口状态(1),如果端口处于连接可用状态,通过 hub 的标志请求重置端口(2),端口重置完成后,会检查端口上报的连接速率(3),随后在 hub 上创建一个 child 节点,用于放置之后枚举的设备
  • 值得注意的是,对于挂在普通hub上的端口,usbh_hub_get_portstatus、usbh_hub_set_feature 等 hub 动作是走的 USB 标志请求,但是对于直接挂在root-hub上的端口,协议栈要求 HOST 控制器驱动实现 usbh_roothub_control,用于完成获取端口状态、重置端口等动作
  • 端口的准备工作完成后,即将进入下一个阶段,调用 usbh_enumerate 尝试枚举在端口上刚刚发现的 USB 设备 (4)
static void usbh_hub_events(struct usbh_hub *hub)
{
   
   

    。。。
        /* Read hub port status */
        ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);1...
            /* Last, check connect status */
            if (portstatus & HUB_PORT_STATUS_CONNECTION) {
   
   
                ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_RESET);2if (ret < 0) {
   
   
                    USB_LOG_ERR("Failed to reset port %u,errorcode:%d\r\n", port, ret);
                    continue;
                }

                usb_osal_msleep(DELAY_TIME_AFTER_RESET);
                /* Read hub port status */
                ret = usbh_hub_get_portstatus(hub, port + 1, &port_status);
                if (ret < 0) {
   
   
                    USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret);
                    continue;
                }

                portstatus = port_status.wPortStatus;
                portchange = port_status.wPortChange;
                if (!(portstatus & HUB_PORT_STATUS_RESET) && (portstatus & HUB_PORT_STATUS_ENABLE
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值