高通QMI学习整理

高通QMI学习整理qmi 介绍 qmi

大家好,欢迎来到IT知识分享网。

1. 什么是QMI?

QMI是CS结构,分为client端和service端,一个service可以同时服务于多个client;在我们的代码中,一般client在AP侧的应用中注册,向CP中的service来发送req,以获取某些数据或者执行某些操作;

QMI的消息类型有三种:
Request : client 向 service 发送请求
Response : service 在收到请求后向 client 发送应答消息
Indication : service 主动向所有 client 广播通知消息


QCCI:Qualcomm common client interface
是一套用于客户端发送消息到服务器或者从服务器接收信息的API集合;

QCSI:Qualcomm common service interface
用于接收客户端的请求及对其做出响应。另外,它也用来发送指示消息(indication message)。

QMI协议常用service:

2. 添加QMI

2.1 添加及初始化client

主要函数为:

2.2 添加及初始化service

主要函数为:

qmi_csi_register(service cookie) qmi_csi_register_return(service_provider) (New Client First Message)Event/Signal qmi_csi_handle_event(service_provider) qmi_csi_connect callback(client_handle,service_cookie) qmi_csi_connect return(connection handle) 

service注册的标准API为qmi_csi_register,其定义如下

qmi_csi_error qmi_csi_register ( qmi_idl_service_object_type service_obj, qmi_csi_connect service_connect, qmi_csi_disconnect service_disconnect, qmi_csi_process_req service_process_req, void *service_cookie, qmi_csi_os_params *os_params, qmi_csi_service_handle *service_provider ) 

eg:

 rc = qmi_csi_register(ping_get_service_object_v01(),qc_connect_cb, qc_disconnect_cb, qc_handle_req_cb, &service_cookie, os_params, &service_cookie.service_handle); 

2.3 添加QMI消息

2.3.1 消息声明
#define QMI_PING_REQ_V01 0x0001 #define QMI_PING_RESP_V01 0x0001 #define QMI_PING_IND_V01 0x0001 
2.3.2 消息数据结构

包含必选成员或可选成员;可选成员必须有对应的 valid 字段,valid 字段仅在编码时使用,最终编码的消息中并不包含 valid 字段;

还可能会有可变长度数组,会自带 length 字段,此 length 与 TLV 中的 L 无关,仅代表所描述可变长度数组的实际数组项数,用于消息编解码,可编码进最终消息,也可不编码进最终消息,由.c 文件中的消息编码规则决定。

eg:

typedef struct { 
    /* 必选成员 */ char ping[4]; /* 可选成员 */ uint8_t client_name_valid; //当需要传递 client_name 时,name_valid 必须为 TRUE,也就是 1; ping_name_type_v01 client_name; }ping_req_msg_v01; /* Message */ 

自定义类型说明

#define PING_MAX_NAME_SIZE_V01 255 //声明可变数组 name[]的最大数组项数 typedef struct { 
    uint32_t name_len; //用于描述可变数组 name[]的真实数组项数 char name[PING_MAX_NAME_SIZE_V01]; }ping_name_type_v01; /* Type */ 
2.3.3 Service Object

Client 通过下面的宏获取 service object,具体实现经由下面的函数声明,其中涉及到服务适用的 IDL 主版本号,次版本号,IDL 工具版本号

#define ping_get_service_object_v01( ) \ ping_get_service_object_internal_v01( \ PING_V01_IDL_MAJOR_VERS, PING_V01_IDL_MINOR_VERS, \ PING_V01_IDL_TOOL_VERS ) qmi_idl_service_object_type ping_get_service_object_internal_v01 ( int32_t idl_maj_version, int32_t idl_min_version, int32_t library_version ) { 
    ... return (qmi_idl_service_object_type)&ping_qmi_idl_service_object_v01; }; 
