什么是QMI?
Qualcom Message Interface 高通信息接口
背景:2005-2006年部署(原高通MSMTM接口),所有服务最初都是由DATA团队设计和维护的,功能是按照AT命令标准构建的,主要用于连接管理器类型应用程序,可通过USB连接(QMUX传输)连接到电脑。
2009年QMI取代RPC(Remote Procedure Call),QMI的范围开始扩大,无线电接口层(RILs)开始向QMI过渡,并定义了必要的支持;底层由更健壮的通信(IPC路由器传输)替代,通过共享内存方式(SMD)在片上SOC之间可用。
高通平台目前都是非对称多核心,最主要的是AP和Modem。两个处理器怎么进行通信呢,我们把AP和Modem当作两个主机,问题就变得了很简单,TCP/IP协议不是一种非常成功的进程间跨主机通信方式。高通没有采用这种方式,但是借鉴了TCP/IP的框架设计。它的框架是这样的,内核态:基于共享内存(SMD)实现链路层,扩展协议域;用户态,封装出类似于socket函数的接口,用于用户态使用。而我所描述的QMI就是用户态使用的API接口,这些接口非常类似于socket,只要有个socket编程的经验的是会容易理解的。
QMI提供什么?
支持同步和异步接口;
支持在在多个处理器之间进行通信;
具有良好的可扩展性;
支持多客户端并发运行;
支持多个服务端并发运行,且每个服务端还对应多个客户端;
每个服务端还支持版本信息;
QMI架构
Messages
Request message ——一条request消息由客户端发往服务端,并由服务端进行处理。
Reponse message——每一个request的消息都会有一个reponse消息与之对应,类似于一个函数的返回值。如果请求消息等效于调用函数,则响应消息等效于返回给调用方的结果。
Indiction message——这类消息是由服务端发给客户端的异步消息,在任何indication message发送之前,客户端首先需要通过request message与服务端建立连接,indication消息可以在任何时候发送。
消息的特点:
- 消息由Type-Length-Value(TLV)单元构成;
- 类型和长度是固定大小,而值是可变长度的;
- 每个消息中可以出现多个tlv值;
- 可以在该值中显示多个数据元素;
- 对于每个消息的TLV id都是唯一的;
消息的结构:
- 由传输头和服务级消息组成
- 每个传输的传输头都是唯一的
Service Message服务级负载对于所有传输都是通用的,并包含以下元素
消息ID
消息长度
零或多个可变长度的tlv
Clients
- 任何想要使用QMI的内部或外部客户端应用程序
Services
- 任何想要使用QMI的内部应用服务程序
Common Libraries
- 为客户端(QCCI)和服务(QCSI)提供功能
- 通用API适用于所有平台/传输
- 封装传输和特定于平台的更改,以便共享客户机/服务代码
- 为每个服务消息提供编码/解码支持
Transports
QMUX
- 高通多路复用协议(QMUX);
- 原始传输通过一个有线USB以太网适配器;
- 支持MPSS处理器上的服务;
- 支持在应用程序(APPS)处理器或usb连接的主PC上的客户端;
- QMUX的客户端是用于特定于平台的,不同平台可能有差异;
IPC Router
- IPC路由器是一个面向消息(message-oriented)的路由器,用于支持Qualcomm QCCI QCSI;
- 增强了同一设备上处理器之间的传输;
- 通过SMD在路由节点之间传递QMI有效负载;
- 支持任何处理器上的客户端和服务;
- API对于所有平台都是通用的,即使实现不同;
QMI IDL
QMI接口是用接口定义语言(IDL)编写的,IDL编译器自动生成文件 .c和.h
编码/解码库
- 库是独立的,并且与自动生成的文件解耦
- 编码/解码消息的元信息将在.c作为Service_Object生成
- 在服务/客户机启动期间,元信息将注册到QCSI/QCCI进行消息编码/解码
- 客户端/服务可以使用QCCI/QCSI API发送/接收消息
QMI消息编码标准
1. 不使用浮点型数据,而是使用Q16类型的固定点数,即提供16位2位补码整数部分和16位小数部分的定点实现。
2. 命名约定
- 为了使所有消息实现保持一致,service端需要固定的独有的前缀
- 结构体以_type结尾
- 枚举以_enum结尾
- 请求消息以_req_msg结尾,类型以_REQ结尾
- 消息回应以_resp_msg结尾,类型以_RESP结尾
- 指示消息以_ind_msg结尾,类型以_IND结尾
QMI QCCI
QCCI是一套用于客户端从服务器接收信息或者发送消息到服务器的API集合。
以下步骤描述初始化客户机:
1. QMI客户机创建一个通知句柄来接收服务到达和退出事件通知。然后是客户端
用notifier句柄注册一个通知回调函数;
2. 当服务到达时,将设置信号,并调用通知回调函数来客户端关于服务到达的消息;
3.QMI客户机在收到服务到达通知后解析服务地址;
4. QMI客户机初始化服务的客户机句柄,该服务的地址在步骤3中解析。然后,这个客户端句柄用于与相关的QMI服务交换QMI消息;
5. 初始化客户端句柄时,将向客户端句柄注册一个错误回调函数。此错误回调用于接收关于服务重启或子系统重启的任何通知;
6. 此时,QMI客户机可以释放notifier句柄,以避免任何资源泄漏;
7. QMI客户机同步或异步发送请求消息,如上图所示;
8. 当所有相关的QMI消息通信完成时,QMI客户机释放客户机句柄。
QCCI APIs Callback function prototypes
- qmi_client_recv_raw_msg_async_cb
- qmi_client_recv_msg_async_cb
- qmi_client_ind_cb
Connection APIs
- qmi_client_notifier_init
- qmi_client_init
- qmi_client_get_service_list
Message sending APIs
- qmi_client_send_raw_msg_sync
- qmi_client_send_msg_sync
- qmi_client_delete_async_txn
Release connection APIs
- qmi_client_release
Encode and decode APIs
- qmi_client_message_encode
- qmi_client_message_decode
QMI QCSI
QCSI是用于接收客户端的请求及对其作出响应。另外,它也用来发送指示消息(indication message)以及对消息进行编解码(encode/decode),这个消息由server端主动发出。
消息的类型有如下三种:
Request message ——一条request消息由客户端发往服务端,并由服务端进行处理。
Reponse message——每一个request的消息都会有一个reponse消息与之对应,类似于一个函数的返回值。如果请求消息等效于调用函数,则响应消息等效于返回给调用方的结果。
Indiction message——这类消息是由服务端发给客户端的异步消息。在任何indication message发送之前,客户端首先需要通过request message与服务端建立连接。indication消息可以在任何时候发送。Callback function prototypes
- qmi_csi_connect
- qmi_csi_disconnect
- qmi_csi_process_req
Registration APIs
- qmi_csi_register
- qmi_csi_unregister
Message sending APIs
- qmi_csi_send_resp
- qmi_csi_send_ind
- qmi_csi_send_broadcast_ind
Event handling APIs
- qmi_csi_handle_event
QMI SERVER
这个api构建在QCSI框架之上
其主要目的是方便客户机以更少的工作量实现服务器
该框架允许用户编写基于对象的服务器,扩展核心服务器对象。核心服务器对象提供每个服务都需要的通用功能
Callback Function Prototypes
- qmi_csi_connect
- qmi_csi_disconnect
- qmi_csi_process_req
Constructor/Destructor APIs
- qmi_core_server_new
- qmi_core_server_delete
Registration APIs
- qmi_core_server_register
- qmi_core_server_unregister
- qmi_core_server_register_client
- qmi_core_server_unregister_client
Message Dispatch APIs
- qmi_core_server_dispatch_msg
- qmi_core_server_send_ind
- qmi_core_server_send_resp
QMUX
QMI Multiplexing Protocol(QMUX):QMI的复用协议
消息从控制点经过类似socket的线程传到QMI接口后,QMI负责对数据进行封装,加上QMUX消息的头,发送到QMUX层,
再通过QMUX层传到共享内存到BP侧。
整个QMUX控制信道的结构如上图,
- I/FType:QMI将控制点数据封装后,发送到QMUX前,加的消息头,长度为一个byte,值通常为0x01,表示这个消息为QMUX消息,如果是其他值,则为其他消息。
- Length: QMUX消息的长度,不包括I/F Type。
- ControlFlags:控制位,表示消息传输的方向。长度为1个byte,只有第7个bit是标志位,其他位为0,bit7=1说明QMUX消息由服务端发送,bit7=0由控制点发送。
- Clien ID: 控制点的标识,在控制点和服务端都需要赋值,当在服务端发出的消息Client ID的值为0xFF,表示该消息为广播消息,由服务端主动发出,被所有控制点搜到。
QMUX SDU和TLV结构:
在整个控制信道的消息中,出去消息标识头I/F Type,和QMUX消息头,数据传输在QMUXSDU中完成,QMUX SDU里面的数据需要支持Type Length Value(TLV)的格式。
TLV格式的数据存放在QMI Service Message里面的Value中。
- Control Flags:表示消息是请求、响应还是指示。