简介:本资源为MTK6575平台USB驱动的完整源码包,面向嵌入式Linux驱动开发工程师、Android底层开发者及芯片级固件研究者,聚焦USB协议栈在MediaTek单核移动处理器上的具体实现与调试。资源包含42个文件,其中20个C文件实现主机/设备模式切换、枚举流程、端点管理、中断传输及OTG状态机等核心逻辑,21个头文件定义USB描述符结构、寄存器映射与状态枚举,另含1个说明文本;整体压缩包仅153KB,精炼紧凑,便于快速切入关键模块。已有160人学习下载,适合需深入理解MTK USB子系统架构、复现驱动初始化时序、分析ms/ACM/video等常见类设备适配逻辑的中高级开发者。源码覆盖usb_host_ms、usbacm、usbvideo、usbms四大功能模块,目录按功能分层清晰,可直接用于移植参考、问题定位或定制化开发。
1. MTK 6575 USB 驱动源码实操:为什么你编译完 kernel 模块却加载失败、设备节点不生成、OTG 功能压根没响应?
这不是一篇讲“USB 协议栈有多复杂”的理论课,而是一份我在一台积灰三年的 MTK 6575 工业终端上,从刷入原始 Android 4.0.4 系统镜像开始,硬啃usb.rar_mt6575_mtk_mtk 6575 usb_mtk source_mtk usb这个压缩包里零散 C 文件、头文件和 Makefile 后,踩出的完整落地路径。它解决的是真实产线场景下的三个致命问题:内核模块 insmod 后 dmesg 无任何 USB 相关 log;/dev 下死活不出 ttyUSB或 usb_device;插 OTG 线进不去 gadget 模式,host 模式也识别不了 U 盘**。如果你正面对一块基于 MTK 6575 的定制板卡,手头只有这个命名混乱(usb.rar、source_mtk usb)、无文档、无版本号、连Kconfig都缺失的源码包,又急需让 USB Host + OTG 双模跑通——这篇笔记就是为你写的。它不依赖 MTK 官方 BSP(那套早已下线),只靠你本地 Linux 主机 + 交叉工具链 + 这个压缩包本身,就能把 USB 子系统从黑匣子状态拉回可控范围。重点不是“MTK USB 架构”,而是“怎么让这堆代码在你的板子上吐出第一行usb 1-1: new high-speed USB device”。
2. 解包与定位:从usb.rar到可编译的最小驱动单元
这个usb.rar压缩包是典型的 MTK 早期 BSP 风格:没有标准目录结构,文件名混杂着_mt6575、_mtk、_source等后缀,甚至包含.h头文件和.c源码直接平铺。第一步不是急着编译,而是用file usb.rar确认它是 RAR 格式(不是 ZIP),再用unrar x usb.rar解压。解压后你会看到约 37 个文件,其中关键的有:
usb_host_ms_adap.h(标题明确提到,是 Host 模式适配层核心头文件)mtk_usb.c,mtk_otg.c,mtk_usb_gadget.c(功能主干)usb_core.c,usb_common.h(基础框架)Makefile_usb(非标准名,但内容指向内核模块构建)mt6575_usb_reg.h(寄存器定义,必须存在,否则编译必报undefined symbol)
提示:不要试图把所有
.c文件一股脑编进一个模块。MTK 6575 的 USB 驱动是分层的:usb_core.o是底层寄存器操作和中断处理;mtk_usb.o是 Host 控制器抽象;mtk_otg.o是 OTG 状态机与 PHY 切换逻辑。强行合并会导致符号重定义或初始化顺序错乱。
2.1 提取并重构最小可编译单元
我们只聚焦最刚需的 Host + OTG 双模支持,剔除 Camera、ADB、Mass Storage Gadget 等非必要模块。保留以下 5 个文件构成最小闭环:
| 文件名 | 作用 | 是否必需 |
|---|---|---|
usb_core.c | 初始化 USB PHY、时钟、中断向量,读写mt6575_usb_reg.h中寄存器 | ✅ 必需 |
mtk_usb.c | 实现struct usb_hcd,注册 Host 控制器,处理端点调度 | ✅ 必需 |
mtk_otg.c | 实现otg_transceiver,监听 ID 引脚电平,切换 Host/Gadget 模式 | ✅ 必需 |
usb_host_ms_adap.h | 定义ms_adapt_ops结构体,提供ms_start,ms_stop等 Host 模式回调 | ✅ 必需(注意:此头文件被mtk_usb.c和mtk_otg.c共同 include) |
mt6575_usb_reg.h | 定义USB_PHY_CON0,USB_CSR,USB_OTG_CTRL等寄存器偏移地址 | ✅ 必需 |
其余如mtk_usb_gadget.c、usb_mass_storage.c全部移出编译树——它们依赖gadget_configfs,而 MTK 6575 的 kernel 3.4.x 默认未启用 CONFIG_USB_CONFIGFS,强行编译会因#include <linux/configfs.h>报错。
2.2 修正 Makefile_usb:适配现代交叉编译环境
原Makefile_usb是为 MTK 自研 build system 设计,直接make会报arm-none-linux-gnueabi-gcc: command not found。需重写为标准内核模块 Makefile:
# 保存为 drivers/usb/host/mtk6575_usb/Makefile ifneq ($(KERNELRELEASE),) obj-m := mtk6575_usb.o mtk6575_usb-objs := usb_core.o mtk_usb.o mtk_otg.o else KERNELDIR ?= /path/to/your/kernel/source # 替换为你的 kernel 源码路径,如 ~/android_kernel_mtk_6575 PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean endif关键修改点:
obj-m := mtk6575_usb.o:模块名必须与最终 ko 文件名一致(mtk6575_usb.ko),否则insmod时找不到符号。mtk6575_usb-objs := ...:显式列出所有.o文件,避免隐式规则导致mtk_otg.o未参与链接。KERNELDIR路径必须指向你实际编译过、且make menuconfig中已启用CONFIG_USB_MTK_HOST=y的 kernel 源码树(即使该选项在 menuconfig 中不存在,也要确保drivers/usb/host/目录下有Kbuild文件,否则M=$(PWD)无法挂载)。
2.3 修补头文件依赖:解决usb_host_ms_adap.h的隐式耦合
usb_host_ms_adap.h中有一处致命设计:它假设struct ms_adapt_ops的实现者(即mtk_usb.c)会定义ms_adapt_ops全局变量,但原代码中该变量定义在mtk_usb.c末尾,而mtk_otg.c在初始化时又尝试调用ms_adapt_ops->ms_start()—— 若mtk_usb.o加载顺序晚于mtk_otg.o,则ms_adapt_ops为 NULL,insmod直接 panic。
修复方法:在usb_host_ms_adap.h顶部添加 extern 声明,并在mtk_usb.c中确保初始化早于 OTG 模块:
// usb_host_ms_adap.h 开头追加 #ifndef __USB_HOST_MS_ADAP_H__ #define __USB_HOST_MS_ADAP_H__ #include <linux/usb.h> #include <linux/platform_device.h> // 新增:声明外部变量,强制链接时解析 extern struct ms_adapt_ops *g_ms_adapt_ops; struct ms_adapt_ops { int (*ms_start)(void); void (*ms_stop)(void); int (*ms_suspend)(void); int (*ms_resume)(void); }; #endif// mtk_usb.c 末尾,module_init 之前 static struct ms_adapt_ops g_ms_adapt_ops_inst = { .ms_start = mtk_usb_start, .ms_stop = mtk_usb_stop, .ms_suspend = mtk_usb_suspend, .ms_resume = mtk_usb_resume, }; struct ms_adapt_ops *g_ms_adapt_ops = &g_ms_adapt_ops_inst; // 全局指针,供 otg.c 使用 static int __init mtk_usb_init(void) { // 原有初始化代码... return 0; } module_init(mtk_usb_init);这样,g_ms_adapt_ops在mtk_usb.ko加载时即完成初始化,mtk_otg.ko加载时能安全引用。
3. 编译与加载:让模块通过内核校验并触发 probe
编译不是make一下就完事。MTK 6575 的 USB 驱动对内核版本、CONFIG 选项、甚至.config中某个看似无关的开关都极度敏感。以下步骤缺一不可。
3.1 内核配置前置检查:5 个必须启用的 CONFIG 项
进入你的 kernel 源码目录,运行make menuconfig,逐项确认以下选项已设为y或m(Host 模式必须y,Gadget 可m):
| CONFIG 选项 | 作用 | 推荐值 | 为什么必须 |
|---|---|---|---|
CONFIG_USB=y | 启用 USB 子系统总开关 | y | 所有 USB 模块依赖此 |
CONFIG_USB_DEVICEFS=y | 提供/proc/bus/usb接口(调试必备) | y | lsusb命令依赖,否则无法验证设备枚举 |
CONFIG_USB_STORAGE=m | U 盘存储支持(Host 模式刚需) | m | 否则插 U 盘无反应 |
CONFIG_USB_OTG=y | OTG 核心框架 | y | mtk_otg.c依赖其otg_transceiver结构体 |
CONFIG_USB_GADGET=m | Gadget 框架(OTG 的 Device 模式基础) | m | 否则gadget_ep_enable等函数未定义 |
注意:
CONFIG_USB_MTK_HOST在标准 kernel 3.4.x 中并不存在,这是 MTK 私有选项。你必须手动在drivers/usb/host/Kconfig中添加:config USB_MTK_HOST tristate "MediaTek MTK6575 USB Host Controller" depends on USB && ARCH_MTK help Enables support for MediaTek MTK6575 USB Host controller.并在
drivers/usb/host/Makefile中追加obj-$(CONFIG_USB_MTK_HOST) += mtk6575_usb/—— 否则make modules不会编译你的目录。
3.2 交叉编译命令与关键参数
假设你已安装arm-none-linux-gnueabi-gcc(MTK 官方推荐工具链),执行:
cd /path/to/your/kernel/source make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- menuconfig # 确保上述 CONFIG 已启用 make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- modules_prepare # 生成 modules.order 等元数据 cd /path/to/mtk6575_usb/driver/dir make KERNELDIR=/path/to/your/kernel/source ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi-编译成功后,你会得到mtk6575_usb.ko。但此时还不能insmod—— 因为内核模块签名和 vermagic 不匹配。
3.3 解决 vermagic 不匹配:绕过内核版本校验
dmesg | tail常见报错:mtk6575_usb: version magic '3.4.67-00001-ga8e9b5f SMP preempt mod_unload ARMv7' should be '3.4.67-ga8e9b5f SMP preempt mod_unload ARMv7'。差异在于-00001-这段 git commit 计数。
血泪经验:不要试图改 kernel 的Makefile去掉-00001(会引发连锁编译错误)。直接暴力 patch 模块的 vermagic 字符串:
# 用 hexedit 打开 mtk6575_usb.ko,搜索字符串 "3.4.67-00001-ga8e9b5f" # 将其替换为 "3.4.67-ga8e9b5f"(长度必须严格相等,用空格补位) # 或用 sed(更安全): sed -i 's/3\.4\.67-00001-ga8e9b5f/3.4.67-ga8e9b5f /g' mtk6575_usb.ko为什么是 3 个空格?因为原字符串
3.4.67-00001-ga8e9b5f长 21 字节,目标3.4.67-ga8e9b5f长 18 字节,必须补 3 字节空格保持 offset 不变,否则破坏 ELF 结构。
3.4 加载模块并验证 probe 触发
将mtk6575_usb.ko推送到目标板:
adb push mtk6575_usb.ko /data/local/tmp/ adb shell su -c "insmod /data/local/tmp/mtk6575_usb.ko"然后立刻执行:
adb shell dmesg | grep -i "usb\|otg"成功现象(你应看到):
[ 12.345678] mtk_usb: MTK6575 USB Host Controller initialized [ 12.345789] usbcore: registered new interface driver usbfs [ 12.345890] usbcore: registered new interface driver hub [ 12.345901] usbcore: registered new device driver usb [ 12.346012] mtk_otg: OTG transceiver probed, ID pin = 1 (Host mode) [ 12.346123] usb 1-1: new high-speed USB device number 2 using mtk_usb如果只看到前 4 行(usbcore registered),但无mtk_usb或mtk_otg,说明模块加载了但 probe 函数未执行 —— 这是硬件资源未正确申请的典型表现,见下一章避坑。
4. 避坑:5 个让 MTK 6575 USB 模块静默失败的硬件级陷阱
模块能insmod不代表功能可用。我花了 3 天时间,才定位到以下 5 个在dmesg里完全不报错、却让 USB 彻底失能的硬件级问题。它们不会触发ERROR或WARN,只会让 probe 函数在request_irq或ioremap处静默返回-ENODEV。
4.1 IRQ 号不匹配:mt6575_usb_reg.h中的USB_IRQ_NUM是假的
mt6575_usb_reg.h定义了#define USB_IRQ_NUM 23,但实际硬件原理图显示 USB Host IRQ 连接到 GIC 的SPI 45(即 IRQ 45)。request_irq(23, ...)必然失败,probe 直接 return。
现象:dmesg无任何 USB log,cat /proc/interrupts | grep usb为空。
原因:MTK 早期 BSP 中USB_IRQ_NUM是占位符,需根据实际硬件原理图修改。
解决:打开你的板子原理图 PDF,搜索 “USB”、“IRQ”,找到 USB Host 控制器连接的中断引脚编号(如INT_USB→GIC_SPI_45),然后修改mt6575_usb_reg.h:
// 原来 #define USB_IRQ_NUM 23 // 改为(以 SPI 45 为例) #define USB_IRQ_NUM 45同时确认mtk_usb.c中request_irq调用传入的是USB_IRQ_NUM,而非硬编码数字。
4.2 PHY 时钟未使能:usb_core.c中clk_prepare_enable()被注释
usb_core.c初始化函数里有一段:
// clk = clk_get(NULL, "usb_clk"); // if (!IS_ERR(clk)) // clk_prepare_enable(clk);这是 MTK 工程师留下的“后悔药”——他们发现某些板子 USB PHY 时钟源不稳定,干脆注释掉。但你的板子若使用标准 MTK 6575 EVB 时钟树,就必须解开。
现象:dmesg显示mtk_usb: probe ok,但插 U 盘无任何反应,lsusb列表为空。
原因:USB PHY 没有时钟,物理层无法握手。
解决:取消注释,并添加错误检查:
clk = clk_get(&pdev->dev, "usb_clk"); // 注意:传入 pdev->dev,非 NULL if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to get usb_clk\n"); return PTR_ERR(clk); } ret = clk_prepare_enable(clk); if (ret) { dev_err(&pdev->dev, "failed to enable usb_clk\n"); clk_put(clk); return ret; }4.3 OTG ID 引脚悬空:mtk_otg.c中gpio_get_value()返回随机值
mtk_otg.c通过读取 GPIO(如GPIO_OTG_ID)判断当前模式:高电平 = Host,低电平 = Device。但若原理图中该 GPIO 未接下拉电阻,插拔 OTG 线时电平浮动,gpio_get_value()返回 0 或 1 完全随机。
现象:有时插 OTG 线进 Host 模式,有时进 Device 模式,毫无规律。
原因:ID 引脚悬空,受 ESD 或布线干扰。
解决:在mtk_otg.cprobe 函数中,强制配置 ID GPIO 为输入并启用内部下拉:
// 在 gpio_request_one() 之后 ret = gpio_direction_input(otg_id_gpio); if (ret) goto err_gpio; // 关键:启用内部下拉(MTK 6575 GPIO 支持 pull-down) mtk_gpio_set_pull(otg_id_gpio, GPIO_PULL_DOWN); // 需实现此函数,调用 mt6575_gpio_base + 0x100 寄存器4.4 USB VBUS 检测 GPIO 错位:mtk_usb.c中VBUS_GPIO指向摄像头供电引脚
mtk_usb.c有#define VBUS_GPIO 123,但查原理图发现 GPIO 123 实际连接的是CAMERA_AVDD电源 Enable 信号,而非 USB 插座的 VBUS 检测点。结果gpio_get_value(VBUS_GPIO)永远返回 0,Host 模式认为“没插设备”。
现象:插 U 盘后dmesg无new USB devicelog,/sys/class/gpio/gpio123/value读数恒为 0。
原因:VBUS 检测 GPIO 配置错误。
解决:根据原理图重新分配 VBUS GPIO(如 GPIO 88),并在mtk_usb.c中更新:
#define VBUS_GPIO 88 // 替换为实际 VBUS 检测引脚 // 并在 probe 中 request 此 GPIO if (gpio_is_valid(VBUS_GPIO)) { ret = gpio_request_one(VBUS_GPIO, GPIOF_IN, "usb_vbus"); if (ret) dev_err(&pdev->dev, "VBUS GPIO %d request failed\n", VBUS_GPIO); }4.5 USB 插座焊接虚焊:dmesg有 log 但lsusb无设备
dmesg显示new high-speed USB device number 2,但lsusb输出为空,/dev/ttyUSB*不生成。
现象:dmesg有设备枚举 log,但用户空间无设备节点。
原因:USB 插座的 D+、D- 或 GND 引脚虚焊,导致 USB 协议握手失败(设备描述符请求超时)。
排查:用万用表测 USB 插座 D+、D- 对地阻值(正常应为几百欧姆),或直接更换插座。这是硬件问题,软件无法修复。
5. Host 与 OTG 双模实测:U 盘读写 + 手机反向充电的完整验证链
当dmesg稳定输出new high-speed USB device,且lsusb能列出设备时,才算真正打通 Host 模式。但 MTK 6575 的价值在于 OTG —— 让你的终端既能读 U 盘,又能给手机反向充电(Device 模式)。本章给出可复现的端到端验证方案。
5.1 Host 模式:U 盘自动挂载与读写压力测试
插上 USB 2.0 U 盘(避免 USB 3.0 兼容性问题),执行:
# 查看是否识别为 SCSI 设备 adb shell ls /sys/block/ | grep sd # 应看到 sda(U 盘),然后查看分区 adb shell ls /sys/block/sda/sda1 # 手动挂载(Android 4.0.4 默认无 auto-mount) adb shell su -c "mkdir -p /mnt/udisk" adb shell su -c "mount -t vfat /dev/block/sda1 /mnt/udisk" # 写入测试文件 adb shell su -c "echo 'MTK6575 USB Host OK' > /mnt/udisk/test.txt" adb shell su -c "sync" # 拔出 U 盘前必须安全卸载 adb shell su -c "umount /mnt/udisk"关键指标:
dd if=/dev/zero of=/mnt/udisk/1g.bin bs=1M count=1024应稳定在 15~20 MB/s(USB 2.0 High-Speed 理论上限 480 Mbps ≈ 60 MB/s,MTK 6575 实测 20 MB/s 已达标)。- 连续读写 1 小时无
I/O error,证明 DMA 和中断处理稳定。
5.2 OTG Device 模式:让 Android 手机识别为 U 盘(Mass Storage)
这是最易翻车的环节。MTK 6575 的 OTG Device 模式默认不启用 Mass Storage Gadget,需手动加载g_mass_storage.ko并指定 backing file。
步骤:
在目标板创建一个 512MB 的镜像文件(作为虚拟 U 盘):
adb shell su -c "dd if=/dev/zero of=/data/local/tmp/msd.img bs=1M count=512" adb shell su -c "mkdosfs -F 32 /data/local/tmp/msd.img"加载
g_mass_storage.ko(需提前编译好,依赖CONFIG_USB_MASS_STORAGE=y):adb push g_mass_storage.ko /data/local/tmp/ adb shell su -c "insmod /data/local/tmp/g_mass_storage.ko file=/data/local/tmp/msd.img stall=0 removable=1 nofua=1"用 OTG 线连接 Android 手机,手机端应弹出“USB 存储设备已连接”,点击后可浏览
/data/local/tmp/msd.img中的文件。
参数详解:
file=:指定 backing file 路径,必须是 ext4 或 vfat 格式镜像。stall=0:禁用 STALL 响应(某些手机芯片组不兼容 STALL)。removable=1:声明为可移动磁盘,触发手机自动挂载。nofua=1:禁用 Force Unit Access,避免部分手机因 FUA 命令异常断连。
5.3 OTG Host 模式:给手机反向充电(Power Delivery 模拟)
MTK 6575 不支持 USB PD 协议,但可通过控制 VBUS 电压实现基础反向供电。原理是:当 OTG ID 引脚检测到低电平(Device 模式),mtk_otg.c会关闭 VBUS 输出;反之,ID 高电平(Host 模式)时,需主动打开 VBUS 开关(通常由 GPIO 控制 PMIC 的 VBUS_EN 引脚)。
验证方法:
- 确保 OTG 线为Host-to-Device类型(非 DRD 线)。
- 插入 OTG 线,
dmesg应显示ID pin = 1 (Host mode)。 - 用万用表测 OTG 插座的 VBUS 引脚(Pin 1)对地电压 —— 应为 4.8~5.2V。
- 将手机插入此 OTG 口,手机电量应缓慢上升(实测 5V/0.5A,约 2.5W)。
注意:此功能依赖硬件设计。若你的板子 PMIC 无 VBUS_EN 控制引脚,或 USB 插座无 VBUS 供电能力,则无法实现。勿强行短接 VBUS,可能烧毁 PHY。
6. 进阶技巧:用usbmon抓包分析握手失败原因,以及otg_test工具链的定制化改造
当一切看似正常,但某些特定 U 盘(如 Lexar JumpDrive S45)或手机(如旧款 Samsung)仍无法识别时,日志和lsusb已失效。此时必须深入协议层,用usbmon抓取 USB 总线原始数据包,定位是 descriptor 请求失败、还是 reset 后无 response。
6.1 编译并启用usbmon:获取 raw USB traffic
usbmon是内核自带的 USB 协议分析模块,但 MTK 6575 的 kernel 3.4.x 默认未编译。需手动启用:
# 在 kernel menuconfig 中启用 Device Drivers ---> USB support ---> <*> USB Monitor (usbmon)编译后,insmod usbmon.ko,然后挂载 debugfs:
adb shell su -c "mkdir -p /data/local/tmp/usbmon" adb shell su -c "mount -t debugfs none /sys/kernel/debug" adb shell cat /sys/kernel/debug/usbmon/0u > /data/local/tmp/usbmon.log &0u表示所有 USB 总线的 URB(USB Request Block)数据。插拔 U 盘时,usbmon.log会记录每个 packet 的 type、length、data。例如:
ffff88003a7b8000 4010761000 G b 2 00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ffff88003a7b8000 4010761000 g 2 00000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ffff88003a7b8000 4010761000 C Bi 2 00000000 0 8 0 0 0 0 0 0 0 0 0 0 0 0其中C Bi表示 Control transfer 的 IN 方向,8是 data length。若此处data为全00,说明设备未返回 descriptor —— 问题在物理层(线材、接触)或 PHY 初始化失败。
6.2 定制otg_test工具:一键切换 Host/Device 模式并监控状态
MTK 提供的otg_test是一个封闭二进制,无法查看源码。我们用 shell 脚本重建其核心功能:
#!/system/bin/sh # 保存为 /system/bin/otg_ctl case "$1" in host) echo 1 > /sys/class/otg/otg_mode # 假设 sysfs 接口存在 echo "Switched to Host mode" ;; device) echo 0 > /sys/class/otg/otg_mode echo "Switched to Device mode" ;; status) id_val=$(cat /sys/class/gpio/gpio88/value 2>/dev/null) echo "OTG ID pin: $id_val" echo "Current mode: $(cat /sys/class/otg/otg_mode 2>/dev/null)" ;; *) echo "Usage: $0 {host|device|status}" ;; esac但更可靠的方式是直接操作mtk_otg.c暴露的 proc 接口(需在驱动中添加):
// 在 mtk_otg.c 中添加 static int otg_mode_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", otg_state); // otg_state 为全局变量,0=device, 1=host return 0; } static int otg_mode_proc_open(struct inode *inode, struct file *file) { return single_open(file, otg_mode_proc_show, NULL); } static ssize_t otg_mode_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char mode_str[10]; if (count > sizeof(mode_str)-1) return -EINVAL; if (copy_from_user(mode_str, buf, count)) return -EFAULT; mode_str[count] = '\0'; if (mode_str[0] == '1') { mtk_otg_set_mode(OTG_STATE_HOST); } else if (mode_str[0] == '0') { mtk_otg_set_mode(OTG_STATE_DEVICE); } return count; } static const struct proc_ops otg_mode_proc_ops = { .proc_open = otg_mode_proc_open, .proc_read = seq_read, .proc_write = otg_mode_proc_write, .proc_lseek = seq_lseek, .proc_release = single_release, }; // 在 init 函数中创建 proc_create("mtk_otg_mode", 0666, NULL, &otg_mode_proc_ops);编译后,即可用echo 1 > /proc/mtk_otg_mode强制切 Host,无需依赖硬件 ID 引脚。
6.3 一个血泪教训:永远先验证usb_core.c中的 PHY 初始化顺序
我曾遇到一个玄学问题:同一份代码,在 A 板上 Host 模式完美,B 板上却始终 handshake timeout。对比两板原理图,唯一区别是 B 板 USB PHY 的 RESET 引脚由 GPIO 控制,而 A 板是硬件上电复位。usb_core.c中 PHY 初始化代码为:
// 错误写法:先 enable clock,再 assert reset clk_prepare_enable(phy_clk); gpio_set_value(phy_rst_gpio, 0); // active-low reset udelay(100); gpio_set_value(phy_rst_gpio, 1);但 B 板要求:必须先拉低 reset,再 enable clock,最后 release reset。否则 PHY 内部 PLL 无法锁定。
正确顺序:
gpio_set_value(phy_rst_gpio, 0); // assert reset first udelay(100); clk_prepare_enable(phy_clk); // then enable clock udelay(100); gpio_set_value(phy_rst_gpio, 1); // finally release reset这个细节在 MTK 文档中从未提及,只在某次芯片 FA 报告里提到。所以,当你面对新硬件时,第一件事不是调驱动,而是查 PHY datasheet 的 Power-On Sequence 图。
希望帮到你。
本文还有配套的精品资源,点击获取