2.3.3.1 表对象
/*Service Object*/ const struct qmi_idl_service_object ping_qmi_idl_service_object_v01 = { 
    0x02, //library_version 对应 PING_V01_IDL_TOOL_VERS 0x01, //idl_version 对应 PING_V01_IDL_MAJOR_VERS 0x0F, //service ID,由 Qualcomm 定义,预留部分 ID 给 ODM 使用 8463, //最大消息长度 //request/response/indication { 
    sizeof(ping_service_command_messages_v01)/sizeof(qmi_idl_service_message_table_entry), sizeof(ping_service_response_messages_v01)/sizeof(qmi_idl_service_message_table_entry), sizeof(ping_service_indication_messages_v01)/sizeof(qmi_idl_service_message_table_entry) }, //request/response/indication 消息 id 与相应数据结构映射列表数组,相关的消息按顺序定义,可以按数组 索引到具体的消息 { 
    ping_service_command_messages_v01, ping_service_response_messages_v01, ping_service_indication_messages_v01}, // qmi_idl_type_table_object 类型表对象,用于索引相应的自定义或预定义类型 &ping_qmi_idl_type_table_object_v01 //类型表对象,类型表,消息表,引用表索引的总入口 }; 结构定义: struct qmi_idl_service_object { 
    uint32_t library_version; uint32_t idl_version; uint32_t service_id; uint32_t max_msg_len; uint16_t n_msgs[QMI_IDL_NUM_MSG_TYPES]; const qmi_idl_service_message_table_entry *msgid_to_msg[QMI_IDL_NUM_MSG_TYPES]; //消息索 引的入口 const qmi_idl_type_table_object *p_type_table; //消息列表中的索引入口 uint32_t idl_minor_version; struct qmi_idl_service_object *parent_service_obj; }; 

类型表对象:

ping_qmi_idl_type_table_object_v01 数据结构: struct qmi_idl_type_table_object { 
    uint16_t n_types; //类型个数 uint16_t n_messages; //消息个数 uint8_t n_referenced_tables; //引用表个数 const qmi_idl_type_table_entry *p_types; //类型表数组 const qmi_idl_message_table_entry *p_messages; //消息表数组 const struct qmi_idl_type_table_object **p_referenced_tables; //引用表二级数组 const qmi_idl_range_table_entry *p_ranges; }; 类型表对象定义: static const qmi_idl_type_table_object ping_qmi_idl_type_table_object_v01 = { 
    sizeof(ping_type_table_v01)/sizeof(qmi_idl_type_table_entry ), sizeof(ping_message_table_v01)/sizeof(qmi_idl_message_table_entry), 1, ping_type_table_v01, ping_message_table_v01, ping_qmi_idl_type_table_object_referenced_tables_v01 }; 

引用表定义:

