拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RT-Thread CherryUSB 中 OHCI 主机控制器驱动的移植与配置指南

RT-Thread CherryUSB 中 OHCI 主机控制器驱动的移植与配置指南
  • 操作系统
  • 嵌入式
  • 物联网
  • 嵌入式OS
  • RTOS

【免费下载链接】rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/

项目地址:https://gitcode.com/gh_mirrors/rt/rt-thread
点击查看免费下载

导读

本文以 components/drivers/usb/cherryusb/port/ohci/README.md 为骨架,系统讲解 RT-Thread 中 CherryUSB 组件所携带的 OHCI(Open Host Controller Interface)USB 主机控制器驱动:它既是 EHCI 的伴生控制器(companion controller),也可脱离 EHCI 独立承担低速(LS)/全速(FS)USB 设备的接入。读完本文,你将掌握 OHCI 驱动的两种工作模式(伴生/独立)、官方支持芯片清单、全部相关配置宏的含义、初始化与中断处理流程,以及基于 port/ohci 目录源码将其移植到新平台的具体步骤。


OHCI 与 EHCI:伴生控制器的来龙去脉

在 USB 主机控制器的发展历史上,EHCI(Enhanced Host Controller Interface)负责高速(HS,480 Mbps)传输,而全速(12 Mbps)与低速(1.5 Mbps)设备则需要由另一套控制器来处理。行业惯例是让 EHCI 配一个“伴生控制器”(companion controller),把低速/全速端口“分流”给后者。OHCI 正是这种经典伴生控制器角色:

This OHCI is a companion controller of EHCI. But you can use OHCI only withoutCONFIG_USB_EHCI_WITH_OHCIdefinition.

这句话点明了本驱动最核心的设计定位,包含两层含义:

  1. 默认是 EHCI 的伴生控制器:在 EHCI 主机控制器上,低速/全速设备通过 OHCI 端口接入,高速设备走 EHCI 端口。
  2. 也可以独立使用:只要不定义CONFIG_USB_EHCI_WITH_OHCI宏,OHCI 驱动会自行注册为完整的 USB 主机控制器(HCD),单独完成根集线器枚举与 URB 提交,此时它不再依赖 EHCI。

这一设计在源码中得到了直接印证:port/ohci/usb_hc_ohci.c 末尾有一段被#ifndef CONFIG_USB_EHCI_WITH_OHCI包起来的“独立模式适配层”:当宏未定义时,驱动会补齐usb_hc_init、usb_hc_deinit、usbh_roothub_control、usbh_submit_urb、usbh_kill_urb、USBH_IRQHandler等 HCD 顶层接口,全部转发到对应的ohci_*函数,从而作为一个完整的主机栈独立运行。而当宏被定义时,这些顶层接口由 EHCI 驱动(port/ehci/usb_hc_ehci.c)提供,OHCI 仅暴露ohci_*内部接口供 EHCI 在初始化、枚举、URB 路由时按端口属性调用。


两种工作模式:EHCI 伴生与 OHCI 独立

伴生模式:定义 CONFIG_USB_EHCI_WITH_OHCI

当平台同时具备 EHCI 与 OHCI 硬件(例如 Allwinner F133、Artinchip d13x/d21x、Nuvoton 全系列),推荐开启伴生模式。需要在配置文件(如 cherryusb_config_template.h 所示的模板,实际项目通常复制为cherryusb_config.h)中取消以下宏的注释:

#define CONFIG_USB_EHCI_WITH_OHCI

在伴生模式下,EHCI 初始化流程会按“是否内置 Transaction Translator(TT)”决定 OHCI 的启用方式,见 port/ehci/usb_hc_ehci.c:

if (g_ehci_hcd[bus->hcd.hcd_id].has_tt) { #ifdef CONFIG_USB_EHCI_WITH_OHCI USB_LOG_INFO("EHCI uses tt for ls/fs device, so cannot enable this macro\r\n"); return -USB_ERR_INVAL; #endif } ... if (g_ehci_hcd[bus->hcd.hcd_id].has_tt) { USB_LOG_INFO("EHCI uses tt for ls/fs device\r\n"); } else { #ifdef CONFIG_USB_EHCI_WITH_OHCI USB_LOG_INFO("EHCI uses companion controller for ls/fs device\r\n"); ohci_init(bus); #else USB_LOG_WRN("Do not enable companion controller, you should use a hub to support ls/fs device\r\n"); #endif }

从源码可以看出两个关键结论:

  • 如果 EHCI 控制器自带 TT(如 MCU 内部集成的 chipidea 控制器),则不应开启CONFIG_USB_EHCI_WITH_OHCI,否则直接返回-USB_ERR_INVAL;
  • 如果 EHCI 没有 TT,则定义该宏后会自动调用ohci_init(bus)初始化伴生控制器,用于处理低速/全速设备;若未定义宏,驱动会告警提示“需要外接 HUB 才能支持 ls/fs 设备”。

同理,在 port/ehci/usb_hc_ehci.c 中,usb_hc_deinit、usbh_roothub_control等接口也通过#ifdef CONFIG_USB_EHCI_WITH_OHCI将 OHCI 的ohci_deinit、ohci_roothub_control、ohci_get_frame_number纳入 EHCI 的宿主路径;URB 层面则依据端点的速度属性在 EHCI/OHCI 之间分发(对应 usb_hc_ehci.c 的usbh_submit_urb/usbh_kill_urb)。

另外注意:伴生模式下 OHCI 的寄存器基址不再从 0x0 开始,需要通过CONFIG_USB_OHCI_HCOR_OFFSET指定偏移。例如 Artinchip(AIC)平台要求该值为 0x400,其 glue 层在 port/ehci/usb_glue_aic.c 中有硬性校验:

#if CONFIG_USB_OHCI_HCOR_OFFSET != 0x400 #error "aic CONFIG_USB_OHCI_HCOR_OFFSET must be 0x400" #endif

Allwinner T113 平台同样要求 0x400(见 port/ehci/usb_glue_t113.c),并额外提供了usb_select_phyTohci()这类 PHY 切换辅助函数,以及将 OHCI 中断注册到vector + 1的t113_ohci_isr。

独立模式:不定义 CONFIG_USB_EHCI_WITH_OHCI

当目标芯片只有 OHCI 硬件(典型如 NXP LPC4X/LPC5X 系列)时,保持CONFIG_USB_EHCI_WITH_OHCI不定义即可。此时 port/ohci/usb_hc_ohci.c 中的usb_hc_init会调用usb_hc_low_level_init(bus)(弱函数,由各平台 glue 文件实现底层时钟/复位/PHY 使能)后再执行ohci_init(bus),整套主机栈完全由 OHCI 驱动承载,行为与 EHCI 独立使用完全对等。


授权与商用注意

原文档明确强调了一条不可忽视的注意事项:

And you need to pay for using OHCI driver.

也就是说,CherryUSB 的这份 OHCI 驱动(代码衍生自 NuttX 的include/nuttx/usb/ohci.h寄存器定义体系,见 usb_ohci_reg.h 的版权头)在商用场景下需要向原作者取得付费授权,这一点与原文档中 EHCI 等其他控制器驱动的开源许可策略不同。在将 OHCI 驱动用于产品、商业固件发布之前,请务必确认授权条款;评估项目是否真的需要 OHCI(例如芯片是否有 EHCI+TT 方案可替代),可以在立项阶段就避免后续授权成本。


官方支持芯片清单

原文档给出了驱动已验证/官方支持的芯片列表,这里完整继承并补充说明:

芯片厂商型号/系列支持模式
AllwinnerTech(全志)F133EHCI + OHCI(伴生)
Nuvoton(新唐)全系列EHCI + OHCI 或 OHCI 独立
Artinchip(矽昌)d13x、d21xEHCI + OHCI(伴生)
NXPLPC4X / LPC5X仅 OHCI(独立)

几点展开说明:

  • AllwinnerTech F133:属于 Allwinner D1 家族周边芯片,EHCI+OHCI 伴生,OHCI 寄存器偏移为 0x400,相关移植参考 port/ehci/usb_glue_t113.c(T113 与 F133 控制器同源)。
  • Nuvoton 全系列:既支持伴生模式也支持 OHCI 独立模式,用户可根据具体型号硬件决定。
  • Artinchip d13x/d21x:伴生模式,CONFIG_USB_OHCI_HCOR_OFFSET必须为 0x400,中断注册示例见 port/ehci/usb_glue_aic.c。
  • NXP LPC4X/LPC5X:只支持 OHCI 独立模式。对应的平台 glue 层为 port/ohci/usb_glue_lpc.c,该文件开头直接声明:
#if defined(CONFIG_USB_EHCI_WITH_OHCI) #error "lpc does not have ehci" #endif

即 LPC 平台如果误开伴生宏会直接编译报错,从源头杜绝错误配置。


源码构成与文件职责

port/ohci 目录共 5 个文件,职责划分如下:

文件职责
usb_hc_ohci.cOHCI 主机控制器核心实现:初始化/去初始化、根集线器控制、帧号读取、URB 提交/终止、中断处理
usb_hc_ohci.h内部数据结构(ED/TD 池、HCD 状态)与对外ohci_*接口声明、配置宏默认值
usb_ohci_reg.hOHCI 寄存器偏移/位定义、struct ohci_hcor(操作寄存器组)、ED/TD/HCCA 结构体
usb_glue_lpc.cNXP LPC4X/LPC5X 平台底层 glue:PHY 上电、时钟使能、复位、NVIC 中断注册
README.md本文档骨架:模式说明、授权提示、支持芯片清单

核心结构体定义集中在 usb_hc_ohci.h:

  • struct ohci_td_hw:每个 Transfer Descriptor(TD)包含硬件struct ohci_gtd hw字段,附带软件上下文urb、方向标志dir_in、缓冲区起始地址buf_start、长度length,整体按 cacheline(CONFIG_USB_OHCI_ALIGN_SIZE,32 或 64 字节)对齐;
  • struct ohci_ed_hw:每个 Endpoint Descriptor(ED)内嵌一个大小为CONFIG_USB_OHCI_TD_NUM的 TD 池td_pool[],并携带waitsem信号量用于 URB 完成等待;
  • struct ohci_hcd:维护 ED 占用位图ohci_ed_used[]与根集线器端口数n_ports。

usb_ohci_reg.h开头的版权声明说明其寄存器定义源自 NuttX 的include/nuttx/usb/ohci.h,按 Apache-2.0 许可随 CherryUSB 进入 RT-Thread,这也正是前面“商用需付费授权”提示的由来——寄存器头文件本身遵循开源许可,但驱动实现与移植层的使用条款以原项目声明为准。


关键配置项详解

OHCI 驱动的全部可配置宏在 cherryusb_config_template.h 的 “OHCI Configuration” 段集中给出,模板中的默认值如下:

/* ---------------- OHCI Configuration ---------------- */ #define CONFIG_USB_OHCI_HCOR_OFFSET (0x0) #define CONFIG_USB_OHCI_ED_NUM 10 #define CONFIG_USB_OHCI_TD_NUM 3 // #define CONFIG_USB_OHCI_DESC_DCACHE_ENABLE

各宏含义与源码依据:

配置宏默认值含义源码依据
CONFIG_USB_OHCI_HCOR_OFFSET0x0OHCI 操作寄存器组(HcControl 等)相对bus->hcd.reg_base的偏移。独立模式为 0x0;AIC/T113 伴生模式必须为 0x400usb_hc_ohci.h 中OHCI_HCOR宏定义
CONFIG_USB_OHCI_ED_NUM10ED(端点描述符)池大小,即同时可管理的端点描述符数量usb_hc_ohci.h,未定义时默认为 10
CONFIG_USB_OHCI_TD_NUM3每个 ED 内置的 TD(传输描述符)数量,决定单个端点可排队的传输数usb_hc_ohci.h,默认 3
CONFIG_USB_OHCI_DESC_DCACHE_ENABLE未定义使能描述符 D-Cache 维护。定义后 ED/TD 结构体会附加 cacheline 填充(32 字节对齐时补 16 字节、64 字节对齐时补 48 字节),避免 cache 一致性问题usb_hc_ohci.h
CONFIG_USB_EHCI_WITH_OHCI未定义伴生模式开关:定义后 OHCI 作为 EHCI 伴生控制器;不定义则 OHCI 独立usb_hc_ohci.c 条件编译区

此外还有两个由CONFIG_USB_ALIGN_SIZE派生的约束,见 usb_hc_ohci.h:

#if CONFIG_USB_ALIGN_SIZE <= 32 #define CONFIG_USB_OHCI_ALIGN_SIZE 32 #elif CONFIG_USB_ALIGN_SIZE <= 64 #define CONFIG_USB_OHCI_ALIGN_SIZE 64 #else #error "CONFIG_USB_ALIGN_SIZE must be 32 or 64" #endif

CONFIG_USB_ALIGN_SIZE必须为 32 或 64,否则编译报错。CONFIG_USB_OHCI_ALIGN_SIZE与 cacheline 对齐要求直接相关:ohci_init会逐一校验 ED 与 TD 是否按 32 字节对齐,不满足则打印USB_LOG_ERR并返回-USB_ERR_INVAL(见 usb_hc_ohci.c)。此外 ED/TD/HCCA 池均以USB_NOCACHE_RAM_SECTION放到非缓存 RAM 段(usb_hc_ohci.c),这一段的定义通常在各 BSP 的链接脚本中实现,移植时需要确保芯片有可用的 non-cacheable RAM。


初始化流程与寄存器操作

ohci_init(usb_hc_ohci.c)按 OHCI 规范完成了从复位到运行态的完整序列:

  1. 软件上下文清零:清空g_ohci_hcd、ED 池与 HCCA 中断表(32 个表项),并为每个 ED 创建waitsem信号量;
  2. 打印版本:读取HcRevision寄存器并打印(USB_LOG_INFO("OHCI hcrevision:0x%02x"));
  3. 屏蔽中断并复位控制器:写HcInterruptDisable = OHCI_INT_MIE,清空HcControl,置位HcCommandStatus.HCR发起控制器复位,轮询等待 HCR 清零(超时 100ms 返回-USB_ERR_TIMEOUT);
  4. 配置帧时序:写入HcFmInterval = DEFAULT_FMINTERVAL(帧间隔 12000 bit time 减 1,FSMPS 按公式(6 * (FI - 210)) / 7计算)、HcPeriodicStart = (FI * 9) / 10、HcLSThreshold = 0x628,对应 usb_hc_ohci.c 顶部的宏定义——注释明确指出“在 12 Mbps 下,每 1 ms 帧有 12000 个 bit time”;
  5. 清空列表指针并绑定 HCCA:HcControlHeadED/HcBulkHeadED置 0,HcHCCA指向ohci_hcca[](该结构在 usb_ohci_reg.h 中要求 256 字节对齐);
  6. 清挂起中断,进入 Operational 态:回写HcInterruptStatus清中断,设置HcControl的 HCFS 为 OPER、使能 Control/Bulk 列表(CLE);
  7. 读取端口数并配置根集线器:从HcRhDescriptorA取NDP(Number Downstream Ports)存入n_ports,清除 PSM/NPS 位,置HcRhStatus的 SGP(Set Global Power)并延时 20ms;
  8. 使能中断:HcInterruptEnable = WDH | RHSC | MIE(写回完成头、根集线器状态变化、主中断使能)。

ohci_deinit(usb_hc_ohci.c)是逆过程:屏蔽并清除中断、清除 SGP、将 HCFS 置为 SUSPEND、删除全部waitsem。

ohci_get_frame_number(usb_hc_ohci.c)直接返回HcFmNumber寄存器低 16 位,供主机栈计算帧号/调度周期传输。


根集线器(Root Hub)控制与中断处理

OHCI 驱动实现了完整的根集线器虚拟 HUB 语义。ohci_roothub_control(usb_hc_ohci.c)解析 USB 标准 HUB 类请求并映射到 OHCI 根集线器寄存器:

  • 设备级请求(RECIPIENT_DEVICE):支持HUB_FEATURE_HUB_C_LOCALPOWER、HUB_FEATURE_HUB_C_OVERCURRENT的 CLEAR/SET、GET_DESCRIPTOR、GET_STATUS;
  • 端口级请求(RECIPIENT_OTHER):覆盖HUB_PORT_FEATURE_ENABLE、SUSPEND/C_SUSPEND、POWER、C_CONNECTION、C_ENABLE、C_OVER_CURREN、C_RESET的 CLEAR_FEATURE 与 SET_FEATURE(含RESET端口复位及等待 PRS 位清零的忙等),以及 GET_STATUS(直接拷贝HcRhPortStatus[n]4 字节)。 端口号由setup->wIndex携带,越界(!port || port > nports)返回-USB_ERR_INVAL。

中断处理入口为OHCI_IRQHandler(uint8_t busid)(usb_hc_ohci.c),按HcInterruptStatus & HcInterruptEnable区分事件源:

  • RHSC(根集线器状态变化):遍历CONFIG_USB_HOST_MAX_RHPORTS个端口,若端口CSC(连接状态变化)置位:当HcRhStatus.DRWE(Device Remote Wakeup Enable)置位时视为远程唤醒事件不做处理;否则将对应端口位写入bus->hcd.roothub.int_buffer[port + 1]并调用usbh_hub_thread_wakeup唤醒 HUB 线程执行插拔处理;
  • WDH(Writeback Done Head):清中断标志(传输完成队列的进一步消费由主机栈侧完成)。

独立模式下,USBH_IRQHandler直接转发给OHCI_IRQHandler;伴生模式下则分别由各平台的 OHCI 中断服务例程(如 AIC 的aic_ohci_isr、T113 的t113_ohci_isr)调用。


RT-Thread 集成方式(SConscript)

RT-Thread 通过菜单配置(Kconfig)与 SConscript 构建脚本集成 CherryUSB。在 components/drivers/usb/cherryusb/SConscript 中,AIC 平台的 EHCI+OHCI 组合构建逻辑为:

if GetDepend(['RT_CHERRYUSB_HOST_EHCI_AIC']): path += [cwd + '/port/ehci'] path += [cwd + '/port/ohci'] src += Glob('port/ehci/usb_hc_ehci.c') src += Glob('port/ehci/usb_glue_aic.c') src += Glob('port/ohci/usb_hc_ohci.c')

即选中RT_CHERRYUSB_HOST_EHCI_AIC后,port/ohci目录会同时加入头文件搜索路径,usb_hc_ohci.c会被编译进工程——此时便要求开启CONFIG_USB_EHCI_WITH_OHCI(AIC glue 的#error会强制这一点)。对应的 CMake 集成见 cherryusb.cmake。对于 NXP LPC4X/LPC5X 这类仅 OHCI 的平台,则应选择独立的 OHCI 主机选项,使 SConscript 走usb_glue_lpc.c的路径。


移植到新平台的一般步骤

综合以上源码分析,将 OHCI 驱动移植到新芯片可按以下步骤进行(从源码结构看,这是 CherryUSB 各 port 目录的通例):

  1. 确认硬件归属:确认目标芯片的 USB 控制器是 OHCI(或 EHCI+OHCI 伴生),确定寄存器基址与 OHCI 操作寄存器偏移;
  2. 复制配置模板:以 cherryusb_config_template.h 为模板生成cherryusb_config.h,按需设置CONFIG_USB_OHCI_HCOR_OFFSET、CONFIG_USB_OHCI_ED_NUM、CONFIG_USB_OHCI_TD_NUM,必要时开启CONFIG_USB_OHCI_DESC_DCACHE_ENABLE;
  3. 实现 glue 层:仿照 port/ohci/usb_glue_lpc.c 实现usb_hc_low_level_init(时钟、复位、PHY 上电、NVIC 中断使能)与usb_hc_low_level_deinit,并实现XXX_IRQHandler调用USBH_IRQHandler(0);独立模式下这是必须的,伴生模式下则由 EHCI glue 中的ohci_isr承担;
  4. 提供非缓存 RAM 段:ED/TD/HCCA 池声明为USB_NOCACHE_RAM_SECTION,需在链接脚本中预留 non-cacheable 内存,否则需开启CONFIG_USB_OHCI_DESC_DCACHE_ENABLE配合 cacheline 对齐(注意CONFIG_USB_ALIGN_SIZE只能是 32 或 64);
  5. 接入构建系统:在 SConscript 中新增平台分支,把port/ohci/usb_hc_ohci.c与 glue 文件加入src、把port/ohci加入path,并在 Kconfig 中暴露对应的 host 选项;
  6. 验证枚举链路:上电后观察USB_LOG_INFO("OHCI hcrevision:0x%02x")与n_ports日志,确认控制器进入 Operational 态;插入全速/低速设备验证根集线器 CSC 中断、usbh_hub_thread_wakeup唤醒链路与设备枚举成功。

总结

RT-Thread 中的 CherryUSB OHCI 驱动是一份按 OHCI 规范实现的完整主机控制器驱动,既可作为 EHCI 的伴生控制器承担低速/全速设备接入,也可通过不定义CONFIG_USB_EHCI_WITH_OHCI独立运行,官方支持 AllwinnerTech F133、Nuvoton 全系列、Artinchip d13x/d21x 与 NXP LPC4X/LPC5X。使用时需特别注意其商用授权声明,并按 cherryusb_config_template.h 中的CONFIG_USB_OHCI_*系列宏正确配置 ED/TD 池、对齐与 cache 维护策略,参考 port/ohci/usb_glue_lpc.c 完成底层 glue 即可快速移植到新平台。

  • 操作系统
  • 嵌入式
  • 物联网
  • 嵌入式OS
  • RTOS

【免费下载链接】rt-thread

RT-Thread is an open source IoT Real-Time Operating System (RTOS). https://rt-thread.github.io/rt-thread/

项目地址:https://gitcode.com/gh_mirrors/rt/rt-thread
点击查看免费下载

相关推荐

上一篇:冴羽博客GitHub_Trending/blo/Blog:React系列文章太空殖民实践
下一篇:告别歪歪扭扭的扫描件:OCRmyPDF自动纠偏技术全解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表