探索数字中国新脉搏:www.54c.com.gov.cn开启智慧生活新篇章

核心内容摘要

穿越时空的绮梦:怡红院欧美,一场未曾设想的浪漫邂逅
告别“撸”之烦恼,拥抱“不撸帝”的轻盈人生_1

YouTube18

我们依然从一个真实的“现场”出发来探究其背后的技术细节。

最近在 Amlogic S905x5 平台上适配高通 Wi-Fi 7QCC2072/PCIe 接口芯片时我们遇到了 QRTRQualcomm IPC Router版本不匹配的问题。

具体 LOG 如下当然这个问题最后并不是 QRTR 的问题仍然是内存的原因。

不过正好借此机会我们聊一聊高通 QRTR 机制。

前世今生为什么会有 QRTR我们先来聊一聊 QRTR 的历史背景为什么会有 QRTR在 QRTR 出现之前以及早期的高通平台APApplication Processor与其他子系统如 ModemADSPWi-Fi FW的通信往往依赖于特定的字符设备驱动或私有的内存共享机制如老旧的 SMD - Shared Memory Driver。

这种方式有几个明显的痛点私有接口难以维护每次新增一个子系统或者改变底层传输方式比如从共享内存改为 PCIe上层应用都需要修改代码。

点对点限制老的机制通常是点对点的Point-to-Point难以处理复杂的路由例如 AP 要通过 Modem 转发消息给另一个挂在 PCIe 上的外设。

缺乏服务发现就像没有 DNS 的网络客户端很难知道某个服务例如位置服务、Wi-Fi 控制服务到底运行在哪个 DSP 上端口是多少。

QRTRQualcomm IPC Router的引入就是为了解决这些问题。

它在 SoC 内部构建了一个“微型互联网”如上图所示QRTR 的核心改进在于接口标准化Socket 化QRTR 在 Linux 内核中实现了一个新的协议族 AF_QIPCRTRLinux 用户空间可以使用标准的 socket 来进行跨核通信。

这对于 Linux 开发者来说极其友好。

传输层解耦QRTR 位于协议层。

底层传输可以是共享内存GLINK/SMEM也可以是 MHI通过 PCIe 传输常见于外挂 Modem/Wi-Fi上层应用完全无感。

动态服务发现QRTR 引入了类似 名字服务Name Service的机制服务启动时会广播自己的 ID客户端可以动态查询。

QRTR 如何像一个“中央路由器”一样连接起不同架构、不同操作系统的处理器核心。

1 QRTR 它是如何工作的你可以把 QRTR 想象成一个运行在 SoC 内部的微型互联网如上图所示。

地址结构node:portNode节点 ID每个子系统有一个唯一的 ID。

例如APApplication Processor可能是 1Modem 可能是 0 或 2ADSP 可能是 3。

Port端口号类似于 TCP/UDP 端口。

每个服务Service监听一个端口。

例如QMI 服务通常会绑定到一个特定的 Port。

协议栈位置上层ApplicationQMIQualcomm MSM Interface消息通常作为 Payload 封装在 QRTR 包中传输。

中间层Router QRTR 负责路由。

它决定这个包是发给本机的另一个进程还是发给 Modem或者是转发给 ADSP。

底层Transport真正的物理数据搬运工如 GLINK基于 RAM 的轻量级链路或 MHIHost Interface基于 PCIe。

我们可以从驱动开发的视角纵向切开 APApplication Processor纵观 QRTR 是如何从用户态一步步穿透内核最终到达硬件驱动。

2 为什么高通 Wi-Fi 驱动这么复杂讲完了理论我们回到移植现场。

如果你习惯了 Realtek 或 Broadcom 的 Wi-Fi 驱动第一次接触高通 Wi-Fi如 QCC2072移植到第三方平台如 Amlogic/Rockchip时大概率会产生疑问“为什么不像 Realtek 那样直接用 PCIe/SDIO 读写寄存器或者发个简单的 Mailbox 就不行非要搞这么一套复杂的 Socket QMI MHI 机制”其实并不是 Wi-Fi 驱动想用 QRTR而是 Wi-Fi 芯片里的固件Firmware只认 QRTR/QMI。