static const qmi_idl_type_table_object *ping_qmi_idl_type_table_object_referenced_tables_v01[] = { 
   &ping_qmi_idl_type_table_object_v01, //该 service 类型表对象 &common_qmi_idl_type_table_object_v01}; //同类型表对象,主要用于索引默认的 response 类型; 

类型表定义:

static const qmi_idl_type_table_entry ping_type_table_v01[] = { 
    { 
   sizeof(ping_name_type_v01), ping_name_type_data_v01} //本 service 仅自定义一个类型,索引只有一 项; }; 

消息表定义:

static const qmi_idl_message_table_entry ping_message_table_v01[] = { 
    { 
   sizeof(ping_req_msg_v01), ping_req_msg_data_v01}, { 
   sizeof(ping_resp_msg_v01), ping_resp_msg_data_v01}, { 
   sizeof(ping_ind_msg_v01), ping_ind_msg_data_v01}, } 

消息 id 与消息表,消息最大长度组成消息表项数组,其成员为

typedef struct { 
    uint16_t qmi_message_id; //消息 ID uint16_t message_table_message_id; //该消息在哪个 message table 中的索引; uint16_t max_msg_len; //该消息 id 支持的最大消息长度 } qmi_idl_service_message_table_entry; static const qmi_idl_service_message_table_entry ping_service_command_messages_v01[] = { 
    { 
   QMI_PING_REQ_V01, TYPE16(0, 0), 266}, { 
   QMI_PING_DATA_REQ_V01, TYPE16(0, 3), 8456}, { 
   QMI_PING_DATA_IND_REG_REQ_V01, TYPE16(0, 5), 279}, { 
   QMI_PING_GET_SERVICE_NAME_REQ_V01, TYPE16(0, 8), 259}, { 
   QMI_PING_NULL_REQ_V01, TYPE16(0, 10), 0} }; 

需要说明的是经由类型表对象索引消息表,进而索引消息 id 的方法:

TYPE16(0, 0) #define TYPE16(table, type) QMI_IDL_TYPE16(table, type) #define QMI_IDL_TYPE16(table, type) (((table) << 12) | (type)) 

必选且固定长度成员编码规则:

Byte1: 成员类型 TLV 之 T,一般自 0x01 开始,顺序递增 Byte2: 成员标记声明(一般表示成员单位长度占用字节数),如果为数组,需添加 QMI_IDL_FLAGS_IS_ARRAY 标记 Byte3: 成员起始地址在数据结构中的偏移 OFFSET8,也可能 OFFSET16,需要占用 2Bytes Byte4: 数组项数,如果成员为数组,需要 Byte4 声明数组项数,如果为元类型,Byte4 不需要 

可选且固定长度成员编码规则:

Byte1: 可选成员标记,以及该成员 valid 字段相对该成员起始地址的偏移 Byte2: 成员类型 TLV 之 T,可选成员一般自 0x10 开始,顺序递增 Byte3: 成员标记声明(一般表示成员单位长度占用字节数)),如果为数组,需添加 QMI_IDL_FLAGS_IS_ARRAY 标记 Byte4: 成员起始地址在数据结构中的偏移 OFFSET8,也可能 OFFSET16,需要占用 2Bytes Byte5: 数组项数,如果成员为数组,需要 Byte5 声明数组项数,如果为元类型,Byte5 不需要 

可选且可变长度成员编码规则:

Byte1: 可选成员标记,以及该成员 valid 字段相对该成员起始地址的偏移 Byte2: 成员类型 TLV 之 T,可选成员一般自 0x10 开始,顺序递增 Byte3: 成员标记声明(一般表示成员单位长度占用字节数),可变成员 Byte3 需添加QMI_IDL_FLAGS_IS_VARIABLE_LEN 标记 Byte4: 成员起始地址在数据结构中的偏移 OFFSET8,也可能 OFFSET16,需要占用 2Bytes Byte5~6: 成员最大项数,如需两个字节表示,需在 Byte3 添加 QMI_IDL_FLAGS_OFFSET_IS_16 Byte7: 成员 len 字段(表述数组实际项数)相对与成员起始地址的偏移 

必选且固定成员,含自定义数据结构,不含数组的消息数据结构编码规则:

Byte1: 成员类型 TLV 之 T,一般自 0x01 开始,顺序递增 Byte2: 成员标记声明(一般表示成员单位长度占用字节数) Byte3: 成员起始地址在数据结构中的偏移 OFFSET8,也可能 OFFSET16,需要占用 2Bytes Byte4:成员类型在类型表数组 ping_type_table_v01[]中的索引; Byte5:成员类型在引用表中的表索引 ID; 

简单示例:

/*Message Definitions*/ static const uint8_t ping_req_msg_data_v01[] = { 
    0x01, //消息类型,出现在 TLV 的 type 字段 QMI_IDL_FLAGS_IS_ARRAY | QMI_IDL_GENERIC_1_BYTE, //该字段是一个数组,数组成员占用 1 个字节 QMI_IDL_OFFSET8(ping_req_msg_v01, ping), //该字段起始地址在该数据结构中的偏移位置 4, 该字段数组项数 //最后一个 TLV 结构 QMI_IDL_TLV_FLAGS_LAST_TLV | QMI_IDL_TLV_FLAGS_OPTIONAL | //该字段为可选成员 (QMI_IDL_OFFSET8(ping_req_msg_v01, client_name) - QMI_IDL_OFFSET8(ping_req_msg_v01, client_name_valid)), //该字段 valid 字段相对该字段起始位置的偏移,可选字段的 valid 是基于字段起始地址 进行计算的; 0x10, //消息类型,可选成员,一般以 0x10 开始定义消息类型,暂未看到明确定义 QMI_IDL_AGGREGATE, //声明该字段为自定义数据结构 QMI_IDL_OFFSET8(ping_req_msg_v01, client_name), //声明该字段起始地址在该数据结构的偏移位 置; 0, 0 //client_name 成员的类型 ping_name_type_v01 的索引,其位于引用表 0 的类型表数组的第 0 个类型 }; 

具体qmi添加示例:

1)添加massage ID,REQ, RESP, IND共用一个消息ID

#define QMI_TEST_REQ_V01 0x0001 #define QMI_TEST_RESP_V01 0x0001 #define QMI_TEST_IND_V01 0x0001 

2) 添加数据结构

