高通SDX62平台 MBIM搜网、查询信号等功能异常

2022-11-15 16:10:46 浏览数 (1)

高通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基本功能可正常使用。

0 人点赞