固件架构决定现代高通 Wi-Fi 芯片内部运行着独立的操作系统通常是 ThreadX 或类似的 RTOS并没有为 PCIe 专门写一套控制协议。

它们复用了高通 Modem基带的成熟架构。

通信对等性在芯片内部Wi-Fi 控制平面Control Plane被视为一个“服务”Service。

固件启动后会通过 QRTR 协议向外界广播“我是 Wi-Fi 服务我在节点 X端口 Y有人要连我吗”初始化流程强依赖当你在 Amlogic 上加载 wlan.ko 时驱动必须通过 QRTR 发送 QMI 消息来完成最基本的初始化。

例如Capability Exchange告诉固件 Host 端支持多大的内存、什么特性QMI_WLFW_HOST_CAP_REQ。

BDF Download下发板级数据Board Data File告诉芯片射频参数。

Mode Switch通知固件进入 FTM测试模式还是 Mission正常工作模式。

如果没有 QRTR这些握手无法完成芯片连射频都开不起来。

1.

1 利弊分析针对非高通 SoC 宿主对于我们这些移植者Amlogic/Rockchip 开发者来说这确实是一把双刃剑。

弊端门槛高启动流程复杂内核配置的“全家桶”依赖你不能只开一个 Wi-Fi 驱动。

为了让 Wi-Fi 跑起来你必须在 Linux Kernel 里搞一整套高通的“周边基建”QRTR / MHI / QMI。

这对于追求精简的嵌入式系统是负担。

调试难度呈指数级上升传统的 PCIe Wi-Fi 驱动加载可能就是“Probe - 寄存器读写 - Ready”。

而高通的流程是PCIe Link Up - MHI 初始化 - MHI 通道建立 - QRTR 握手 - QMI 消息往返 - WMI 初始化 - Ready。

这中间任何一个环节比如 MHI 状态机卡住都会导致 Wi-Fi 无法启动。

好处控制面与数据面彻底解耦。

这是最大的技术优势也是高通敢于这么设计的底气。

QRTR 只负责控制指令如“扫描”、“连接”、“设置功率”等低频操作。

独立的数据通道真正的大流量数据sk_buff走的是独立的 DMA 环CE - Copy Engine 或基于 MHI 的数据通道。

这意味着即使控制面因为某种原因阻塞比如 QMI 消息多也不会直接卡死数据传输反之亦然。

工具链复用与远程调试因为走了 Socket理论上你可以在 Amlogic 的用户空间写一个工具直接给 Wi-Fi 固件发 QMI 指令查询内部状态温度、死机堆栈等而不需要改内核驱动。

甚至你可以用 qrtr-lookup 看到 Wi-Fi 固件在线状态甚至可以用高通的工具直接通过 TCP/IP 远程连接到 QRTR 端口上调试 Wi-Fi 固件。

Show Me The Code核心代码解析前面梳理了高通 QRTR 的技术原理下面我们扒一扒其源码看看它是如何落地的。

下面所有代码都是基于高通 QCC2072 Wi-Fi 驱动及 Linux qrtr 核心模块。

如需代码请扫下面群二维码。

QRTR 在 Wi-Fi 驱动框架中的核心逻辑基本都藏在如下几个文件中

1 QRTR 的架构层次我们先看整体总观下 QRTR 的整体架构。

在 Linux 内核空间中QRTR 其实并不是孤立存在的它通过清晰的分层向上承接 Socket 调用向下对接硬件总线。

如下所示QMI 和 QRTR 通过内核 socket 进行上下层的通信。

如上所示QMI 通过 socket 接口下发指令到 QRTRQRTR 负责路由分发它不知道底层是 PCIe 还是共享内存它只认 Node ID。

如上图的 Wi-Fi 架构下QRTR 把包发送给 MHI 的 Ring Buffer然后下发给 Wi-Fi 固件。

