4 android QMI机制---QCRIL消息发送
原文:https://blog.****.net/u012439416/article/details/74276765
4 QCRIL消息发送
当ril有请求过来时,就会调用ril库的onRequest()方法,此时就会根据当前Qcril注册的函数列表
进入到qcril_request_api的onRequest_rid方法中,因此, onRequest_rid方法是QCRIL中的入口
方法。调用的流程如如下,
qcril_execute_event首先调用qcril_hash_table_lookup方法从表中查找当前的Event,如果没有
找到当前的Request,就认为非法,找到之后,进入qcril_dispatch_event()中派发该Event,
(entry_ptr->handler)(params_ptr, &ret);
ret是返回的结果,通过entry_ptr->handler调用当前Event的处理函数。这里的handler对应
qcril_hash_table中的某一项。第一章中将qcril_event_table表中的数据拷贝给了qcril_hash_table,
所以这里的handler可以理解为qcril_event_table中的某一项。
之后的流程就会进入到某个具体请求的处理函数中,比如打电话是对应的请求是RIL_REQUEST_DIAL,
其处理函数为:qcril_qmi_voice_request_dial;挂断电话对应的请求是RIL_REQUEST_HANGUP,
其处理函数为qcril_qmi_voice_request_hangup;
qcril_qmi_voice_request_hangup方法进一步调用qcril_qmi_client_send_msg_async发送到QMI层。
当然, qcril_qmi_client_send_msg_async是异步处理的, qcril_qmi_client_send_msg_sync是同步处理的。
最后,这2个方法都会调用qmi_client_send_msg_sync完成发送。
一些其他的处理方法或者会调用这两个方法发送到QMI层,或者直接调用qmi_client_send_msg_sync发送。
2.1 QMI层消息处理
调用流程图如下,
ril_err = qcril_qmi_client_send_msg_async ( QCRIL_QMI_CLIENT_VOICE,
QMI_VOICE_ANSWER_CALL_REQ_V02,
&ans_call_req_msg,
sizeof(ans_call_req_msg),
ans_call_resp_msg_ptr,
sizeof(*ans_call_resp_msg_ptr),
(void*)(uintptr_t)user_data);
这里选择的qmi_service是voice。之所以选择voice作为发送通道,是因为第二个参数QMI_VOICE_GET_CONFIG_REQ_V02.
qcril_qmi_client_send_msg_async方法主要逻辑如下,
if (NULL != client_info.qmi_svc_clients[svc_type])
{
qmi_error = qmi_client_send_msg_async_with_shm(client_info.qmi_svc_clients[svc_type],
•••
这个函数首先会去判断我们调用的这个voice的服务类型是否存在于QMI定义的服务列表中,如果不存在,
还需要自己添加该service,如果存在,执行QMI client端发送消息的接口函数qmi_client_send_msg_async_with_shm。
该方法直接调用qmi_client_send_msg_sync方法,
rc = qmi_client_send_msg_sync(user_handle,
msg_id,
req_c_struct,
req_c_struct_len,
resp_c_struct,
resp_c_struct_len,
timeout_msecs);
同步消息在QMUX层发送到BP侧后,会一直等待BP的响应所以函数的最后一个参数是一个timeout,
而异步消息不需要。
qmi_client_send_msg_sync方法首先计算请求消息的长度和设定返回消息的最大长度,然后对请求
消息按QMUX格式进行编码通过函数qmi_service_send_msg_sync_millisec()发送出去,最后
等待BP侧返回的响应,将返回的response进行解码。
2.2 QMUX层消息处理
QMUX消息处理流程图如下,
qmi_service_send_msg_sync_millisec方法首先去获取一些QMUX层所需要的消息,发送通道的conn_id,
QMUX消息格式用到的client_id
conn_id = QMI_SRVC_CLIENT_HANDLE_TO_CONN_ID (user_handle);
client_id = QMI_SRVC_CLIENT_HANDLE_TO_CLIENT_ID (user_handle);
然后打包到一个传输消息的结构体 qmi_service_txn_info_type *txn,这个txn在稍后跟进代码中会发现,
就是QMUX消息中的tranciationID
/* Initialize Id fields */
txn->conn_id = conn_id;
txn->service_id = service_id;
txn->client_id = client_id;
txn->msg_id = msg_id;
txn->api_flag = api_flag;
/* Initialize fields */
txn->srvc_txn_info.txn_type = QMI_TXN_SYNC;
txn->srvc_txn_info.sync_async.sync.user_reply_buf = NULL;
txn->srvc_txn_info.sync_async.sync.user_reply_buf_size = 0;
txn->srvc_txn_info.sync_async.sync.rsp_rc = QMI_NO_ERR;
txn->srvc_txn_info.sync_async.sync.qmi_err_code = QMI_SERVICE_ERR_NONE;
这些完成之后,调用qmi_service_send_msg方法发送,该方法会判断服务ID和通道的conn_id是否有效,
if ((int)conn_id >= (int)QMI_MAX_CONN_IDS)
{
return QMI_INTERNAL_ERR;
}
if ( qmi_qcci_internal_public_service_id_to_bookkeeping_service_id ( service_id ) >=
QMI_MAX_SERVICES)
{
return QMI_INTERNAL_ERR;
}
首先调用qmi_service_write_std_srvc_msg_hdr,方法给发送过来的消息加上message ID和Length,
if (qmi_service_write_std_srvc_msg_hdr (&msg_buf,
&msg_buf_size,
msg_id,
msg_buf_size) < 0)
所以不难猜测,在QMI发送端的接口函数qmi_client_send_msg_sync()中编解码的原理是按照QMUX
消息中的TLV格式进行编解码。再走到这步加上message ID和Length,整个QMUX SDU中的QMI service message
已完成。qmi_service_write_std_srvc_msg_hdr方法如下,
static
int qmi_service_write_std_srvc_msg_hdr (unsigned char **msg_buf,
int *msg_buf_size, unsigned long msg_id, int length)
{
unsigned char *tmp_msg_buf;
/* Back pointer up by 4 bytes */
*msg_buf -= QMI_SRVC_STD_MSG_HDR_SIZE;
*msg_buf_size += QMI_SRVC_STD_MSG_HDR_SIZE;
tmp_msg_buf = *msg_buf;
/* Write the message ID field (16 bits) */
WRITE_16_BIT_VAL (tmp_msg_buf,msg_id);
/* Write the length field */
WRITE_16_BIT_VAL (tmp_msg_buf,length);
return 0;
}
加message ID和Length的方法:消息指针的偏移。指针向前偏移,消息长度增加。随后用类似方法,
通过函数qmi_service_write_std_txn_hdr_and_inc_txn_id()将control flags和tranciation ID加上,
完成整个QMUX SDU。最后通过函数qmi_qmux_if_send_qmi_msg()发送到QMUX层,到这里,
整个QMI interface的流程走完,主要作用:获取上层发送的请求,选择相应的serviceid,conn_id,client id,
对消息完成整个QMUX SDU的封装,发送到QMUX。
函数qmi_qmux_if_send_qmi_msg()的工作是,对上层发过来打包好的QMUX SDU完成加
QMUX HEADER的工作。添加QMUX header的方法同样是指针偏移。
qmi_qmux_if_send_qmi_msg方法直接调用qmi_qmux_if_send_to_qmux方法进行处理,
首先将QMUX header里面的控制位,QMI服务ID,QMI客户端ID打包到结构体hdr,再通过Memcpy完成,
/* Set up message for sending */
memset(&hdr, 0, sizeof(qmi_qmux_if_msg_hdr_type));
hdr.msg_id = msg_id;
hdr.qmux_client_id = qmux_client_id;
hdr.qmux_txn_id = qmux_txn_id;
hdr.qmi_conn_id = qmi_conn_id;
hdr.qmi_service_id = qmi_service_id;
hdr.qmi_client_id = qmi_client_id;
hdr.control_flags = 0; // Unused for TX to QMUX, only valid for RX
/* Decrement msg pointer and increment msg_len */
msg -= QMI_QMUX_IF_HDR_SIZE;
msg_len += (int)QMI_QMUX_IF_HDR_SIZE;
/* Copy header into message buffer */
memcpy ((void *)msg, (void *)&hdr, QMI_QMUX_IF_HDR_SIZE);
到这里整个QMUXMessage完成封装。然后会把封装好的QMUXmessage发到下层,通过调用宏
QMI_QMUX_IF_PLATFORM_TX_MSG()。这个宏定义了函数linux_qmi_qmux_if_client_tx_msg()。
qmi_platform_qmux_if.h中宏QMI_QMUX_IF_PLATFORM_TX_MSG定义如下,
#define QMI_QMUX_IF_PLATFORM_TX_MSG(client,msg,msg_len) \
linux_qmi_qmux_if_client_tx_msg (client,msg,msg_len)
因此,调用宏QMI_QMUX_IF_PLATFORM_TX_MSG就是调用linux_qmi_qmux_if_client_tx_msg方法,
该方法通过socket,将消息发到linux_qmi_qmux_if_server的接口。
if ((rc = send (client_fd,
(void *) msg,
(size_t) msg_len,
MSG_DONTWAIT | MSG_NOSIGNAL)) < 0)