高通SDX62平台 MBIM搜网、查询信号等功能异常
1. 问题描述
按照高通SDX62平台产品规格,其支持RMNET、ECM、RNDIS、PPP、MBIM等拨号;但经测试,发现MBIM拨号功能正常,但搜网、查询信号等功能均异常,返回“error:operation failed:Failure”或返回信息为空
2. 分析与调试
2.1 前期准备
在Linux上进行mbim业务,需要安装MBIM依赖环境:
代码语言:javascript复制安装mbim库:apt install libmbim-utils
Mbimcli库版本与ubuntu版本对应关系如下:
Ubuntu 16.04…………mbimcli 1.14
Ubuntu 18.04…………mbimcli 1.18
Ubuntu 20.04…………mbimcli 1.22
版本越高,功能越全,安装完成后,可以使用mbimcli –help 命令查看当前版本支持的mbim命令。
2.2 初步分析
由于mbim查询信号命令在高版本libmbim库中才支持,因此在本地ubuntu20.04复现问题,并抓取log。初步分析模块侧qbi log,发现mbim的扫网命令已成功发送到模块内qbi,qbi已将对应请求通过qmi消息转发给modem进行处理,而modem返回错误码94 (QMI_ERR_NOT_SUPPORTED),log如下:
代码语言:javascript复制1980-01-06 00:01:37:168606::framework/src/qbi_qmi_txn.c::qbi_qmi_txn_alloc::0177:::Allocated new QMI transaction for svc_id 1 msg_id 0x0021 req size 232 rsp size 18040 parent txn iid 49
1980-01-06 00:01:37:168684::framework/src/qbi_txn.c::qbi_txn_set_timeout::1484:::Setting timeout in 49 ms for txn iid 600000
1980-01-06 00:01:37:168751::framework/src/qbi_svc.c::qbi_svc_proc_action::3012:::Processing action 1 for txn iid 49
1980-01-06 00:01:37:168814::framework/src/qbi_qmi.c::qbi_qmi_dispatch::2793:::Dispatching QMI requests for QBI transaction iid 49
1980-01-06 00:01:37:169066::framework/src/qbi_qmi.c::qbi_qmi_dispatch::2840:::Sent QMI request for svc_id 1 msg_id 0x0021 with txn iid 49
1980-01-06 00:01:37:169158::framework/src/qbi_txn.c::qbi_txn_check_timeout::0627:::Next transaction times out in 0 ms (ctx id 600000)
1980-01-06 00:01:37:169282::platform/src/qbi_os_linux.c::qbi_os_timer_wake_lock::1325:::Set wake lock
1980-01-06 00:01:37:171003::framework/src/qbi_task.c::qbi_task_cmd_proc_all::0261:::Processing command ID 1
1980-01-06 00:01:37:171146::framework/src/qbi_qmi.c::qbi_qmi_proc_rsp_cb::2027:::Processing QMI response for svc_id 1 msg_id 0x0021 with txn iid 49
1980-01-06 00:01:37:171238::svc/src/qbi_svc_bc_nas.c::qbi_svc_bc_nas_visible_providers_q_nas21_rsp_cb::2511::Error:Received error code 94 from QMI
涉及到qmi返回信息错误,需进一步去排查问题根因。
2.3 高通mbim架构
高通QTI支持MBIM,主要是通过改变现有的软件层——增加一个新的层,QBI,来处理MBIM控制通道协议 。其基本架构如上图: 控制通道:USB/MBIM驱动<=> QBI <=> QMI/Modem 数据通道:USB <=> MBIM-NTB<=> RmNet/Modem 1、QBI是一个新的软件层,包含了特定于处理命令(cid)的逻辑,这些命令在MBIM中定义,通过控制通道作为封装的消息发送。 QBI运行在与USB驱动程序相同的处理器上,并通过QMI与调制解调器进行接口。 它转换用于与主机通信的MBIM cid和用于控制调制解调器的QMI消息。
2、公共消息传递模块是第一个与主机传入的CID请求接触的框架模块。 它解析和验证传入命令的公共部分,将其封送为更易于访问的内部格式,并将请求传递给公共设备服务层以进行分派。 在输出方向上,这一层在内部格式和CID线格式之间转换。 3、CID交互是QBI的重要组成部分。 在接收到新的CID请求后,公共消息传递层在继续发送请求之前调用事务分配例程。 4、公共设备服务层是设备服务实现和QBI框架其余部分之间的主要接口。 设备服务在启动时向公共层注册一次。
通过高通mbim架构架构我们可以看到高通整个mbim消息会在qbi层转换成qmi发送给modem,因此我们进一步梳理消息处理流程,流程总体可分为两部分——mbim消息在qbi侧处理、qmi消息在modem侧处理。
2.4 MBIM消息在qbi侧处理流程简析
当host发送mbim命令如搜网请求mbimcli -p -d /dev/cdc-wdm0 --query-visible-providers,libmbim库会将该命令转换成符合mbim协议的mbim命令MBIM_CID_VISIBLE_PROVIDERS,并将该命令通过模块的mbim口发送给模块,当模块收到该命令后会查找hdlr表,查看对应的处理函数:
通常mbim请求包括两种——查询类、设置类,因此在该hdlr表中会存储查询类型处理函数、设置类型处理函数:
代码语言:javascript复制/*! @brief CID-specific handlers for each command type */ typedef struct {
qbi_svc_cmd_hdlr_f *query_fcn; /*!< Handler for QBI_MSG_CMD_TYPE_QUERY */
/*! Minimum size of the InformationBuffer in a valid query request. The framework will reject requests that do not meet this minimum. */
uint32 query_infobuf_min_size;
qbi_svc_cmd_hdlr_f *set_fcn; /*!< Handler for QBI_MSG_CMD_TYPE_SET */
/*! Minimum size of the InformationBuffer in a valid set request. The framework will reject requests that do not meet this minimum. */
uint32 set_infobuf_min_size;
} qbi_svc_cmd_hdlr_tbl_entry_s;
由于扫网请求属于查询命令,不存在设置情况,因此我们可以在SDX62代码中看到MBIM_CID_VISIBLE_PROVIDERS对应的处理函数仅有查询类型一种——qbi_svc_bc_ nas_visible_providers_q_req,而网络注册既可以查询注网情况,也可设置手动注网,因此MBIM_CID_REGISTER_STATE对应的处理函数包括查询和设置两种——qbi_svc_bc _nas_register_state_q_req和qbi_svc_bc_nas_register_state_s_req:
进一步分析扫网请求的处理函数qbi_svc_bc_nas_visible_providers_q_req,在这个函数中首先会判断radio状态的合法性、sim卡状态,只有sim卡状态ready,radio在on的时候才能正常网络搜索和注册;若上述条件符合,进一步确认搜网请求是全扫还是快扫,全扫搜索所有频段,并不断输出结果,快扫是扫描存储信息:
代码语言:javascript复制qbi_svc_action_e qbi_svc_bc_nas_visible_providers_q_req(qbi_txn_s *txn)
{
…
req = (qbi_svc_bc_visible_providers_q_req_s *) txn->req.data;
if (FALSE == qbi_svc_bc_radio_state_is_radio_on(txn->ctx))//确认radio状态
{
QBI_LOG_E_0("Network scan can't be performed while radio is off");
txn->status = QBI_MBIM_STATUS_RADIO_POWER_OFF;
}
else if (QBI_MBIM_STATUS_SIM_NOT_INSERTED == qbi_svc_bc_sim_subscriber_ready_state_to_mbim_status(txn->ctx))//确认sim卡ready
{
QBI_LOG_E_0("SIM card not inserted");
txn->status = QBI_MBIM_STATUS_SIM_NOT_INSERTED;
}
else if (req->action == QBI_SVC_BC_VISIBLE_PROVIDERS_FULL_SCAN)//全扫
{
if (qbi_svc_bc_nas_visible_providers_q_need_mode_pref_workaround(txn->ctx))
{
/* Need to temporarily set the mode preference to 3GPP only, then perform the network scan, then restore the mode preference. This is a workaround tied to WHCK. */
action = qbi_svc_bc_nas_visible_providers_q_set_mode_pref(txn,qbi_svc_bc_nas_visible_providers_q_set_mode_pref_nas33_rsp_cb,FALSE, 0);
}
else
{
/* Devices with 3GPP support perform a manual network scan. */
action = qbi_svc_bc_nas_visible_providers_q_build_nas21_req(txn);
}
}
else if (req->action == QBI_SVC_BC_VISIBLE_PROVIDERS_QUICK_SCAN)//快速扫网
{
QBI_LOG_E_0("Received quick scan request; not supported!");
txn->status = QBI_MBIM_STATUS_NO_DEVICE_SUPPORT;
}
else
{
QBI_LOG_E_1("Invalid action %d", req->action);
txn->status = QBI_MBIM_STATUS_INVALID_PARAMETERS;
}
return action;
} /* qbi_svc_bc_nas_visible_providers_q_req() */
请求为全扫,因此会调用qbi_svc_bc_nas_visible_providers_q_build_nas 21_req函数进行处理,在该函数中会将我们的MBIM请求转换成qmi信息,发送给mode m,并注册回调函数qbi_svc_bc_nas_visible_providers_q_nas21_rsp_cb以异步的方式去处理modem的返回结果:
0x0021对应于扫网请求,这也和qbi log中msg_id 0x0021 req对应起来:
代码语言:javascript复制1980-01-06 00:01:37:168606::framework/src/qbi_qmi_txn.c::qbi_qmi_txn_alloc::0177:::Allocated new QMI transaction for svc_id 1 msg_id 0x0021 req size 232 rsp size 18040 parent txn iid 49
在回调函数处理中会按照qmi返回的信息去填充各字段,首先去判断返回是否成功,如果qmi返回成功的情况进一步判断搜到的网络个数,并且去查询搜到网络的plmn信息:
代码语言:javascript复制static qbi_svc_action_e qbi_svc_bc_nas_visible_providers_q_nas21_rsp_cb(qbi_qmi_txn_s *qmi_txn)
{
…
qmi_rsp = (nas_perform_network_scan_resp_msg_v01 *) qmi_txn->rsp.data;
if (qmi_rsp->resp.result != QMI_RESULT_SUCCESS_V01)//返回失败
{
/*! @note If the network scan is aborted due to internal modem behavior, e.g. CM_PH_EVENT_TER MINATE_GET_NETWORKS due to LTE registration activities, QMI NAS will return QMI_ERR_INTERN AL. Ideally, QMI NAS wouldreturn a more unique error code that we could map to MBIM_STATUS_B USY, but currently we need to maintain this mapping to prevent failures in WHCK's DriverStress tool when using LTE. */
if (qmi_rsp->resp.error == QMI_ERR_DEVICE_IN_USE_V01 || qmi_rsp->resp.error == QMI_ERR_ INTERNAL_V01)//内部错误
{
qmi_txn->parent->status = QBI_MBIM_STATUS_BUSY;
}
else//返回失败,并打印错误码
{
QBI_LOG_E_1("Received error code %d from QMI", qmi_rsp->resp.error);
qmi_txn->parent->status = QBI_MBIM_STATUS_FAILURE;
}
}
else if (qmi_rsp->scan_result_valid &&qmi_rsp->scan_result != NAS_SCAN_SUCCESS_V01)
{
if (!qbi_svc_bc_check_device_state(qmi_txn->parent, TRUE, FALSE))
…
else
{
qmi_txn->parent->status = QBI_MBIM_STATUS_BUSY;
}
}
else//返回成功
{
if (!qmi_rsp->nas_3gpp_network_info_valid)
{
qmi_rsp->nas_3gpp_network_info_len = 0;
}//搜到的网络个数
if (qmi_rsp->nas_3gpp_network_info_len == 0)
{
qmi_txn->parent->status = QBI_MBIM_STATUS_PROVIDERS_NOT_FOUND;
action = QBI_SVC_ACTION_ABORT;
}
else if (!qmi_rsp->mnc_includes_pcs_digit_valid ||qmi_rsp->mnc_includes_pcs_digit_len != qmi_rsp->nas_3gpp_network_info_len)
…
else if (!qmi_rsp->nas_network_radio_access_technology_valid ||qmi_rsp->nas_3gpp_network_info _len != qmi_rsp->nas_network_radio_access_technology_len)
…
else if (!qbi_svc_bc_nas_visible_providers_q_prepare_info(qmi_txn->parent, qmi_rsp->nas_3gpp_ network_info, qmi_rsp->nas_3gpp_network_info_len, qmi_rsp->mnc_includes_pcs_digit, qmi_rsp->nas_network_radio_access_technology, (qmi_txn->parent->svc_id == QBI_SVC_ID_BC) ? TRUE : FALSE))
..//plmn
else
{
nas44_txn = qbi_qmi_txn_alloc(qmi_txn->parent, QBI_QMI_SVC_NAS, QMI_NAS_GET_PLMN_ NAME_REQ_MSG_V01, (qmi_txn->parent->svc_id == QBI_SVC_ID_BC) ? qbi_svc_bc_nas_visible_p roviders_q_nas44_rsp_cb : qbi_svc_atds_operators_q_nas44_rsp_cb);
action = qbi_svc_bc_nas_visible_providers_q_get_next_plmn_name(qmi_txn->parent, nas44_txn);
}
}
return action;
} /* qbi_svc_bc_nas_visible_providers_q_nas21_rsp_cb() */
再次回到我们的log,在qbi log中我们收到了qmi返回的错误码94,在qmi手册《80_ NV701_2_A_QMI_COMMON_1_11_FOR_MPSS_HE_1_0__QMI_》中查询到94对应的是QMI_ERR_NOT_SUPPORTED:
2.5 QMITestPro复测
通过上面的qbi流程简析,我们可以看到是qmi给我们的扫网请求直接返回了错误码94(QMI_ERR_NOT_SUPPORTED),为了排除qbi中发送消息错误,查看qbi代码在转发qmi时没有携带任何参数,进一步查阅qmi手册《80_NV701_6_A_QMI_NAS_1_2 10_FOR_MPSS_HE_1_0__QMI_NE》,确认扫网qmi消息QMI_NAS_PERFORM_NETW ORK_SCAN没有强制参数,只有些可选参数,也就是说不需要携带参数,modem侧也是可以正常处理返回的:
为了确认我们的分析,以及排除qbi中消息发送时可能的错误,特别在windows下使用高通qmi测试工具qmitestpro进行qmi消息的发送测试,结果SDX62平台同样的返回了错误码94(QMI_ERR_NOT_SUPPORTED):
对比SDX55平台,这几条qmi返回是ok的:
2.6 Modem代码qmi nas相关消息处理流程分析
Qmi nas服务初始化中,会注册命令处理函数,包括正常的请求、初始化、分配内存、销毁内存、定时器等请求的处理函数:
代码语言:javascript复制void qmi_nas_init( void )
{
…
/* Set the cmd handlers in QMI MMODE task */
qmi_mmode_set_cmd_handler( QMI_MMODE_CMD_NAS_ALLOC_CLID, qmi_nas_process_alloc_clid );
qmi_mmode_set_cmd_handler( QMI_MMODE_CMD_NAS_DEALLOC_CLID, qmi_nas_process_dealloc_clid );
qmi_mmode_set_cmd_handler( QMI_MMODE_CMD_NAS_INIT_CBACK, qmi_nas_process_init_cback );
qmi_mmode_set_cmd_handler( QMI_MMODE_CMD_NAS_CMD_HDLR, qmi_nas_process_cmd_hdlr );
…
qmi_mmode_set_cmd_handler( QMI_MMODE_CMD_NAS_TIMER_EVT_CB, qmi_nas_process_timer_evt );
memset( &qmi_nas_state, 0x00, sizeof(qmi_nasi_state_type) );
memset( &qmi_nasi_cfg, 0x00, sizeof(qmi_nasi_cfg) );
…
qmi_nas2_init(); //Register service with QMUX.
qmi_nasi_cfg.fw_cfg.base_version.major = NASI_BASE_VER_MAJOR;
qmi_nasi_cfg.fw_cfg.base_version.minor = NASI_BASE_VER_MINOR;
qmi_nasi_cfg.fw_cfg.addendum_version.major = NASI_ADDENDUM_VER_MAJOR;
qmi_nasi_cfg.fw_cfg.addendum_version.minor = NASI_ADDENDUM_VER_MINOR;
qmi_nasi_cfg.fw_cfg.cbs.alloc_clid = qmi_nas_fw_alloc_clid_cb;
qmi_nasi_cfg.fw_cfg.cbs.dealloc_clid = qmi_nas_fw_dealloc_clid_cb;
qmi_nasi_cfg.fw_cfg.cbs.init_cback = qmi_nas_fw_init_cb;
qmi_nasi_cfg.fw_cfg.cbs.cmd_hdlr = qmi_nas_fw_cmd_hdlr_cb;
qmi_nasi_cfg.cmd_hdlr_array = qmi_nasi_cmd_callbacks;
qmi_nasi_cfg.cmd_num_entries = NASI_CMD_MAX;
qmi_nasi_cfg.service_id = QMUX_SERVICE_NAS;
errval = qmi_framework_reg_service( QMUX_SERVICE_NAS, &qmi_nasi_cfg.fw_cfg );
svc_obj = nas_get_service_object_v01();
(void) qmi_si_register_object ( svc_obj, 0, nas_get_service_impl_v01() );
if ( errval != QMI_FRAMEWORK_ERR_NONE )
{
QM_MSG_ERROR_1("qmi_nas_init() qmi_framework_reg_service failed %d", errval);
return;
}
} /* qmi_nas_init() */
nas服务初始化流程见上,我们比较关注的是cmd请求处理函数的注册,当cmd请求处理函数注册成功后,后续收到nas想关qmi,就可以在该函数中进行处理,我们进一步看qmi_nas_process_cmd_hdlr函数的处理流程。首先qmi_nas_process_cmd_hdlr函数进行了合法性的校验,然后调用了qmi_mmode_svc_req_hdlr函数:
qmi_nas_process_cmd_hdlr函数中会先通过我们传入的qmi_nasi_cfg去获取transcation id信息,以便进一步将qmi请求发送给对应的模块去处理:
代码语言:javascript复制void qmi_mmode_svc_req_hdlr(qmi_mmode_svc_config_type *svc_cfg, qmi_framework_msg _hdr_type *msg_hdr, qmi_common_client_state_type *cl_sp, dsm_item_type *sdu_in)
{
...
x_p = qmi_mmode_svci_get_transaction(svc_cfg->svc_sp, cl_sp);//获取trascation id
…
qmi_mmode_svci_dispatch_transaction(&msg_hdr->common_hdr,svc_cfg,x_p);//发送qmi命令给对应的模块
…
}
qmi_mmode_svci_dispatch_transaction函数中调用qmi_mmode_svci_input发送qmi消息给对应的模块:
代码语言:javascript复制static void qmi_mmode_svci_dispatch_transaction(qmi_framework_common_msg_hdr_type *cmn_ msg_hdr, qmi_mmode_svc_config_type *svc_cfg, qmi_transaction_type *x_p)
{
…
/*-------------------------------------------------------------------------
Dispatch each of the commands in the transaction
-------------------------------------------------------------------------*/
for( i = 0; i < x_p->n_cmds; i )
{
cmd_buf_p = x_p->cmd_list[i];
msg = x_p->req_list[i];
x_p->req_list[i] = NULL;
/*-----------------------------------------------------------------------
Dispatch the current SDU/msg transaction
-----------------------------------------------------------------------*/
if( FALSE == qmi_mmode_svci_input(cmn_msg_hdr, svc_cfg, cmd_buf_p, &msg ) )
{
…
QM_MSG_MED_3("Transaction %x Command #%d (%x) processing failed", x_p, i, cmd_buf_p);
free_t = TRUE;
// we should ensure the transaction doesn't stall here.
}
/*-----------------------------------------------------------------------
Ensure the input SDU is freed. Command handlers don't need to worry about freeing the input message.
-----------------------------------------------------------------------*/
dsm_free_packet ( &msg );
}
if(TRUE == free_t)
{
// this indicates that qmi_mmode_svci_input() failed for one of the bundled
// commands in the transaction. This failure could be either problem
// with assembling response or invalid command type was sent to the
// service. In either case right thing to do is to cleanup transaction
// at this point, otherwise it will never be cleaned up.
qmi_mmode_svci_free_transaction( &x_p );
}
} /* qmi_mmode_svci_dispatch_transaction() */
qmi_mmode_svci_input中查询命令处理函数列表,按照接收到的请求找到对应的处理函数,然后调用该回调函数,对收到的请求进行处理,并最后将结果返回给qmi客户端:
代码语言:javascript复制static boolean qmi_mmode_svci_input(qmi_framework_common_msg_hdr_type *cmn_msg_hdr, q mi_mmode_svc_config_type *svc_cfg, qmi_cmd_buf_type *cmd_buf_p, dsm_item_type **sdu_in) {
...
cl_sp = cmd_buf_p->x_p->cl_sp;
/*-------------------------------------------------------------------------
Checking to see if a cmd hndler is registerd for the input command
-------------------------------------------------------------------------*/
cmd_hdlr = svc_cfg->cmd_hdlr_array;
for (cmd = 0; cmd < svc_cfg->cmd_num_entries; cmd , cmd_hdlr )
{
if (cmd_buf_p->cmd_type == cmd_hdlr->cmd_type)//匹配命令类型
{
break;
}
}
if( cmd == svc_cfg->cmd_num_entries || cmd_hdlr->request_hdlr == NULL )//无效请求
{
...
retval = qmi_svc_put_result_tlv( &response_ptr, QMI_RESULT_FAILURE, QMI_ERR_INVALID_ QMI_CMD );
if (FALSE == retval)
{
dsm_free_packet(&response_ptr);
response_ptr = NULL;
}
}
else
{
cmd_buf_p->in_progress = TRUE;
if((cmd_buf_p->x_p->ctl & QMI_FLAG_MASK_MSGTYPE) == QMI_FLAG_MSGTYPE_CMD)
{
response_ptr = cmd_hdlr->request_hdlr( svc_cfg->svc_sp, cmd_buf_p, cl_sp, sdu_in );//调用请求对应的回调函数
}
else
{
response_ptr = NULL;
}
}
cmd_buf_p->in_progress = FALSE;
/*-------------------------------------------------------------------------
send response if ready. Check for need to queue this command in pending queue.
-------------------------------------------------------------------------*/
if (response_ptr == NULL)
{
qmi_mmode_svc_free_transaction_cmd_buf(&cmd_buf_p);
return FALSE;
}
else if (response_ptr == QMI_SVC_RESPONSE_PENDING)
{
// command buffer will be freed later when response is completed
return TRUE;
}
return qmi_mmode_svc_send_response(cmn_msg_hdr, cmd_buf_p, response_ptr );//将回调函数处理结果返回给qmi客户端
} /* qmi_mmode_svci_input() */
上述函数中说到的请求对应的回调函数,在nas服务初始化时已经定义在回调函数列表中,并注册在了qmi_nasi_cfg中,该结构体包括了nas服务版本号、初始化的回调函数、cmd处理的回调函数、内存分配回调函数、内存销毁回调函数、命令处理回调函数、命令个数以及nas服务id:
代码语言:javascript复制typedef struct
{
qmi_framework_svc_config_type fw_cfg;
qmi_svc_cmd_hdlr_type *cmd_hdlr_array;
void *svc_sp;
uint16 cmd_num_entries;
int16 service_id;
} qmi_mmode_svc_config_type;
qmi_nasi_cfg中的命令处理回调函数列表包括了nas服务相关的一些qmi命令请求和处理函数,如我们问题涉及到的搜网请求0x0021,在此处对应的是NASI_CMD_VAL_PERFO RM_NETWORK_SCAN,其对应的处理函数是qmi_nasi_perform_network_scan:
进一步分析qmi_nasi_perform_network_scan函数,其首先会判断请求的合法性,并解析搜网返回的结果,再次检查返回的结果的合法性,最后返回处理的结果:
代码语言:javascript复制static dsm_item_type *qmi_nasi_perform_network_scan(void *sp, void *cmd_buf_p,void *cl_sp, dsm_item_type **sdu_in)
{
...//变量定义和初始化
req_msg = (nas_perform_network_scan_req_msg_v01 *) QM_MEM_ALLOC(sizeof(nas_perform_network_scan_req_msg_v01));
rsp_msg = (nas_perform_network_scan_resp_msg_v01 *) QM_MEM_ALLOC(sizeof(nas_perform_network_scan_resp_msg_v01));
#ifdef FEATURE_DUAL_SIM//双卡
if ( ((qmi_nasi_client_state_type *)cl_sp)->report_status.bound_subs == QMI_NAS_SUBS_SECONDARY )
{
asubs_id = SYS_MODEM_AS_ID_2;
}
#endif
#ifdef FEATURE_TRIPLE_SIM//三卡
else if ( ((qmi_nasi_client_state_type *)cl_sp)->report_status.bound_subs == QMI_NAS_SUBS_TERTIARY )
{
asubs_id = SYS_MODEM_AS_ID_3;
}
#endif
…
if(errval == QMI_ERR_NONE_V01 && !qmi_mmode_api_control_status())
{
errval= QMI_ERR_NOT_SUPPORTED_V01;
}
if ( ! TARGET_SUPPORTS_GSM(asubs_id) && ! TARGET_SUPPORTS_WCDMA(asubs_id) && ! TARGET_SUPPORTS_LTE(asubs_id) && ! TARGET_SUPPORTS_TDS(asubs_id) )//模块制式判断
{
errval = QMI_ERR_OP_DEVICE_UNSUPPORTED_V01;
}
if ( errval == QMI_ERR_NONE_V01 )
{
errval = qmi_mmode_idl_message_decode( qmi_nasi_global.svc_obj, (uint16_t) ((qmi_cmd_buf _type *) cmd_buf_p)->cmd_type, sdu_in, (void *) req_msg, (uint32_t) sizeof(nas_perform_network_scan _req_msg_v01));//解析请求填充新结构体
}
…//解析响应结果
qmi_mmode_idl_message_encode( qmi_nasi_global.svc_obj, QMI_IDL_RESPONSE, (uint16_t) ( (qmi_cmd_buf_type *) cmd_buf_p )->cmd_type, (void *) rsp_msg, (uint32_t) sizeof(nas_perform_netw ork_scan_resp_msg_v01), &response);
…
return response;
} /* qmi_nasi_perform_network_scan() */
2.7 qmi_mmode_api _control_status准入条件
在2.6中qmi_mmode_api _control_status函数中发现,判断变量是否申请内存成功时会使用函数qmi_mmode_api _control_status作为准入条件,若不符合,将会设置错误码为QMI_ERR_NOT_SUPPORT ED_V01(94),这也是在modem侧处理qmi请求时唯一一处返回该错误码的地方:
因此怀疑我们在qbi侧获取到qmi返回的错误码94正是在这个地方返回的,加log进一步确认了我们的怀疑点,modem在处理扫网qmi请求时确实时进入到了这个异常分支中;并且通过搜索,我们发现调用该函数的qmi有多达九条:
测试这些qmi确实均存在问题:
进一步确认这个函数的作用:
由于该函数高通没有给我们开源,仅提供了头文件,查看该函数的申明,发现该函数适用于cpe产品,但没有更多的信息去确认该函数具体作用。
2.8 基线说明
为了进一步确认qmi_mmode_api _control_status函数作用,我们通过查看日志,发现该函数为基线新增,高通网站确认该修改点的合入原因,查看releasenotes 和CR list,写的比较粗,没有说明该修改点高通的修改原因。
2.9 和SDX55对比
由于该问题在SDX55中不存在,因此我们对比SDX55和SDX62该qmi消息的处理函数,发现qmi_mmode_api _control_status函数确实是两者之间的唯一差异点,这进一步证明了我们的分析的正确性:
由于无法进一步分析该函数作用,因此暂时通过宏来控制屏蔽该函数影响,问题得以解决。
3 结果
经过上述修改,编译版本进行验证,发现上面所述存在问题的qmi消息均可成功返回结果,进一步测试MBIM查询信号、搜网等命令,可正常返回:
MBIM搜网、查询信号失败等问题均已解决,并且解决了其他相关qmi返回错误问题,SDX62 MBIM基本功能可正常使用。