
简介面向联发科MediaTek平台的USB主机控制器驱动USB HCD源代码主要服务于嵌入式驱动开发工程师、系统底层研究者以及需要为联发科设备定制USB功能的开发者。压缩包为rar格式共6个文件全部为C语言源码整体仅55KB代码量虽然精简但围绕USB驱动核心功能组织覆盖主机控制器驱动、USB Mass Storage类设备、日志调试等模块便于快速定位具体实现。目前已有228人学习下载对相关从业者具有一定参考价值。仔细阅读源码可以掌握联发科平台在USB枚举过程、配置与接口选择、端点管理、中断处理、批量/中断/控制/同步传输、电源管理以及故障恢复等环节的设计思路并可在此基础上进行驱动调试、性能瓶颈分析、新USB设备兼容性增强或扩展OTG等高级功能为实际项目提供稳定高效的驱动支持。1. 联发科 usb_driver 里的 usb_hcd 到底是什么拿到一份命名为usb_driver.rar_usb_hcd_联发科的压缩包先别急着解压它大概率不是某个小工具而是一整套 SoC 平台上的 USB 主机控制器驱动源码。usb_hcd 全称是 Host Controller DriverLinux USB 子系统里负责直接操作 USB 控制器硬件的层次USB Core 只是通用框架HCD 才是各芯片平台真正要做的部分。联发科平台把这份 HCD 单独打包常见动机是官方 BSP 里对时钟、PHY、OTG 切换做了私有定制主线内核自带的通用驱动覆盖不了。这套东西解决的是三类具体诉求板子起系统后 U 盘/网卡插上没反应、枚举报错刷屏、Type-C 口在 device 和 host 角色之间切换失败。读者对象基本是 BSP 工程师、驱动开发者和做公板转产品方案的硬件工程师。新手可以照后面的步骤把驱动编进内核熟手能从踩坑记录里对照自己遇到的现象。标题里的 rar 只是分发形式核心价值在那份 HCD 代码到底怎么写、怎么挂进内核、怎么调物理层参数。2. 为什么联发科的 usb_hcd 不能直接用内核主线自带驱动2.1 Linux USB 子系统的 HCD 层到底做了什么Linux USB 驱动从上到下分四层应用层、USB 设备驱动class driver / function driver、USB Core、HCD。HCD 是最贴近硬件的一环它把 USB Core 下发的 URBUSB Request Block翻译成控制器硬件能执行的寄存器操作、描述符读取、中断处理和 DMA 事务。USB Core 只关心「发一个 bulk 传输到端点 3」至于这个传输在硬件上是通过 EHCI 队列、xHCI 命令 Ring 还是私有 DMA 通道发全由 HCD 决定。内核里每个 HCD 的核心是struct usb_hcd和配套的struct hc_driver。usb_hcd承载控制器状态、镜像根 hub、带宽与端点管理等通用数据hc_driver才是厂商填回调函数的那个关键结构。usb_add_hcd()把两者绑定并注册到 USB Core之后 USB 子系统才能通过bus-op调用到厂商写的底层函数。hc_driver里最关键的几组回调回调函数职责.irq处理控制器中断把完成传输通知到 URB 层次.start/.stop控制器上电/下电初始化内部状态.urb_enqueue/.urb_dequeue提交/取消一个 URB例如 bulk 读、control 传输.hub_status_data/.hub_control查询并响应根 hub 的端口状态变化事件主线内核里的 ehci-hcd、xhci-hcd 也实现同一套接口但设备树和 PHY 初始化流程每家 IP 完全不同。联发科平台的 usb_hcd 包通常同时包含hc_driver实现、PHY 驱动和 OTG role switch 联动代码三块都齐了板子才能正常枚举。2.2 联发科平台的差异点时钟、复位和 PHY 联动很多工程师第一次拿到 vendor HCD 会问同一个问题IP 不是 DWC 的么直接把dwc2或者dwc3使能不就行了对DWC 是通用 IP但 SoC 集成时真正干的活是时序配置这才是平台差异的根源。联发科平台 USB host 跑起来至少依赖三样私有逻辑。第一是参考时钟USB 2.0 的 48MHz 或 USB 3.0 需要的 SerDes 参考时钟未必是常开的通常从 SoC 的 PLL 分出来由时钟框架单独管理。第二是 PHY 初始化顺序usb_phy_power_on()之前必须完成 pinctrl 把 DP/DM 引脚切到 USB 模式否则 PHY 的差分信号根本出不来。第三是复位释放时机控制器 IP 的 reset 信号往往与 PMIC 时序绑定too early 或 too late 都会造成寄存器读回异常。主线dwc2/dwc3驱动只负责 IP 内部数据通路SoC 私有时钟、复位、PHY 上电由驱动自己拼凑经常拼不齐。常见翻车现场是设备树里开了一个 USB 控制器dmesg里也能看到usb 1-1: new high-speed USB device number 2但设备永远枚举不出来因为 PHY 根本没被正确初始化。vendor HCD 的价值就是把这三段私有逻辑固定在 probe 流程里。2.3 vendor 代码和主线内核的分工我一般会把联发科 HCD 源码当参考实现而不是替代品。正确策略是先确认目标内核版本再看 vendor 包是基于哪个内核 tag 剪出来的。如果目标 SoC 有自己的主线支持优先走主线驱动路线vendor 代码用来对照参数如果主线只支持到通用 DWC 层次那就老老实实把 vendor HCD 作为主方案。vendor HCD 包相比主线驱动多了不少「惯例性代码」例如suspend/resume时对 PHY 的断电时序、睡眠唤醒时 USB 信号恢复、公有 pinctrl 复用等。这些代码在主线仓库里通常没有对应实现直接删掉容易踩 PM 的坑。保留 vendor 代码的代价是维护压力每次内核升级都要跟 HCD API 的变动做适配特别是usb_phy相关的接口内核版本一跳就经常编译不过。3. 把 usb_hcd 编译进内核从 rar 到可加载模块3.1 先解包确认这份源码对应的内核版本拿到仓库代码第一件事不是改代码而是先确认它面向哪个内核版本。解压后看drivers/usb/host/下面是否有对应的 vendor 目录比如mtk_hcd/或mtxxxx_hcd/。这个目录里的Kconfig和Makefile会说明它挂在哪一层MODULE_LICENSE和文件顶部的 Copyright 时间也能判断代码是否太老。# 解压 rarrar 在 Linux 下通常用 unrar 或 unar unrar x usb_driver.rar_usb_hcd_联发科.rar # 解开后确认内核源码树的根位置 cd usb_driver/linux/kernel/drivers/usb/host/ ls -l mtk_hcd/ # 看 Kconfig 和 Makefile 的配置名 grep -r USB_MTK_HCD mtk_hcd/Kconfig mtk_hcd/Makefile注意这个包名里带「联发科」不一定代表代码只适配联发科芯片很多第三方方案商也把公版代码标注成平台名。你需要比对compatible mediatek,...和设备树里的实际 SoC 型号确认匹配再继续。如果grep出来的配置项和设备树节点都对得上说明源码包完整可编。3.2 Kconfig 与 Makefile 匹配以 CONFIG_USB_MTK_HCD 为例vendor HCD 源码放进去之后必须让内核构建系统认识它。一套标准做法是在drivers/usb/host/Kconfig末尾追加一个tristate配置项tristate表示既可以编进内核也可以编成模块。驱动开发阶段用m更灵活不用每次改代码都重烧 kernel。config USB_MTK_HCD tristate MediaTek USB Host Controller Driver depends on USB ARCH_MTK default n help MediaTek SoC USB host controller driver. Say Y here to support USB host functionality on MediaTek platforms.ARCH_MTK这个依赖是关键。如果你在通用 x86 开发机上编这个驱动ARCH_MTK不成立配置项会被自动隐藏make menuconfig里根本找不到。这时候可以在主 Makefile 或者板级 defconfig 中强制打开也可以把depends on放宽成depends on USB临时验证编译。正式产品里我建议保留ARCH_MTK依赖避免模块被错误装载到无关平台。对应的 Makefile 片段一般放在drivers/usb/host/Makefile末尾obj-$(CONFIG_USB_MTK_HCD) mtk_hcd.o mtk_hcd-objs : mtk_hcd_core.o mtk_hcd_phy.o mtk_hcd_otg.omtk_hcd-objs里写的是组成最终模块的所有.o文件顺序不用管。只要 Kconfig 和 Makefile 里配置名一致内核构建系统会维护依赖关系。改完这两文件先执行make menuconfig在 Device Drivers → USB support → Host Controller Drivers 下找到MediaTek USB Host Controller Driver按 M 编成模块。3.3 给 hcd 配设备树节点dr_mode 与 phys 是重点设备树描述的是硬件连接关系HCD 驱动通过platform_get_resource()拿地址和中断通过devm_of_phy_get()拿 PHY 句柄。联发科 USB host 节点常见结构包含compatible、reg、interrupts、clocks、phys与dr_mode几个关键字段。usb_host: usb11280000 { compatible mediatek,mtk-hcd; reg 0x0 0x11280000 0x0 0x1000; interrupts GIC_SPI 65 IRQ_TYPE_LEVEL_LOW; clocks topckgen CLK_TOP_USB0, apmixedsys CLK_APMIXED_USBPLL; clock-names sys_ck, ref_ck; phys u2phy0; phy-names usb2-phy; dr_mode host; status okay; };dr_mode决定控制器工作在 host、peripheral 还是 otg。做 U 盘、4G 模块这类固定主机场景直接写host最省心做 Type-C 双角色设备就得写otg同时要求驱动实现 role switch 回调。clocks一般要配两个一个是控制器自身时钟一个是 PHY 的参考时钟漏掉参考时钟会导致枚举随机失败。phys引用 PHY 节点phy-names与驱动里的phy_get调用对应。如果驱动代码里调用的是devm_of_phy_get_by_index()则不需要phy-names。建议打开时看一遍驱动 probe 函数里具体拿 PHY 的方式实测经常出现名字不匹配导致EPROBE_DEFER。3.4 编译与开机验证从 dmesg 到 /sys/bus/usb编译这一步要注意构建环境的内核版本必须与目标板 kernel source 对应。单独编一个 HCD 模块可以用# 假设内核源码目录是 ~/src/kernel export ARCHarm64 export CROSS_COMPILEaarch64-linux-gnu- make O~/build/kernel mtk_hcd.ko # 拷贝模块到目标板 scp ~/build/kernel/drivers/usb/host/mtk_hcd.ko rootboard:/lib/modules/5.4/模块编好后进入目标板先insmod mtk_hcd.ko再看dmesg尾部输出。正常注册的 HCD 会打印mtk-hcd mtk-hcd.0: new USB bus registered与总线编号usb1。如果没出现这行大概率是 probe 没执行或者设备树节点没匹配。验证链路再走一步插上 U 盘看/sys/bus/usb/devices/下是否出现1-1这样的目录项。有目录但枚举失败问题多半在物理层目录都没有说明 HCD 的根 hub 没起来回头查中断和时钟。这一步能快速把问题归类到驱动还是硬件。4. 联发科 usb_hcd 落地的典型坑与排查方法4.1 现象插上 U 盘 dmesg 完全没有任何反应现象dmesg尾部只有mtk-hcd mtk-hcd.0: new USB bus registered插入 U 盘后连new full-speed USB device都不打印。lsusb也看不到任何设备。原因root hub 虽然注册成功但 HCD 的hub_status_data没有检测到端口连接事件。通常是三个位置出问题PHY 没有真正上电、控制器中断没有触发、或者 GPIO 上拉没有生效。联发科平台最常见的坑是 PHY 的power_on依赖clocks里的 ref_ck设备树漏写ref_ck后 PHY 内部 PLL 停振DP/DM 上无法形成正确的线路状态。解决插上设备后立刻查/proc/interrupts看 USB 控制器的中断计数是不是真的在涨。如果不涨查/sys/kernel/debug/clk/clk_summary里usb_ref相关时钟是否enabled。我一般会直接在驱动hub_status_data入口加一条dev_info打印排掉中断和 PHY 哪个先断掉。确认是 ref_ck 问题在设备树里把CLK_APMIXED_USBPLL常开或者改驱动在 probe 时显式clk_prepare_enable()。这个坑的血泪教训是vendor 包解出来之后不要着急编先对照设备树把所有 clock 节点列出来一次补全。断断续续补容易陷入「改了 dts 重烧结果另一个时钟又没开」的循环。4.2 现象枚举到一半刷屏 device descriptor read error -71现象插上 U 盘后日志反复出现usb 1-1: new high-speed USB device number 4紧接着usb 1-1: device descriptor read/8, error -71如此往复循环最后报告unable to enumerate USB device。原因-71 是EPROTO表示协议层错误。host 发了 8 字节的 GET_DESCRIPTOR 请求但 device 没有正确应答。问题可能在 host PHY 的发送信号质量也可能在 device 端的应答时序。常见板级原因包括 USB 差分线走得太长、串了磁珠、PHY 驱动强度偏弱、供电瞬间跌落等。驱动层面还可能是maximum-speed配错把高速设备当成全速设备来握手。解决先抓包确认错误发生在哪个 stage。usbmon能看到 URB 完成状态如果是-71出现在ctrl-in端点基本锁定是 device 没回 ACK重点查 PHY。联发科 PHY 驱动里通常有phy_tune相关接口调整输出信号幅值或者 pre-emphasis多试几组参数。另外检查 VBUS 供电是否稳定插上设备瞬间电压跌落超过 5%枚举失败概率大增。这类问题比较玄学软件能做的就是先把 PHY 参数调成官方推荐的数值然后把 dts 里maximum-speed high-speed改成full-speed做对照实验看是否能枚举出全速设备。能枚举出全速说明物理层还有救调 PHY 参数解决全速也不行基本可以判断是走线或器件问题需要硬件介入。4.3 现象OTG 口插 Type-C 设备 host 角色起不来现象Type-C 口接入 U 盘后系统没有切换为 hostdmesg完全没有 USB 相关打印/sys/class/usb_role/下的角色还是device。原因联发科的 OTG 实现一般分三层Type-C/PD 协议检测、extcon/typec 通知、role switch 驱动。任何一层断链HCD 都不会进入 host 模式。常见问题是设备树里只开了dr_mode otg但没配置usb-role-switch属性或者 role switch 节点里没有usb_con连接上挂的端到端关系。解决先手动强制切换角色把协议栈的问题排除掉。查/sys/class/usb_role/下当前端口名然后直接写入ls /sys/class/usb_role/ # 假设端口名为 usb0-role-switch echo host /sys/class/usb_role/usb0-role-switch/role手动切到 host 后如果 U 盘能识别说明 HCD 本身没问题问题在 Type-C 驱动没有把插拔事件传给 role switch。顺着typec到usb_role_switch的连接关系查一遍设备树里endpoint是否连接完整。手动切换也失败就得回头查 PHY 的状态切换函数看usb_phy_set_mode是否在 host 模式时正确把 DP/DM 置于设备直通状态。4.4 现象把别家驱动代码复制进来编译过不了现象从 vendor 包里抽出的mtk_hcd_core.c在目标内核上编译报出一堆类似struct usb_hcdhas no member named...的编译错误。包里的代码看起来功能完整但就是编不过。原因HCD 接口在内核不同版本之间变动频繁。比如老的usb_hcd结构成员、usb_phy的调用方式、usb_add_hcd的参数几乎每个大版本都有调整。vendor 包通常基于某个老内核裁剪直接搬到新内核必然翻车。解决先确认源码包注释里的内核版本再对比目标内核的include/linux/usb.h。我的做法是保留 vendor HCD 作为参考实现按目标内核 API 重写hc_driver的回调注册部分私有 PHY 和时钟管理代码原样保留。别硬改老代码去适配新内核结构体成员变动牵一发动全身。重写过程中反复用git diff维护一个补丁文件后续内核再升级时复用这套迁改逻辑。5. 用 usbmon 验证 HCD 行为把问题钉死在 host 还是 deviceHCD 出问题时最容易陷入「改一行参数、重编、烧录、插拔一次」的死循环。其实在动手调 PHY 之前我应该先回答一个问题失败到底是 host 侧没把 URB 送出去还是 device 侧收到了但没应答。usbmon 是解开这个问题最快的手段。# 加载 usbmon modprobe usbmon # 查看当前有哪些总线 cat /sys/kernel/debug/usb/devices | grep -E ^T:|^B: # 对总线 1 进行抓包输出到文件 tcpdump -i usbmon1 -w /tmp/usb_wait.pcap抓包结束后用tcpdump -r或者 Wireshark 打开。枚举阶段能看到GET_DESCRIPTOR包和对应的应答如果 URB 显示-EPROTO而 device 端并没有任何响应说明 PHY 物理信号异常如果 device 端 ACK 正常但后续的 SET_CONFIGURATION 失败问题就在驱动的端点配置逻辑上。usbmon 拿到的信息已经能覆盖大多数分支判断。新手最容易忽略的一点是 usbmon 属于 USB core 层如果 HCD 自己就没把 URB 送进硬件usbmon 是看不到对应包的这时候-71刷屏就不存在反而是中断没触发的问题。所以先用 usbmon 排除硬件链路再用perf或者 trace 看 HCD 的urb_enqueue是否被调用两条路走完就能把问题圈定在驱动逻辑还是 PHY 参数。处理类似这份 usb_driver 包时我的习惯是先把 usbmon 的抓包能力验证好再去做 PHY 参数调整。每一条-71背后可能有十种以上成因抓包能先排掉一半剩下的一半交给示波器量 DP/DM。希望帮到你。本文还有配套的精品资源点击获取