2 QRTR 初始化流程对很多做驱动的同学来说一个完整的调用栈往往能让事情事半功倍那我们就来看看 QRTR 在 unified_wlan_cnss_core.c 中调用栈的情况。

如上图所示mhi_init先修路。

MHI 总线驱动必须先 ReadyPCIe 链路必须通。

qrtr_proto_init再建邮局。

注册 AF_QIPCRTR 协议族。

qrtr_ns_init雇佣邮递员。

启动名字服务Name Service准备广播 Hello 包。

qrtr_mhi_init最后通车。

将 MHI 通道注册为 QRTR 的一个 Endpoint端点打通任督二脉。

Endpoint端点管理qrtr.hEndpoint端点结构qrtr.h协议族注册成功后内核就会挂载以下 socket opsqrtr.c

3 核心数据结构QRTR 的“语言”路通了后我们再来聊一聊 QRTR 如何通信QRTR 定义了一套自己的包结构。

具体如下所示Socket 地址结构linux_inc/uapi/linux/qrtr.h协议头格式V1 协议头qrtr.c- 32字节协议头格式V2 协议头qrtr.c- 紧凑格式消息类型linux_inc/uapi/linux/qrtr.h

4 桥梁MHI 传输层与 API 接口QRTR 的核心层是不碰硬件的它依赖“端点Endpoint”来传输数据。

在 Wi-Fi 场景下这个端点就是 MHI。

设备结构mhi.c数据流mhi.c发送路径qrtr_sendmsg - node_enqueue - mhi_queue_skb。

数据被封装成 sk_buff直接塞入 MHI 的发送队列。

接收路径MHI 收到中断 - dl_callback - qrtr_endpoint_post。

数据从 PCIe 出来后被丢进 QRTR 的接收队列唤醒等待的 Socket。

MHI 通道配置mhi.c

5 命名服务ns.c为什么不需要知道 Wi-Fi 固件在哪个 Node全靠 NSName Service在后台默默工作。

核心数据结构命名服务工作流程核心机制广播NS 启动后会向广播地址发送 HELLO。

监听Wi-Fi 固件收到 HELLO回复 NEW_SERVER告诉 AP“我是 Wi-Fi 控制服务我在 Node X”。

记录内核更新本地的 Radix Tree基数树将服务 ID 映射到具体的 Node ID。

以后应用层发包只需要填“服务 ID”内核自动查找路由表发给对应的 Node。

4 流控制机制qrtr.c在实战中“发送阻塞”是驱动 Hang 死的最大元凶。

QRTR 设计了一套基于水位的流控机制。

水位机制QRTR_TX_FLOW_HIGH当待确认的包超过 10 个发送线程就会被强制休眠。

QRTR_TX_FLOW_LOW只有当对端确认了足够多的包Pending 数降到 5 以下发送线程才会被唤醒。

流控制逻辑通过上述组略的浏览大家我们对高通 Wi-Fi 驱动的底层通信逻辑有一个了解了。

其实代码只是骨架数据流才是灵魂。

看懂了静态的数据结构并不代表就能搞定驱动代码。

真正的挑战发生在其系统上电的那几百毫秒内——QMI 消息是如何被层层封装塞进 QRTR 的信封最后跳上 MHI 的高速列车的当 Wi-Fi 固件“嗷嗷待哺”请求 BDF板级数据时驱动是如何通过 QMI 握手精准响应的为什么我的 Log 里全是 QMI transaction failed究竟是 MHI 的通道堵了还是 QRTR 的路由丢了下一篇我们将离开静态代码结合驱动 Log逐帧拆解 QMI/QRTR/MHI 的交互逻辑。

希望本文对你所有帮助也欢迎大家私信或者评论区积极讨论相互学习一起成长。

独行者速众行者远。

连接 · 交流 · 共创。

欢迎加入微信群聊Join US保持联系保持好奇。

若二维码过期或群满请添加微信19901741315备注“入群”拉你一把。

无风险9.1免费版安装软件网站-无风险9.1免费版安装软件网站应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123