DJOneHub 通话接通无声音与 ADB 接口修复
- 处理日期:2026-08-17
- 项目:https://github.com/rogerbush007-a11y/DJOneHub-mac-enhanced
- 模块:BAIWANG EG25G-QDC507
- 模块 USB 身份:
2C7C:0125 - 操作系统:macOS
1. 原始问题和现象
使用 DJOneHub 拨打或接听电话时:
- 电话可以正常拨出、振铃和接通;
- AT 通话控制、短信和网络功能基本正常;
- 接通后双方都听不到声音;
- DJOneHub 界面显示:
1
2
MaVo 音频准备失败:模块语音路由准备失败:
USB ADB interface (6/adb) not found on 2c7c:0125
日志中的关键记录为:
1
2
3
call state "dialing" -> "active"
module voice: starting route
module voice: start failed: USB ADB interface (6/adb) not found on 2c7c:0125
这说明电话控制链路已经成功,失败发生在电话接通后启动模块语音媒体路由的阶段。
2. 问题的本质
本次问题由两个连续的原因组成。
2.1 模块 ADB 尚未解锁
DJOneHub 的电话控制和音频使用不同链路:
- 拨号、接听、挂断通过模块 AT 接口完成;
- 双向声音需要 DJOneHub 通过 USB ADB,把模块侧语音运行时临时部署到模块,并启动 D4/UAC 路由;
- 因此即使电话已经接通,只要 ADB 接口不存在,仍然不会有声音。
故障时模块的 USB 配置为:
1
+QCFG: "usbcfg",0x2C7C,0x0125,1,1,1,1,1,0,1
倒数第二位为 0,表示 ADB USB 功能未启用;最后一位为 1,表示 USB Audio 已启用。
macOS 当时实际看到的接口布局为:
- interface 0~3:模块厂商接口;
- interface 4~5:CDC-ECM 网络接口;
- interface 6~8:USB Audio;
- 没有
bInterfaceSubClass = 66的 ADB 接口。
进一步执行只读查询:
1
AT+QADBKEY?
模块返回:
1
2
+QADBKEY: 30115171
OK
这表明当前固件启用了 Quectel ADB 挑战码保护。没有先提交正确的 QADBKEY,直接写入 ADB 功能位可能暂时返回 OK,但重启后不会得到真正可用的 ADB 接口。
2.2 系统 adb 后台进程抢占了接口
完成模块解锁并重新枚举后,interface 6 已变成正确的 ADB 接口,但 DJOneHub 第一次启动语音路由又返回:
1
claim USB ADB interface 6: LIBUSB_ERROR_ACCESS
检查 macOS USB 注册表后发现:
1
2
ADB Interface@6
UsbExclusiveOwner = "pid 89856, adb"
本机已有一个 Android Platform Tools 的 adb server 长期运行并独占 interface 6。DJOneHub 也需要直接通过 libusb 独占该接口,因此两者不能同时使用。
停止 Android adb server 后,DJOneHub 才能成功打开接口并启动语音路由。
2.3 项目当前逻辑存在的缺口
项目源码中,模块语音路由明确依赖 USB ADB:
1
2
3
4
adb, err := openDJIUSBADB()
if err != nil {
return err
}
ADB 探测要求找到 interface 6 或 ADB subclass 66,并且必须有一对 Bulk IN/OUT 端点:
1
2
3
if byte(alt.bInterfaceNumber) != 6 && byte(alt.bInterfaceSubClass) != 66 {
continue
}
但 v1.2.9 又把旧配置 …,1,1,1,1,1,0,1 判断成“通话音频已就绪”。该配置虽然能枚举 USB Audio,却不保证当前固件已解锁并枚举 ADB。因此 UI 可能显示准备完成,真正接通电话后才暴露 ADB 不存在的问题。
3. 总体处理思路和方案
处理过程遵循以下原则:
- 先做只读检查,避免盲目修改模块配置;
- 确认当前没有语音通话,避免在通话中切换 USB 组合;
- 查询当前
USBCFG和QADBKEY挑战码; - 计算并提交本模块对应的 ADB 解锁密码;
- 只修改
USBCFG的 ADB 功能位,保留 VID、PID 和其他功能位; - 写入后立即精确回读,只有完全匹配才允许重启;
- 重启后从 macOS USB 描述符验证真实接口,而不是只相信 AT 配置字符串;
- 若接口存在但无法打开,检查并释放其他 ADB 客户端的独占占用;
- 实际启动并停止一次模块语音路由,验证运行时部署、驱动加载和 D4/UAC 路由完整可用。
4. 完整处理过程
4.1 检查通话状态
先检查 DJOneHub 状态及模块原始 CLCC:
1
2
3
4
5
curl -fsS 'http://127.0.0.1:7575/api/calls/status'
curl -fsS -X POST 'http://127.0.0.1:7575/api/at' \
-H 'Content-Type: application/json' \
--data-binary '{"command":"AT+CLCC"}'
模块返回了两条 mode=1 的记录:
1
2
3
+CLCC: 2,1,0,1,0,"",128
+CLCC: 1,1,0,1,0,"",128
OK
3GPP/项目解析规则中 mode=0 才是语音,mode=1 是数据会话。因此当时没有活动语音通话,可以安全修改 USB 配置。
4.2 查询原始 USB 配置
1
2
3
curl -fsS -X POST 'http://127.0.0.1:7575/api/at' \
-H 'Content-Type: application/json' \
--data-binary '{"command":"AT+QCFG=\"USBCFG\""}'
结果:
1
2
+QCFG: "usbcfg",0x2C7C,0x125,1,1,1,1,1,0,1
OK
项目此前已经保存过原始出厂配置备份:
1
~/Library/Application Support/DJOneHub/module-backups/
原始配置为:
1
2CA3:4006,1,1,1,1,1,0,0
4.3 查询 ADB 挑战码并计算解锁密码
查询命令:
1
AT+QADBKEY?
返回挑战码:
1
30115171
Quectel 常见 QADBKEY 算法使用 MD5-crypt:
1
openssl passwd -1 -salt '30115171' 'SH_adb_quectel'
输出:
1
$1$30115171$zqsKlu75gP1ZamfXKVUmG0
取哈希主体的前 15 个字符,得到本模块当次使用的 ADB 解锁密码:
1
zqsKlu75gP1Zamf
先用公开示例挑战码进行过交叉校验,生成结果与 Quectel 官方社区公开密码一致,再用于当前模块。
4.4 提交 ADB 解锁密码
实际发送:
1
AT+QADBKEY="zqsKlu75gP1Zamf"
模块返回:
1
OK
4.5 启用 ADB 与 USB Audio 完整组合
保持现有 2C7C:0125 身份及其他功能位不变,只把倒数第二位 ADB 从 0 改为 1:
1
AT+QCFG="USBCFG",0x2C7C,0x0125,1,1,1,1,1,1,1
写入返回:
1
OK
随后立即回读:
1
AT+QCFG="USBCFG"
精确得到:
1
2
+QCFG: "usbcfg",0x2C7C,0x125,1,1,1,1,1,1,1
OK
只有在回读完全一致后才继续重启。
4.6 重启模块
1
AT+CFUN=1,1
重启期间 4G、短信、USB AT 和音频设备会短暂断开并重新枚举。
4.7 验证真实 USB 接口
重启完成后,DJOneHub 健康接口报告如下 USB 布局:
1
2
3
interface 6: class=255, subclass=66, protocol=1, endpoints=2
interface 7: USB Audio Control
interface 8~9: USB Audio Streaming
macOS 注册表最终确认:
1
2
3
4
ADB Interface@6
"bInterfaceClass" = 255
"bInterfaceSubClass" = 66
"bNumEndpoints" = 2
这证明 ADB 与 UAC 已同时真实枚举,不再只是配置字符串显示成功。
4.8 处理 ADB 接口占用
首次测试语音路由时返回:
1
claim USB ADB interface 6: LIBUSB_ERROR_ACCESS
查到独占进程:
1
UsbExclusiveOwner = "pid 89856, adb"
对应程序:
1
~/.local/android/platform-tools/adb
停止 adb server:
1
adb kill-server
确认进程停止且 interface 6 不再有独占所有者后,继续测试。
4.9 启动实际语音路由验证
调用 DJOneHub 本地接口:
1
2
curl --max-time 70 -X POST \
'http://127.0.0.1:7575/api/voice/start'
日志确认完成了全部关键步骤:
1
2
3
4
5
6
7
module voice: pushed qdc507_aprv3.ko
module voice: pushed qdc507_voice.ko
module voice: pushed mavo-pcm-bridge.armv7
module voice: insmod qdc507_aprv3.ko
module voice: insmod qdc507_voice.ko
module voice: runtime prepared (qdc507-3.18.44-voice-20260712.5)
module voice: route is ready (UAC voice enabled)
API 返回:
1
2
3
4
5
6
7
8
{
"started": true,
"status": {
"ready": true,
"last_error": "",
"runtime_installed": true
}
}
由于当时没有真实电话,验证成功后按正常生命周期停止测试路由:
1
2
curl --max-time 40 -X POST \
'http://127.0.0.1:7575/api/voice/stop'
日志确认:
1
module voice: route stopped
下一次电话进入 active 状态时,DJOneHub 会自动重新启动该路由。
5. 最终结果
最终状态如下:
- ADB 解锁成功;
USBCFG持久化为2C7C:0125,1,1,1,1,1,1,1;- interface 6 正确枚举成 ADB;
- interface 7~9 正确枚举成 USB Audio;
- 模块语音运行时推送成功;
- 两个语音内核模块加载成功;
- D4/UAC 语音路由成功进入 ready;
- 测试结束后路由已正常停止;
- Android adb server 已停止,不再抢占模块接口。
6. 换电脑后的处理
QADBKEY 解锁和 USBCFG 保存在模块侧,因此正常换到另一台 Mac 后,不需要再次解锁模块。
新电脑仍需完成:
- 安装 DJOneHub;
- 在“设置 → 语音运行时”重新下载并校验语音运行时,因为该缓存保存在电脑本地;
- 授予 DJOneHub 麦克风权限;
- 等待 macOS 完成 USB Audio 和 CDC-ECM 网络接口识别;
- 确保没有 Android
adbserver、Android File Transfer、OpenMTP 等程序独占模块 ADB 接口。
若新电脑出现:
1
LIBUSB_ERROR_ACCESS
优先执行:
1
adb kill-server
只有以下情况才可能需要重新解锁或重新配置模块:
- 模块恢复出厂配置;
- 刷写或升级固件后安全状态被重置;
- 其他工具改写了
USBCFG; - 回读发现倒数第二位重新变成
0; - interface 6 不再是 subclass 66 的 ADB 接口。
7. 总结与反思
7.1 后续需要加强的知识点
-
AT 控制面与媒体面的区别
电话能拨通只证明 AT/IMS/VoLTE 控制链路有效,不代表 PCM、UAC 和主机音频链路有效。 -
USB 组合配置与真实描述符验证
AT+QCFG="USBCFG"的回读只能表示配置值,最终仍应使用 IOKit、libusb 或系统 USB 注册表验证接口 class、subclass、端点和实际占用者。 -
Quectel ADB 安全机制
某些固件要求先执行QADBKEY挑战应答;没有解锁时,直接打开 ADB 功能位可能无法在重启后真正生效。 -
macOS USB 接口独占
Android adb server 与 DJOneHub 的直连 ADB 实现都会独占同一个 USB interface。检测到LIBUSB_ERROR_ACCESS时,应先检查UsbExclusiveOwner,而不是反复重装或重启模块。 -
CLCC 字段语义
CLCC mode=0是语音,mode=1是数据。只根据“存在 CLCC 行”判断正在通话会造成误判。 -
持久配置变更的安全流程
必须坚持“备份原值 → 精确写入 → 立即回读 → 再重启 → 验证真实枚举 → 功能测试”的顺序,避免模块因 USB 组合错误而失去控制接口。
7.2 对项目的改进建议
建议 DJOneHub 后续增加:
- 初始化时主动查询
AT+QADBKEY?,发现挑战码时进入明确的 ADB 解锁步骤; - 不再仅凭
…,0,1判断完整通话音频已就绪; - 在 UI 显示“UAC 已存在,但 ADB 未枚举/未解锁”的独立状态;
- 初始化完成后检查 interface 6 的 class/subclass 和 Bulk 端点;
- 遇到
LIBUSB_ERROR_ACCESS时显示接口占用进程,并提示执行adb kill-server; - 在拨号前做快速 ADB 可用性检查,避免电话接通后才发现没有声音;
- 对重启后配置回退、应用退出和验证超时提供可靠的恢复流程。
8. 重要提醒
- 不要随意修改
USBCFG中其他功能位或照抄其他型号模块的 VID/PID; - 不要在真实语音通话期间修改 USB 配置或重启模块;
- 不要让普通 Android adb server 与 DJOneHub 同时占用模块接口;
- 如需恢复,优先使用 DJOneHub 已保存的原始配置备份;
- 文档中的 QADBKEY 密码与本次模块挑战码相关,不应直接用于其他模块。