typedef struct { 
    uint8_t test; }qmi_test_req_msg_v01; /* Message */ typedef struct { 
    qmi_response_type_v01 resp; }qmi_test_resp_msg_v01; /* Message */ 
static const uint8_t qmi_test_req_msg_data_v01[] = { 
    QMI_IDL_TLV_FLAGS_LAST_TLV | 0x01, QMI_IDL_GENERIC_1_BYTE, QMI_IDL_OFFSET8(qmi_test_req_msg_v01, test) }; static const uint8_t qmi_test_resp_msg_data_v01[] = { 
    QMI_IDL_TLV_FLAGS_LAST_TLV | 0x02, QMI_IDL_AGGREGATE, QMI_IDL_OFFSET8(qmi_test_resp_msg_v01, resp), QMI_IDL_TYPE88(1, 0) }; 

4)消息表添加

static const qmi_idl_message_table_entry qmi_test_message_table_v01[] = { 
    ... { 
   sizeof(qmi_test_req_msg_v01), qmi_test_req_msg_data_v01}, { 
   sizeof(qmi_test_resp_msg_v01), qmi_test_resp_msg_data_v01}, ... } 

5)消息表项数组添加(req 和 resp 分别添加对应的表):

static const qmi_idl_service_message_table_entry qmi_test_service_command_messages_v01[] = { 
    ... { 
   QMI_TEST_REQ_V01 , QMI_IDL_TYPE16(0, 66), 4}, ... } static const qmi_idl_service_message_table_entry qmi_test_service_response_messages_v01[] = { 
    ... { 
   QMI_TEST_RESP_V01 , QMI_IDL_TYPE16(0, 67), 11}, ... } 

(注意顺序)

6)添加处理函数

一般是在AP侧添加发送相关qmi消息的函数,然后modem添加处理该消息的函数

AP:

qmi_client_send_msg_sync() qmi_client_send_msg_sync(g_qmi_test->user_handle, QMI_TEST_REQ_V01 , req_item_ptr, sizeof(qmi_test_req_msg_v01), resp_item_ptr, sizeof(qmi_test_resp_msg_v01), COMM_LONG_PLATFORM_TIMEOUT); 

CP:

static qmi_csi_cb_error qmi_test ( void *conn_handle, qmi_req_handle req_handle, unsigned int msg_id, void *req_c_struct, unsigned int req_c_struct_len, void *service_cookie ); static qmi_csi_cb_error (* const qmi_test_req_handle_table[]) ( void *conn_handle, qmi_req_handle req_handle, unsigned int msg_id, void *req_c_struct, unsigned int req_c_struct_len, void *service_cookie ) = { 
    ..., &qmi_test, // 与消息ID对应 ..., } 

3 如何调试

QMI中的常见问题:

  1. AP调用qmi send函数,未能收到resp;
    通过QXDM查看modem是否有收到qmi消息,通过选择 Log Packets->Common->QMI,来筛选QMI消息;
  1. AP发送了数据,CP收到了但是从结构体中获取的值不对;
    首先确认发出去的值对不对

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/122534.html

(0)
上一篇 2025-10-15 22:33
下一篇 2024-12-12 12:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信