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

资讯详情

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

高通平台新增QMI接口实战:从内核配置到用户态验证全流程

高通平台新增QMI接口实战:从内核配置到用户态验证全流程 简介面向高通平台驱动、协议栈或modem相关开发工程师在QMI框架中增加新接口是功能定制与移植时的常见需求若不清楚底层消息表与类型表的注册规则很容易在运行时出现接口找不到或类型错位的问题。文档以高通LE2.2/GNSS DRV FLOW为实例系统梳理了从IDL生成到消息表、类型表落位的完整关键路径。作者逐步说明了uim_remote_message_table_v01、loc_message_table_v02、uim_remote_type_table_v01等具体表项的定位方法并给出两个必须对照确认的位置新增message必须追加到message_table末尾且其type由在message_table中的位置决定同时user_data类型还需要同时确认它在uim_remote_type_table_v01中的索引和uim_remote_qmi_idl_type_table_object_referenced_tables_v01中的位置避免两层引用关系错配。文档还对inheritance_obj-p_type_table-p_referenced_tables这类指针链路做了备注便于在实际代码中快速索引与排错整体为单个docx文件共291KB文字精炼、步骤明确适合在修改代码时对照查阅。已有12212人学习下载对于正在做高通平台QMI二次开发、modem/GNSS功能扩展或接口调试的工程师是一份能直接落地的实操笔记。1. 背景先行为什么要在高通平台增加QMI接口做过高通平台驱动开发的人应该都有体会QMIQualcomm MSM Interface这套协议栈就像一块跳棋板上层应用要到modem调制解调器那边取数据不经过它基本走不通。前阵子接到一个需求要在现有的AP侧Linux系统里新增一个QMI服务通道把模组的信号质量、驻网状态、SIM卡信息这些数据实时捞回来给业务层用。整趟走下来涉及内核配置、设备树、驱动注册、端口枚举和用户态验证踩了不少坑这里把关键点整理一下当作是给自己的备忘。QMI本质上是一套基于共享内存或USB传输的IPC进程间通信协议它跑在高通的modem和AP应用处理器之间。Linux侧通常通过内核里的qrtrQualcomm Remote Transport层来承载QMI消息用户态则通过libqmi库或qmicli工具来发请求、收响应。所谓的“增加QMI接口”在不同场景下有不同含义是在现有通道上新增一个服务比如新增一个自定义QMI service还是增加一个物理逻辑上的QMI端口比如加一路USB QMI网卡甚至是新增一个独立的QRTR节点把数据路由到新设备。我这次的需求偏向第二种需要在现有系统里增加一个可用的QMI数据通道同时确认它在用户态能正常枚举。这篇内容适合谁看两种人最合适一是刚接手高通平台驱动开发没多久、对QMI框架还停留在“听说过”阶段的工程师二是做上层通信应用、需要和modem打交道但不清楚底层链路怎么设计的同学。看完你至少能明白一个QMI接口从内核到用户态要走通需要哪些环节、每个环节卡住会是什么现象以及怎么快速定位问题。2. 动手之前先想清楚QMI接口的整体链路与关键配置项2.1 一条QMI通道从内核到用户态要经过哪些环节一条可用的QMI接口数据路径大致是这样的modem侧的服务比如DMS、NAS、UIM通过共享内存或标准接口把消息发出来AP侧的内核qrtr驱动负责将消息从物理链路比如smem共享内存、rpmsg远程消息传递、或USB上收下来然后按服务ID和端口号分发给对应的套接字。用户态进程通过AF_QIPCRTR协议族的socket直接连接某个service_id:port就能收发QMI报文。要“增加”一个接口最核心的几个改动点分别是设备树节点、驱动绑定、QRTR端口分配、用户态库的编译选项。任何一环没对上结果就是接口不存在、枚举失败或者发消息无响应。以我这次的项目为例平台是高通SM8250系统采用标准Linux内核版本5.4modem侧固件已内置了目标QMI服务。我需要做的其实是在AP侧确认/新增对应的QRTR服务端口并让用户态能通过/dev/qrtr或者直接socket访问。某些场景下还需要把新增接口以网络设备形式暴露出比如QMI WWAN网卡那就还要配置rmnet通道。2.2 内核相关配置项梳理内核里与QMI强相关的配置项对照Kconfig整理了一张表方便排查问题时快速核对配置项作用缺失时的典型现象CONFIG_QRTR启用QRTR协议核心框架无法创建AF_QIPCRTR套接字CONFIG_QRTR_SMD基于共享内存Shared Memory的QRTR传输层AP和Modem之间的服务列表为空CONFIG_QRTR_TUN提供内核态到用户态的测试回环通道某些调试工具无法使用CONFIG_RPMSG远程处理器消息传输基础与ADSP/CDSP通信异常CONFIG_QCOM_SMEM共享内存管理modem内存映射失败服务不可见CONFIG_USB_NET_QMI_WWAN通过USB承载的QMI/wwan网卡驱动USB接口下找不到QMI网卡节点我在新内核上第一次编译时发现CONFIG_QRTR已默认打开但CONFIG_QRTR_SMD没有勾选导致系统启动后QRTR总线没有设备用户态qrtr-lookup查不到任何服务。这个问题很隐蔽因为内核编译不报错只有开机后服务列表一片空白排查起来容易被忽略。提示修改内核配置后务必确认新的kernel镜像真的烧进去了。曾经遇到过改完配置编译完结果fastboot刷错了分区表整半天全白忙。2.3 设备树节点的作用与典型结构设备树在高通平台里决定了驱动能否被实例化和硬件资源中断、内存地址、时钟、电源域是否就位。QMI相关设备树节点主要集中在/sys/firmware/devicetree/base/soc/目录下典型的SMD边缘节点长这样smd-edge { compatible qcom,smd-edge; label modem; qcom,smd-edge 0; qcom,smd-channel APPS_RIV; qcom,smd-irq tlmm_pinmux 32; ... };如果是新平台、新modem光看默认设备树可能不够需要确认对应的smd通道名和中断号是否与modem侧释放的通道一致。高通各平台的默认配置一般都能用但如果做了modem侧裁剪比如把某个服务迁移到了新通道那AP侧设备树的通道映射就得同步改。另外新的设备树绑定可能不再使用传统SMD而是走rpmsg节点。遇到这种情况要确认glink子节点配得对不对。Glink平台节点通常长这样smd-edge { compatible qcom,glink-smem; qcom,remote-pid 1; transport smem; mboxes apcs_glb 12; mbox-names smd; label mpss; };这种差别虽然不影响最终用户态看到的QMI服务但改错了会导致整个modem链路起不来system log里能看到明显的超时错误。所以拿到任务之后第一件事不是急着写代码而是对照现有设备和平台文档看走的是SMD、RPM还是Glink通道。3. 实操过程从内核驱动到用户态接口的完整烧录验证3.1 步骤一核对硬件和modem侧服务状态在动任何代码之前先确认硬件上modem部分已正常启动。启动后第一时间抓串口日志搜关键字modem、mpss、subsys正常状态是subsys_modem: modem is ready或者ssr_remoteproc: modem boot success之类的信息。如果modem还没起来就去看QMI等于无源之水。建议先跑一轮底层的开机日志“有没有加载”永远先于“好不好用”。确认modem起来之后再通过qrtr-lookup或者直接读QRTR节点看modem侧上报了哪些服务。操作方式是# 编译并放入板端的工具 qrtr-lookup正常会输出类似这样的内容service 17 version 1 instance 0 node 1 service 17 version 2 instance 0 node 1这里的service号就是QMI服务类型17是DMS18是NAS21是UIM等等node表示由哪个远程处理器发布的服务。若这里列不出来说明内核QRTR链路有问题先回头查内核配置和设备树暂时不需要动任何上层代码。3.2 步骤二配置内核并编出可用镜像确认modem侧OK后进入内核源码目录勾选相关选项。建议直接用make menuconfig搜索关键字逐个确认make ARCHarm64 menuconfig依次check以下项Networking support Bluetooth QIPC部分平台结构下有嵌套不强制Networking support QMI helpersNetworking support QRTRDevice Drivers Remoteproc QCOM Shared MemoryDevice Drivers Network device support USB Network Adapters如果项目用的是AOSP或Yocto环境改完配置后需要重新生成defconfig不要图省事直接改arch/arm64/configs/下的既有文件最好基于厂商提供的base配置文件修改以便后续diff和review。编译完成后确认生成的Image.gz和dtb时间戳正常。烧录方式因平台而异常见的是fastboot flash boot加fastboot flash dtbo。烧录完成后先引导系统执行uname -a确认内核版本与编译时间确保镜像生效。注意部分平台存在boot.img内嵌dtb的打包方式编译后需要显式重新打包比如使用mkbootimg --dtb否则新设备树不生效白白烧录。3.3 步骤三确认驱动绑定和QRTR服务可见性系统起来后先看设备树节点是否被正确解析然后检查QRTR总线上挂载的设备# 查看相关平台的设备节点 find /sys/firmware/devicetree/base -name *smd* -o -name *glink*再看内核日志里相关驱动的probe情况dmesg | grep -E qrtr|smd|glink|rpmsg正常会看到类似qrtr: registered router qrtr: node 0 is up qrtr_smd: QMI SMD transport registered如果QRTR已注册但看不到node 1 is up大概率说明SMD/Glink通道没有与modem建立连接。最有效的排查方法是在板端依次查看QRTR节点状态ls /sys/kernel/debug/qrtr/ cat /sys/kernel/debug/qrtr/namesnames文件列出的内容就等价于用户态qrtr-lookup看到的结果但多一条直接内核态的信息。若发现modem侧节点始终没有出现建议回头重点检查qcom,smem-state、中断号这些和平台ROM固件约定好的参数。3.4 步骤四用户态编译libqmi并验证接口内核链路通了接下来把用户态工具准备好。高通官方推荐的是libqmi源码在freedesktop的git仓库上https://gitlab.freedesktop.org/mobile-broadband/libqmi克隆后正常编译git clone https://gitlab.freedesktop.org/mobile-broadband/libqmi.git cd libqmi meson build ninja -C build编译选项里重点确认两个一个是--enable-qrtr另一个是--enable-mbim。从libqmi 1.24版本开始QRTR支持作为独立构建选项老版本里可能需要额外指定。编译完成后把qmicli和配套工具推到板端或交叉编译进根文件系统。验证接口是否新增成功可以用标准的qmicli命令来查询modem基本信息qmicli -d /dev/cdc-wdm0 --dms-get-ids qmicli -d /dev/cdc-wdm0 --nas-get-signal-info这里的/dev/cdc-wdm0是USB QMI设备节点。但如果是走共享内存而非USB那么-d参数就不适用这时候要改用--device-open-qrtr方式qmicli -p --device-open-qrtr -d 17 --dms-get-ids其中17是目标服务ID。这种方式能直接验证QRTR通道下服务是否可交互。如果查询能返回Device ID或IMEI说明整条链路已经通了。注意qmicli默认会对modem加锁如果同时开了多个会话可能会提示“Device is already open”。调试时建议使用带-p参数进入“骚扰模式”直接绕过锁。4. 常见问题与排查速查表4.1 服务列表为空先别急着怀疑驱动遇到qrtr-lookup什么都没有大多数人第一反应是改驱动。实际上多数时候问题出在内核配置或modem固件没有完全启动。这里给一个排错顺序实测最省时间现象第一步排查第二步排查第三步排查qrtr-lookup无输出检查modem启动日志确认CONFIG_QRTR_SMD存在检查SMD通道中断配置服务列表有内容但连不上确认服务ID和实例号正确检查多个client是否占用端口用qmicli -p强制访问发送请求超时确认modem当前状态飞行/异常重启检查功耗管理是否休眠抓包看是否有response设备节点不存在确认cdc-wdm驱动枚举看USB描述符有没有QMI接口检查内核USB规则过滤4.2 驱动绑定失败常用的三板斧第一种设备树节点存在但驱动未probe。先看driver_override是否干扰了匹配ls /sys/bus/platform/devices/*/driver再用of_device_is_available检查节点状态。有时候厂商的默认设备树里该节点被status disabled了此时需要显式置为okay。第二种节点匹配了但probe返回错误。常见原因是中断申请失败或mbox请求失败内核日志里一般有明显报错smd-edge: failed to request smd interrupt这种大多数是设备树里interrupts或者mboxes配置和实际硬件不一致换一组正确的pin或mbox名称即可。第三种probe成功但QRTR依旧不通。这种情况多半不是驱动本身问题而是modem侧固件没有释放该服务。可以在modem侧通过QXDM高通调试工具抓取DIAG_LOG确认服务端是否真的存在不要一上来就怀疑AP侧代码。4.3 用户态工具查询无响应的隐藏原因用户态查询无响应容易被忽视的原因是QRTR包被功耗管理挡住了。系统在深度睡眠时共享内存总线可能不工作这时qmicli发出的消息就“悬空”了。排查方式是查看suspend前后dmesg是否有USB或IPC相关状态切换。另一种常见情况是多个进程同时打开了同一个QMI端口。因为QMI通道是点对点单服务器模型多个客户端同时使用很容易引发冲突。这时候要么用qmicli -p抢占要么在架构上做一个QMI代理服务所有业务统一通过这个代理访问modem侧避免端口争抢。经验生产环境千万不要让每个上层应用都直接连QMI服务端口很容易把modem侧的服务搞挂。最稳妥的做法是写一个常驻的middleware比如基于libmbim或自定义QMI daemon统一管理通道和请求分发。5. 一些值得留意的细节与心得增加QMI接口这件事表面上看就是打开一个配置、写一段树节点、编一次内核但真要稳定复现还是有不少坑。第一个细节是内核版本和工具链版本要匹配。某次我在5.4内核上手动升级了libqmi到1.30结果新版库默认启用了新的qrtr连接逻辑跟旧内核有个小功能不兼容导致查询消息一直发不出去。最后回退到1.26版本解决。所以升级用户态库之前千万确认内核版本年份和库版本差不多时间别差得太远。第二个细节是多核平台的affinity问题。在高通平台AP侧有多个remoteproc比如modem、ADSP、CDSP它们的QRTR节点号不同。写自动化脚本时不要写死node 1最好通过服务发现动态获取节点号否则下次modem的编号有变化你的脚本就废了。第三个细节是安全策略。部分平台开启了对QRTR socket的SELinux限制。增加QMI接口后如果应用层反映“socket创建失败”或“bind失败”但内核日志却干干净净大概率是SELinux策略问题。此时抓avc denied日志并针对对应进程补充allow规则即可。开发过程中我也养成了一些好习惯放在这给大家参考每次动手前先把板端当前状态内核hash、设备树时间戳、modem固件版本完整记录一次形成基线。改坏状态有地方退。每次改动尽量只碰一个变量。比如内核配置批次、设备树树节点批次、用户态工具批次分开验证不要混在一起排查。所有验证脚本统一放到一个角落目录管理方便后续回归和交接。初次接触QMI这套体系的人可能会被一堆模块缩写劝退。但其实把它拆成“modem侧发布服务、内核负责路由、用户态负责访问”三个环节问题定位就有清晰方向。接口能不能通每一步都有自己的验证手段真正把每层的状态日志打扎实了增加一个新接口不过是个日常工程活。本文还有配套的精品资源点击获取
返回列表