Loading drivers/platform/msm/ipa/ipa_v3/ipa_i.h +4 −0 Original line number Diff line number Diff line Loading @@ -2501,6 +2501,8 @@ int ipa3_teth_bridge_disconnect(enum ipa_client_type client); int ipa3_teth_bridge_connect(struct teth_bridge_connect_params *connect_params); int ipa3_teth_bridge_get_pm_hdl(void); /* * Tethering client info */ Loading Loading @@ -2980,4 +2982,6 @@ static inline int ipa_mpm_panic_handler(char *buf, int size) #endif /* CONFIG_IPA3_MHI_PRIME_MANAGER */ /* query ipa APQ mode*/ bool ipa3_is_apq(void); #endif /* _IPA3_I_H_ */ drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +353 −17 Original line number Diff line number Diff line Loading @@ -57,6 +57,40 @@ struct ipa_msg_desc { struct qmi_elem_info *ei_array; }; static struct ipa_mhi_prime_aggr_info_req_msg_v01 aggr_req = { .aggr_info_valid = 1, .aggr_info_len = 5, .aggr_info[0] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_DPL_PROD_V01, .bytes_count = 16, }, .aggr_info[1] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_CONS_V01, .bytes_count = 24, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, .aggr_info[2] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_PROD_V01, .bytes_count = 16, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, .aggr_info[3] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_RMNET_CONS_V01, .bytes_count = 31, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, .aggr_info[4] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_RMNET_PROD_V01, .bytes_count = 31, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, }; /* QMI A5 service */ static void ipa3_handle_indication_req(struct qmi_handle *qmi_handle, Loading Loading @@ -798,6 +832,244 @@ int ipa3_qmi_filter_request_ex_send( resp.resp.error, "ipa_install_filter"); } /* sending add offload-connection-request to modem*/ int ipa3_qmi_add_offload_request_send( struct ipa_add_offload_connection_req_msg_v01 *req) { struct ipa_add_offload_connection_resp_msg_v01 resp; struct ipa_msg_desc req_desc, resp_desc; int rc = 0; int i, j; uint32_t id; /* check if modem up */ if (!ipa3_qmi_modem_init_fin || !ipa_q6_clnt) { IPAWANDBG("modem QMI haven't up yet\n"); return -EINVAL; } /* check if the filter rules from IPACM is valid */ if (req->filter_spec_ex2_list_len == 0) { IPAWANDBG("IPACM pass zero rules to Q6\n"); } else { IPAWANDBG("IPACM pass %u rules to Q6\n", req->filter_spec_ex2_list_len); } /* currently set total max to 64 */ if (req->filter_spec_ex2_list_len + ipa3_qmi_ctx->num_ipa_offload_connection >= QMI_IPA_MAX_FILTERS_V01) { IPAWANDBG( "cur(%d), req(%d), exceed limit (%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection, req->filter_spec_ex2_list_len, QMI_IPA_MAX_FILTERS_V01); return -EINVAL; } for (i = 0; i < req->filter_spec_ex2_list_len; i++) { if ((req->filter_spec_ex2_list[i].ip_type != QMI_IPA_IP_TYPE_V4_V01) && (req->filter_spec_ex2_list[i].ip_type != QMI_IPA_IP_TYPE_V6_V01)) return -EINVAL; if (req->filter_spec_ex2_list[i].is_mux_id_valid == false) return -EINVAL; if ((req->filter_spec_ex2_list[i].filter_action <= QMI_IPA_FILTER_ACTION_INVALID_V01) || (req->filter_spec_ex2_list[i].filter_action > QMI_IPA_FILTER_ACTION_EXCEPTION_V01)) return -EINVAL; } req_desc.max_msg_len = IPA_ADD_OFFLOAD_CONNECTION_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01; req_desc.ei_array = ipa_add_offload_connection_req_msg_v01_ei; memset(&resp, 0, sizeof(struct ipa_add_offload_connection_resp_msg_v01)); resp_desc.max_msg_len = IPA_ADD_OFFLOAD_CONNECTION_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_IPA_ADD_OFFLOAD_CONNECTION_RESP_V01; resp_desc.ei_array = ipa_add_offload_connection_resp_msg_v01_ei; rc = ipa3_qmi_send_req_wait(ipa_q6_clnt, &req_desc, req, &resp_desc, &resp, QMI_SEND_REQ_TIMEOUT_MS); if (rc < 0) { IPAWANERR("QMI send Req %d failed, rc= %d\n", QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01, rc); return rc; } rc = ipa3_check_qmi_response(rc, QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01, resp.resp.result, resp.resp.error, "ipa_add_offload_connection"); if (rc) { IPAWANERR("QMI get Response %d failed, rc= %d\n", QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01, rc); return rc; } /* Check & copy rule-handle */ if (!resp.filter_handle_list_valid) { IPAWANERR("QMI resp invalid %d failed\n", resp.filter_handle_list_valid); return -ERANGE; } if (resp.filter_handle_list_len != req->filter_spec_ex2_list_len) { IPAWANERR("QMI resp invalid size %d req %d\n", resp.filter_handle_list_len, req->filter_spec_ex2_list_len); return -ERANGE; } mutex_lock(&ipa3_qmi_lock); for (i = 0; i < req->filter_spec_ex2_list_len; i++) { id = resp.filter_handle_list[i].filter_spec_identifier; /* check rule-id matched or not */ if (req->filter_spec_ex2_list[i].rule_id != id) { IPAWANERR("QMI error (%d)st-(%d) rule-id (%d)\n", i, id, req->filter_spec_ex2_list[i].rule_id); mutex_unlock(&ipa3_qmi_lock); return -EINVAL; } /* find free spot*/ for (j = 0; j < QMI_IPA_MAX_FILTERS_V01; j++) { if (ipa3_qmi_ctx->ipa_offload_cache[j].valid == false) break; } if (j == QMI_IPA_MAX_FILTERS_V01) { IPAWANERR("can't find free spot for rule-id %d\n", id); mutex_unlock(&ipa3_qmi_lock); return -EINVAL; } /* save rule-id handle to cache */ ipa3_qmi_ctx->ipa_offload_cache[j].rule_id = resp.filter_handle_list[i].filter_spec_identifier; ipa3_qmi_ctx->ipa_offload_cache[j].rule_hdl = resp.filter_handle_list[i].filter_handle; ipa3_qmi_ctx->ipa_offload_cache[j].valid = true; ipa3_qmi_ctx->ipa_offload_cache[j].ip_type = req->filter_spec_ex2_list[i].ip_type; ipa3_qmi_ctx->num_ipa_offload_connection++; } mutex_unlock(&ipa3_qmi_lock); IPAWANDBG("Update cached conntrack entries (%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection); return rc; } /* sending rmv offload-connection-request to modem*/ int ipa3_qmi_rmv_offload_request_send( struct ipa_remove_offload_connection_req_msg_v01 *req) { struct ipa_remove_offload_connection_resp_msg_v01 resp; struct ipa_msg_desc req_desc, resp_desc; int rc = 0; int i, j; uint32_t id; /* check if modem up */ if (!ipa3_qmi_modem_init_fin || !ipa_q6_clnt) { IPAWANDBG("modem QMI haven't up yet\n"); return -EINVAL; } /* check if the # of handles from IPACM is valid */ if (req->filter_handle_list_len == 0) { IPAWANDBG("IPACM deleted zero rules !\n"); return -EINVAL; } IPAWANDBG("IPACM pass (%d) rules handles to Q6, cur (%d)\n", req->filter_handle_list_len, ipa3_qmi_ctx->num_ipa_offload_connection); /* max as num_ipa_offload_connection */ if (req->filter_handle_list_len >= ipa3_qmi_ctx->num_ipa_offload_connection) { IPAWANDBG( "cur(%d), req_rmv(%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection, req->filter_handle_list_len); return -EINVAL; } mutex_lock(&ipa3_qmi_lock); for (i = 0; i < req->filter_handle_list_len; i++) { /* check if rule-id match */ id = req->filter_handle_list[i].filter_spec_identifier; for (j = 0; j < QMI_IPA_MAX_FILTERS_V01; j++) { if ((ipa3_qmi_ctx->ipa_offload_cache[j].valid) && (ipa3_qmi_ctx->ipa_offload_cache[j].rule_id == id)) break; } if (j == QMI_IPA_MAX_FILTERS_V01) { IPAWANERR("can't find rule-id %d\n", id); mutex_unlock(&ipa3_qmi_lock); return -EINVAL; } /* fill up the filter_handle */ req->filter_handle_list[i].filter_handle = ipa3_qmi_ctx->ipa_offload_cache[j].rule_hdl; ipa3_qmi_ctx->ipa_offload_cache[j].valid == false; ipa3_qmi_ctx->num_ipa_offload_connection--; } mutex_unlock(&ipa3_qmi_lock); req_desc.max_msg_len = IPA_REMOVE_OFFLOAD_CONNECTION_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_IPA_REMOVE_OFFLOAD_CONNECTION_REQ_V01; req_desc.ei_array = ipa_remove_offload_connection_req_msg_v01_ei; memset(&resp, 0, sizeof(struct ipa_remove_offload_connection_resp_msg_v01)); resp_desc.max_msg_len = IPA_REMOVE_OFFLOAD_CONNECTION_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_IPA_REMOVE_OFFLOAD_CONNECTION_RESP_V01; resp_desc.ei_array = ipa_remove_offload_connection_resp_msg_v01_ei; rc = ipa3_qmi_send_req_wait(ipa_q6_clnt, &req_desc, req, &resp_desc, &resp, QMI_SEND_REQ_TIMEOUT_MS); if (rc < 0) { IPAWANERR("QMI send Req %d failed, rc= %d\n", QMI_IPA_REMOVE_OFFLOAD_CONNECTION_REQ_V01, rc); return rc; } IPAWANDBG("left cached conntrack entries (%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection); return ipa3_check_qmi_response(rc, QMI_IPA_REMOVE_OFFLOAD_CONNECTION_REQ_V01, resp.resp.result, resp.resp.error, "ipa_rmv_offload_connection"); } /* sending ul-filter-install-request to modem*/ int ipa3_qmi_ul_filter_request_send( struct ipa_configure_ul_firewall_rules_req_msg_v01 *req) Loading Loading @@ -1160,7 +1432,9 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) IPAWANDBG("Q6 QMI service available now\n"); if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { IPAWANDBG("Dun send QMI msg to modem\n"); ipa3_qmi_modem_init_fin = true; IPAWANDBG("QMI-client complete, ipa3_qmi_modem_init_fin : %d\n", ipa3_qmi_modem_init_fin); return; } Loading @@ -1170,7 +1444,7 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) if ((rc == -ENETRESET) || (rc == -ENODEV)) { IPAWANERR( "ipa3_qmi_init_modem_send_sync_msg failed due to SSR!\n"); /* Cleanup will take place when ipa3_wwan_remove is called */ /* Cleanup when ipa3_wwan_remove is called */ vfree(ipa_q6_clnt); ipa_q6_clnt = NULL; return; Loading @@ -1180,12 +1454,12 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) IPAWANERR("ipa3_qmi_init_modem_send_sync_msg failed\n"); /* * Hardware not responding. * This is a very unexpected scenario, which requires a kernel * panic in order to force dumps for QMI/Q6 side analysis. * This is a very unexpected scenario * which requires a kernel panic in * order to force dumps for QMI/Q6 side analysis. */ BUG(); } ipa3_qmi_modem_init_fin = true; if ((ipa3_modem_init_cmplt == true) && Loading Loading @@ -1376,19 +1650,26 @@ static void ipa3_qmi_service_init_worker(struct work_struct *work) { int rc; /* Initialize QMI-service*/ IPAWANDBG("IPA A7 QMI init OK :>>>>\n"); /* start the QMI msg cache */ ipa3_qmi_ctx = vzalloc(sizeof(*ipa3_qmi_ctx)); if (!ipa3_qmi_ctx) { IPAWANERR("Failed to allocate the memory to ipa3_qmi_ctx\n"); IPAWANERR("Failed to allocate ipa3_qmi_ctx\n"); return; } if (ipa3_is_apq()) { /* Only start QMI-client */ IPAWANDBG("Only start IPA A7 QMI client\n"); goto qmi_client_start; } /* Initialize QMI-service*/ IPAWANDBG("IPA A7 QMI init OK :>>>>\n"); ipa3_qmi_ctx->modem_cfg_emb_pipe_flt = ipa3_get_modem_cfg_emb_pipe_flt(); ipa3_qmi_ctx->num_ipa_offload_connection = 0; ipa3_svc_handle = vzalloc(sizeof(*ipa3_svc_handle)); if (!ipa3_svc_handle) Loading @@ -1415,6 +1696,7 @@ static void ipa3_qmi_service_init_worker(struct work_struct *work) goto deregister_qmi_srv; } qmi_client_start: /* Initialize QMI-client */ ipa_clnt_req_workqueue = create_singlethread_workqueue("clnt_req"); if (!ipa_clnt_req_workqueue) { Loading Loading @@ -1461,14 +1743,17 @@ static void ipa3_qmi_service_init_worker(struct work_struct *work) destroy_workqueue(ipa_clnt_req_workqueue); ipa_clnt_req_workqueue = NULL; deregister_qmi_srv: if (!ipa3_is_apq()) qmi_handle_release(ipa3_svc_handle); destroy_qmi_handle: vfree(ipa3_qmi_ctx); destroy_ipa_A7_svc_wq: if (!ipa3_is_apq()) { vfree(ipa3_svc_handle); ipa3_qmi_ctx = NULL; ipa3_svc_handle = NULL; } ipa3_qmi_ctx = NULL; } int ipa3_qmi_service_init(uint32_t wan_platform_type) { Loading Loading @@ -1672,6 +1957,57 @@ int ipa3_qmi_set_data_quota(struct ipa_set_data_usage_quota_req_msg_v01 *req) resp.resp.error, "ipa_set_data_usage_quota_req_msg_v01"); } int ipa3_qmi_set_aggr_info(enum ipa_aggr_enum_type_v01 aggr_enum_type) { struct ipa_mhi_prime_aggr_info_resp_msg_v01 resp; struct ipa_msg_desc req_desc, resp_desc; int rc; IPAWANDBG("sending aggr_info_request\n"); /* replace to right qmap format */ aggr_req.aggr_info[1].aggr_type = aggr_enum_type; aggr_req.aggr_info[2].aggr_type = aggr_enum_type; aggr_req.aggr_info[2].pkt_count = 1; /*disable aggregation */ aggr_req.aggr_info[3].aggr_type = aggr_enum_type; aggr_req.aggr_info[4].aggr_type = aggr_enum_type; memset(&resp, 0, sizeof(struct ipa_mhi_prime_aggr_info_resp_msg_v01)); req_desc.max_msg_len = IPA_MHI_PRIME_AGGR_INFO_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_IPA_MHI_PRIME_AGGR_INFO_REQ_V01; req_desc.ei_array = ipa_mhi_prime_aggr_info_req_msg_v01_ei; resp_desc.max_msg_len = IPA_MHI_PRIME_AGGR_INFO_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_IPA_MHI_PRIME_AGGR_INFO_RESP_V01; resp_desc.ei_array = ipa_mhi_prime_aggr_info_resp_msg_v01_ei; IPAWANDBG("Sending QMI_IPA_MHI_PRIME_AGGR_INFO_REQ_V01(%d)\n", aggr_enum_type); if (unlikely(!ipa_q6_clnt)) { IPAWANERR(" ipa_q6_clnt not initialized\n"); return -ETIMEDOUT; } rc = ipa3_qmi_send_req_wait(ipa_q6_clnt, &req_desc, &aggr_req, &resp_desc, &resp, QMI_SEND_STATS_REQ_TIMEOUT_MS); if (rc < 0) { IPAWANERR("QMI send Req %d failed, rc= %d\n", QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01, rc); return rc; } IPAWANDBG_LOW("QMI_IPA_MHI_PRIME_AGGR_INFO_RESP_V01 received\n"); return ipa3_check_qmi_response(rc, QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01, resp.resp.result, resp.resp.error, "ipa_mhi_prime_aggr_info_req_msg_v01"); } int ipa3_qmi_stop_data_qouta(void) { struct ipa_stop_data_usage_quota_req_msg_v01 req; Loading drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h +49 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #define IPA_DFLT_WAN_RT_TBL_NAME "ipa_dflt_wan_rt" #define MAX_NUM_Q6_RULE 35 #define MAX_NUM_QMI_RULE_CACHE 10 #define MAX_NUM_QMI_MPM_AGGR_CACHE 3 #define DEV_NAME "ipa-wan" #define SUBSYS_LOCAL_MODEM "modem" #define SUBSYS_REMOTE_MODEM "esoc0" Loading Loading @@ -83,6 +84,13 @@ extern struct ipa3_qmi_context *ipa3_qmi_ctx; struct ipa_offload_connection_val { enum ipa_ip_type_enum_v01 ip_type; bool valid; uint32_t rule_id; uint32_t rule_hdl; }; struct ipa3_qmi_context { struct ipa_ioc_ext_intf_prop q6_ul_filter_rule[MAX_NUM_Q6_RULE]; u32 q6_ul_filter_rule_hdl[MAX_NUM_Q6_RULE]; Loading @@ -99,9 +107,15 @@ struct ipa3_qmi_context { struct ipa_configure_ul_firewall_rules_req_msg_v01 ipa_configure_ul_firewall_rules_req_msg_cache [MAX_NUM_QMI_RULE_CACHE]; struct ipa_mhi_prime_aggr_info_req_msg_v01 ipa_mhi_prime_aggr_info_req_msg_cache [MAX_NUM_QMI_MPM_AGGR_CACHE]; bool modem_cfg_emb_pipe_flt; struct sockaddr_qrtr client_sq; struct sockaddr_qrtr server_sq; int num_ipa_offload_connection; struct ipa_offload_connection_val ipa_offload_cache[QMI_IPA_MAX_FILTERS_V01]; }; struct ipa3_rmnet_mux_val { Loading Loading @@ -196,6 +210,14 @@ extern struct qmi_elem_info ipa_mhi_clk_vote_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_cleanup_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_cleanup_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_endp_desc_indication_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_prime_aggr_info_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_prime_aggr_info_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_add_offload_connection_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_add_offload_connection_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_remove_offload_connection_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_remove_offload_connection_resp_msg_v01_ei[]; /** * struct ipa3_rmnet_context - IPA rmnet context * @ipa_rmnet_ssr: support modem SSR Loading Loading @@ -223,6 +245,12 @@ int ipa3_qmi_filter_request_send( int ipa3_qmi_filter_request_ex_send( struct ipa_install_fltr_rule_req_ex_msg_v01 *req); int ipa3_qmi_add_offload_request_send( struct ipa_add_offload_connection_req_msg_v01 *req); int ipa3_qmi_rmv_offload_request_send( struct ipa_remove_offload_connection_req_msg_v01 *req); int ipa3_qmi_ul_filter_request_send( struct ipa_configure_ul_firewall_rules_req_msg_v01 *req); Loading Loading @@ -294,6 +322,9 @@ int ipa3_qmi_get_network_stats(struct ipa_get_apn_data_stats_req_msg_v01 *req, int ipa3_qmi_set_data_quota(struct ipa_set_data_usage_quota_req_msg_v01 *req); int ipa3_qmi_set_aggr_info( enum ipa_aggr_enum_type_v01 aggr_enum_type); int ipa3_qmi_stop_data_qouta(void); void ipa3_q6_handshake_complete(bool ssr_bootup); Loading Loading @@ -334,6 +365,18 @@ static inline int ipa3_qmi_filter_request_send( return -EPERM; } static inline int ipa3_qmi_add_offload_request_send( struct ipa_add_offload_connection_req_msg_v01 *req) { return -EPERM; } static inline int ipa3_qmi_rmv_offload_request_send( struct ipa_rmv_offload_connection_req_msg_v01 *req) { return -EPERM; } static inline int ipa3_qmi_ul_filter_request_send( struct ipa_configure_ul_firewall_rules_req_msg_v01 *req) { Loading Loading @@ -467,6 +510,12 @@ static inline int ipa3_qmi_get_per_client_packet_stats( return -EPERM; } static inline int ipa3_qmi_set_aggr_info( enum ipa_aggr_enum_type_v01 aggr_enum_type) { return -EPERM; } static inline void ipa3_qmi_init(void) { Loading drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +14 −0 Original line number Diff line number Diff line Loading @@ -7942,6 +7942,20 @@ void ipa3_read_mailbox_17(enum uc_state state) } } /** * ipa3_is_apq() - indicate apq platform or not * * Return value: true if apq, false if not apq platform * */ bool ipa3_is_apq(void) { if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) return true; else return false; } /** * ipa3_disable_prefetch() - disable\enable tx prefetch * Loading drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +115 −35 Original line number Diff line number Diff line Loading @@ -169,6 +169,8 @@ struct rmnet_ipa3_context { [IPACM_MAX_CLIENT_DEVICE_TYPES]; bool dl_csum_offload_enabled; atomic_t ap_suspend; bool ipa_config_is_apq; bool ipa_mhi_aggr_formet_set; }; static struct rmnet_ipa3_context *rmnet_ipa3_ctx; Loading Loading @@ -339,9 +341,11 @@ static int ipa3_add_qmap_hdr(uint32_t mux_id, uint32_t *hdr_hdl) strlcpy(hdr_entry->name, hdr_name, IPA_RESOURCE_NAME_MAX); if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5 && rmnet_ipa3_ctx->dl_csum_offload_enabled) { hdr_entry->hdr_len = IPA_DL_CHECKSUM_LENGTH; /* 8 bytes */ if (rmnet_ipa3_ctx->dl_csum_offload_enabled) { if (rmnet_ipa3_ctx->ipa_config_is_apq || ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5) { hdr_entry->hdr_len = IPA_DL_CHECKSUM_LENGTH; /* 8 bytes */ /* new DL QMAP header format */ hdr_entry->hdr[0] = 0x40; hdr_entry->hdr[1] = (uint8_t) mux_id; Loading @@ -349,12 +353,18 @@ static int ipa3_add_qmap_hdr(uint32_t mux_id, uint32_t *hdr_hdl) hdr_entry->hdr[3] = 0; hdr_entry->hdr[4] = 0x4; /* * Need to set csum required/valid bit on which will be replaced * by HW if checksum is incorrect after validation * Need to set csum required/valid bit on * which will be replaced by HW if checksum * is incorrect after validation */ hdr_entry->hdr[5] = 0x80; hdr_entry->hdr[6] = 0; hdr_entry->hdr[7] = 0; } else { hdr_entry->hdr_len = IPA_QMAP_HEADER_LENGTH; /* 4 bytes */ hdr_entry->hdr[1] = (uint8_t) mux_id; } } else { hdr_entry->hdr_len = IPA_QMAP_HEADER_LENGTH; /* 4 bytes */ hdr_entry->hdr[1] = (uint8_t) mux_id; Loading Loading @@ -896,12 +906,18 @@ static int ipa3_wwan_register_to_ipa(int index) tx_properties.prop = tx_ioc_properties; tx_ipv4_property = &tx_properties.prop[0]; tx_ipv4_property->ip = IPA_IP_v4; if (rmnet_ipa3_ctx->ipa_config_is_apq) tx_ipv4_property->dst_pipe = IPA_CLIENT_MHI_PRIME_TETH_CONS; else tx_ipv4_property->dst_pipe = IPA_CLIENT_APPS_WAN_CONS; snprintf(tx_ipv4_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d", A2_MUX_HDR_NAME_V4_PREF, rmnet_ipa3_ctx->mux_channel[index].mux_id); tx_ipv6_property = &tx_properties.prop[1]; tx_ipv6_property->ip = IPA_IP_v6; if (rmnet_ipa3_ctx->ipa_config_is_apq) tx_ipv6_property->dst_pipe = IPA_CLIENT_MHI_PRIME_TETH_CONS; else tx_ipv6_property->dst_pipe = IPA_CLIENT_APPS_WAN_CONS; /* no need use A2_MUX_HDR_NAME_V6_PREF, same header */ snprintf(tx_ipv6_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d", Loading @@ -916,6 +932,9 @@ static int ipa3_wwan_register_to_ipa(int index) rx_ipv4_property->attrib.meta_data = rmnet_ipa3_ctx->mux_channel[index].mux_id << WWAN_METADATA_SHFT; rx_ipv4_property->attrib.meta_data_mask = WWAN_METADATA_MASK; if (rmnet_ipa3_ctx->ipa_config_is_apq) rx_ipv4_property->src_pipe = IPA_CLIENT_MHI_PRIME_TETH_PROD; else rx_ipv4_property->src_pipe = IPA_CLIENT_APPS_WAN_PROD; rx_ipv6_property = &rx_properties.prop[1]; rx_ipv6_property->ip = IPA_IP_v6; Loading @@ -923,16 +942,47 @@ static int ipa3_wwan_register_to_ipa(int index) rx_ipv6_property->attrib.meta_data = rmnet_ipa3_ctx->mux_channel[index].mux_id << WWAN_METADATA_SHFT; rx_ipv6_property->attrib.meta_data_mask = WWAN_METADATA_MASK; if (rmnet_ipa3_ctx->ipa_config_is_apq) rx_ipv6_property->src_pipe = IPA_CLIENT_MHI_PRIME_TETH_PROD; else rx_ipv6_property->src_pipe = IPA_CLIENT_APPS_WAN_PROD; rx_properties.num_props = 2; if (rmnet_ipa3_ctx->ipa_config_is_apq) { /* provide mux-id to ipacm in apq platform*/ pyld_sz = sizeof(struct ipa_ioc_ext_intf_prop); ext_ioc_properties = kmalloc(pyld_sz, GFP_KERNEL); if (!ext_ioc_properties) return -ENOMEM; ext_properties.prop = ext_ioc_properties; ext_properties.num_props = 1; ext_properties.prop[0].mux_id = rmnet_ipa3_ctx->mux_channel[index].mux_id; ext_properties.prop[0].ip = IPA_IP_MAX; IPAWANDBG("ip: %d mux:%d\n", ext_properties.prop[0].ip, ext_properties.prop[0].mux_id); ret = ipa3_register_intf_ext( rmnet_ipa3_ctx->mux_channel[index].vchannel_name, &tx_properties, &rx_properties, &ext_properties); if (ret) { IPAWANERR("[%d]ipa3_register_intf failed %d\n", index, ret); goto fail; } goto end; } /* non apq case */ pyld_sz = rmnet_ipa3_ctx->num_q6_rules * sizeof(struct ipa_ioc_ext_intf_prop); ext_ioc_properties = kmalloc(pyld_sz, GFP_KERNEL); if (!ext_ioc_properties) return -ENOMEM; ext_properties.prop = ext_ioc_properties; ext_properties.excp_pipe_valid = true; ext_properties.excp_pipe = IPA_CLIENT_APPS_WAN_CONS; Loading @@ -957,9 +1007,11 @@ static int ipa3_wwan_register_to_ipa(int index) &ext_properties); if (ret) { IPAWANERR("[%s]:ipa3_register_intf failed %d\n", rmnet_ipa3_ctx->mux_channel[index].vchannel_name, ret); rmnet_ipa3_ctx->mux_channel[index].vchannel_name, ret); goto fail; } end: rmnet_ipa3_ctx->mux_channel[index].ul_flt_reg = true; fail: kfree(ext_ioc_properties); Loading Loading @@ -1154,7 +1206,7 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev) struct ipa3_wwan_private *wwan_ptr = netdev_priv(dev); unsigned long flags; if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { if (rmnet_ipa3_ctx->ipa_config_is_apq) { IPAWANERR_RL("IPA embedded data on APQ platform\n"); dev_kfree_skb_any(skb); dev->stats.tx_dropped++; Loading Loading @@ -1502,7 +1554,24 @@ static int handle3_egress_format(struct net_device *dev, struct ipa_sys_connect_params *ipa_wan_ep_cfg; int ep_idx; IPAWANDBG("get RMNET_IOCTL_SET_EGRESS_DATA_FORMAT\n"); IPAWANDBG("get RMNET_IOCTL_SET_EGRESS_DATA_FORMAT %x\n", e->u.data); /* in APQ platform, only get QMAP format */ if (rmnet_ipa3_ctx->ipa_config_is_apq) { if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_CHECKSUM) { /* QMAPv5 */ rmnet_ipa3_ctx->dl_csum_offload_enabled = false; /* send aggr_info_qmi */ rc = ipa3_qmi_set_aggr_info(DATA_AGGR_TYPE_QMAP_V01); } else { /* QMAP */ rmnet_ipa3_ctx->dl_csum_offload_enabled = false; /* send aggr_info_qmi */ rc = ipa3_qmi_set_aggr_info(DATA_AGGR_TYPE_QMAP_V01); } rmnet_ipa3_ctx->ipa_mhi_aggr_formet_set = true; return rc; } ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_PROD); if (ep_idx == IPA_EP_NOT_ALLOCATED) { Loading Loading @@ -1836,7 +1905,8 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* check if UL filter rules coming*/ v_name = ext_ioctl_data.u.rmnet_mux_val.vchannel_name; if (rmnet_ipa3_ctx->num_q6_rules != 0) { if (rmnet_ipa3_ctx->num_q6_rules != 0 || (rmnet_ipa3_ctx->ipa_config_is_apq)) { mux_mutex_ptr = &rmnet_ipa3_ctx->add_mux_channel_lock; IPAWANERR_RL("dev(%s) register to IPA\n", Loading Loading @@ -2144,12 +2214,17 @@ int ipa3_wwan_set_modem_perf_profile(int throughput) { struct ipa_rm_perf_profile profile; int ret; int tether_bridge_handle = 0; if (ipa3_ctx->use_ipa_pm) { ret = ipa_pm_set_throughput(rmnet_ipa3_ctx->q6_pm_hdl, /* query rmnet-tethering handle */ tether_bridge_handle = ipa3_teth_bridge_get_pm_hdl(); if (tether_bridge_handle > 0) { /* only update with valid handle*/ ret = ipa_pm_set_throughput(tether_bridge_handle, throughput); if (ret) return ret; } /* for TETH MODEM on softap/rndis */ ret = ipa_pm_set_throughput(rmnet_ipa3_ctx->q6_teth_pm_hdl, throughput); } else { Loading Loading @@ -2543,6 +2618,10 @@ static int ipa3_wwan_probe(struct platform_device *pdev) ret = get_ipa_rmnet_dts_configuration(pdev, &ipa3_rmnet_res); ipa3_rmnet_ctx.ipa_rmnet_ssr = ipa3_rmnet_res.ipa_rmnet_ssr; /* check if booting as mhi-prime */ rmnet_ipa3_ctx->ipa_config_is_apq = ipa3_is_apq(); ret = ipa3_init_q6_smem(); if (ret) { IPAWANERR("ipa3_init_q6_smem failed\n"); Loading @@ -2561,6 +2640,7 @@ static int ipa3_wwan_probe(struct platform_device *pdev) rmnet_ipa3_ctx->rmnet_index = 0; rmnet_ipa3_ctx->egress_set = false; rmnet_ipa3_ctx->a7_ul_flt_set = false; rmnet_ipa3_ctx->ipa_mhi_aggr_formet_set = false; for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) memset(&rmnet_ipa3_ctx->mux_channel[i], 0, sizeof(struct ipa3_rmnet_mux_val)); Loading Loading @@ -2907,7 +2987,7 @@ static int ipa3_lcl_mdm_ssr_notifier_cb(struct notifier_block *this, return NOTIFY_DONE; } if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { if (rmnet_ipa3_ctx->ipa_config_is_apq) { IPAWANERR("Local modem SSR event=%lu on APQ platform\n", code); return NOTIFY_DONE; Loading Loading @@ -2984,7 +3064,7 @@ static int ipa3_rmt_mdm_ssr_notifier_cb(struct notifier_block *this, return NOTIFY_DONE; } if (ipa3_ctx->platform_type != IPA_PLAT_TYPE_APQ) { if (!rmnet_ipa3_ctx->ipa_config_is_apq) { IPAWANERR("Remote mdm SSR event=%lu on non-APQ platform=%d\n", code, ipa3_ctx->platform_type); return NOTIFY_DONE; Loading Loading @@ -4705,10 +4785,10 @@ static int __init ipa3_wwan_init(void) &ipa3_lcl_mdm_ssr_notifier); if (!IS_ERR(ssr_hdl)) rmnet_ipa3_ctx->lcl_mdm_subsys_notify_handle = ssr_hdl; else if (ipa3_ctx->platform_type != IPA_PLAT_TYPE_APQ) else if (!rmnet_ipa3_ctx->ipa_config_is_apq) return (int)PTR_ERR(ssr_hdl); if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { if (rmnet_ipa3_ctx->ipa_config_is_apq) { /* Register for Remote Modem SSR */ ssr_hdl = subsys_notif_register_notifier(SUBSYS_REMOTE_MODEM, &ipa3_rmt_mdm_ssr_notifier); Loading Loading
drivers/platform/msm/ipa/ipa_v3/ipa_i.h +4 −0 Original line number Diff line number Diff line Loading @@ -2501,6 +2501,8 @@ int ipa3_teth_bridge_disconnect(enum ipa_client_type client); int ipa3_teth_bridge_connect(struct teth_bridge_connect_params *connect_params); int ipa3_teth_bridge_get_pm_hdl(void); /* * Tethering client info */ Loading Loading @@ -2980,4 +2982,6 @@ static inline int ipa_mpm_panic_handler(char *buf, int size) #endif /* CONFIG_IPA3_MHI_PRIME_MANAGER */ /* query ipa APQ mode*/ bool ipa3_is_apq(void); #endif /* _IPA3_I_H_ */
drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +353 −17 Original line number Diff line number Diff line Loading @@ -57,6 +57,40 @@ struct ipa_msg_desc { struct qmi_elem_info *ei_array; }; static struct ipa_mhi_prime_aggr_info_req_msg_v01 aggr_req = { .aggr_info_valid = 1, .aggr_info_len = 5, .aggr_info[0] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_DPL_PROD_V01, .bytes_count = 16, }, .aggr_info[1] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_CONS_V01, .bytes_count = 24, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, .aggr_info[2] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_PROD_V01, .bytes_count = 16, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, .aggr_info[3] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_RMNET_CONS_V01, .bytes_count = 31, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, .aggr_info[4] = { .ic_type = DATA_IC_TYPE_MHI_PRIME_V01, .ep_type = DATA_EP_DESC_TYPE_TETH_RMNET_PROD_V01, .bytes_count = 31, .aggr_type = DATA_AGGR_TYPE_QMAPv5_V01, }, }; /* QMI A5 service */ static void ipa3_handle_indication_req(struct qmi_handle *qmi_handle, Loading Loading @@ -798,6 +832,244 @@ int ipa3_qmi_filter_request_ex_send( resp.resp.error, "ipa_install_filter"); } /* sending add offload-connection-request to modem*/ int ipa3_qmi_add_offload_request_send( struct ipa_add_offload_connection_req_msg_v01 *req) { struct ipa_add_offload_connection_resp_msg_v01 resp; struct ipa_msg_desc req_desc, resp_desc; int rc = 0; int i, j; uint32_t id; /* check if modem up */ if (!ipa3_qmi_modem_init_fin || !ipa_q6_clnt) { IPAWANDBG("modem QMI haven't up yet\n"); return -EINVAL; } /* check if the filter rules from IPACM is valid */ if (req->filter_spec_ex2_list_len == 0) { IPAWANDBG("IPACM pass zero rules to Q6\n"); } else { IPAWANDBG("IPACM pass %u rules to Q6\n", req->filter_spec_ex2_list_len); } /* currently set total max to 64 */ if (req->filter_spec_ex2_list_len + ipa3_qmi_ctx->num_ipa_offload_connection >= QMI_IPA_MAX_FILTERS_V01) { IPAWANDBG( "cur(%d), req(%d), exceed limit (%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection, req->filter_spec_ex2_list_len, QMI_IPA_MAX_FILTERS_V01); return -EINVAL; } for (i = 0; i < req->filter_spec_ex2_list_len; i++) { if ((req->filter_spec_ex2_list[i].ip_type != QMI_IPA_IP_TYPE_V4_V01) && (req->filter_spec_ex2_list[i].ip_type != QMI_IPA_IP_TYPE_V6_V01)) return -EINVAL; if (req->filter_spec_ex2_list[i].is_mux_id_valid == false) return -EINVAL; if ((req->filter_spec_ex2_list[i].filter_action <= QMI_IPA_FILTER_ACTION_INVALID_V01) || (req->filter_spec_ex2_list[i].filter_action > QMI_IPA_FILTER_ACTION_EXCEPTION_V01)) return -EINVAL; } req_desc.max_msg_len = IPA_ADD_OFFLOAD_CONNECTION_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01; req_desc.ei_array = ipa_add_offload_connection_req_msg_v01_ei; memset(&resp, 0, sizeof(struct ipa_add_offload_connection_resp_msg_v01)); resp_desc.max_msg_len = IPA_ADD_OFFLOAD_CONNECTION_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_IPA_ADD_OFFLOAD_CONNECTION_RESP_V01; resp_desc.ei_array = ipa_add_offload_connection_resp_msg_v01_ei; rc = ipa3_qmi_send_req_wait(ipa_q6_clnt, &req_desc, req, &resp_desc, &resp, QMI_SEND_REQ_TIMEOUT_MS); if (rc < 0) { IPAWANERR("QMI send Req %d failed, rc= %d\n", QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01, rc); return rc; } rc = ipa3_check_qmi_response(rc, QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01, resp.resp.result, resp.resp.error, "ipa_add_offload_connection"); if (rc) { IPAWANERR("QMI get Response %d failed, rc= %d\n", QMI_IPA_ADD_OFFLOAD_CONNECTION_REQ_V01, rc); return rc; } /* Check & copy rule-handle */ if (!resp.filter_handle_list_valid) { IPAWANERR("QMI resp invalid %d failed\n", resp.filter_handle_list_valid); return -ERANGE; } if (resp.filter_handle_list_len != req->filter_spec_ex2_list_len) { IPAWANERR("QMI resp invalid size %d req %d\n", resp.filter_handle_list_len, req->filter_spec_ex2_list_len); return -ERANGE; } mutex_lock(&ipa3_qmi_lock); for (i = 0; i < req->filter_spec_ex2_list_len; i++) { id = resp.filter_handle_list[i].filter_spec_identifier; /* check rule-id matched or not */ if (req->filter_spec_ex2_list[i].rule_id != id) { IPAWANERR("QMI error (%d)st-(%d) rule-id (%d)\n", i, id, req->filter_spec_ex2_list[i].rule_id); mutex_unlock(&ipa3_qmi_lock); return -EINVAL; } /* find free spot*/ for (j = 0; j < QMI_IPA_MAX_FILTERS_V01; j++) { if (ipa3_qmi_ctx->ipa_offload_cache[j].valid == false) break; } if (j == QMI_IPA_MAX_FILTERS_V01) { IPAWANERR("can't find free spot for rule-id %d\n", id); mutex_unlock(&ipa3_qmi_lock); return -EINVAL; } /* save rule-id handle to cache */ ipa3_qmi_ctx->ipa_offload_cache[j].rule_id = resp.filter_handle_list[i].filter_spec_identifier; ipa3_qmi_ctx->ipa_offload_cache[j].rule_hdl = resp.filter_handle_list[i].filter_handle; ipa3_qmi_ctx->ipa_offload_cache[j].valid = true; ipa3_qmi_ctx->ipa_offload_cache[j].ip_type = req->filter_spec_ex2_list[i].ip_type; ipa3_qmi_ctx->num_ipa_offload_connection++; } mutex_unlock(&ipa3_qmi_lock); IPAWANDBG("Update cached conntrack entries (%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection); return rc; } /* sending rmv offload-connection-request to modem*/ int ipa3_qmi_rmv_offload_request_send( struct ipa_remove_offload_connection_req_msg_v01 *req) { struct ipa_remove_offload_connection_resp_msg_v01 resp; struct ipa_msg_desc req_desc, resp_desc; int rc = 0; int i, j; uint32_t id; /* check if modem up */ if (!ipa3_qmi_modem_init_fin || !ipa_q6_clnt) { IPAWANDBG("modem QMI haven't up yet\n"); return -EINVAL; } /* check if the # of handles from IPACM is valid */ if (req->filter_handle_list_len == 0) { IPAWANDBG("IPACM deleted zero rules !\n"); return -EINVAL; } IPAWANDBG("IPACM pass (%d) rules handles to Q6, cur (%d)\n", req->filter_handle_list_len, ipa3_qmi_ctx->num_ipa_offload_connection); /* max as num_ipa_offload_connection */ if (req->filter_handle_list_len >= ipa3_qmi_ctx->num_ipa_offload_connection) { IPAWANDBG( "cur(%d), req_rmv(%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection, req->filter_handle_list_len); return -EINVAL; } mutex_lock(&ipa3_qmi_lock); for (i = 0; i < req->filter_handle_list_len; i++) { /* check if rule-id match */ id = req->filter_handle_list[i].filter_spec_identifier; for (j = 0; j < QMI_IPA_MAX_FILTERS_V01; j++) { if ((ipa3_qmi_ctx->ipa_offload_cache[j].valid) && (ipa3_qmi_ctx->ipa_offload_cache[j].rule_id == id)) break; } if (j == QMI_IPA_MAX_FILTERS_V01) { IPAWANERR("can't find rule-id %d\n", id); mutex_unlock(&ipa3_qmi_lock); return -EINVAL; } /* fill up the filter_handle */ req->filter_handle_list[i].filter_handle = ipa3_qmi_ctx->ipa_offload_cache[j].rule_hdl; ipa3_qmi_ctx->ipa_offload_cache[j].valid == false; ipa3_qmi_ctx->num_ipa_offload_connection--; } mutex_unlock(&ipa3_qmi_lock); req_desc.max_msg_len = IPA_REMOVE_OFFLOAD_CONNECTION_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_IPA_REMOVE_OFFLOAD_CONNECTION_REQ_V01; req_desc.ei_array = ipa_remove_offload_connection_req_msg_v01_ei; memset(&resp, 0, sizeof(struct ipa_remove_offload_connection_resp_msg_v01)); resp_desc.max_msg_len = IPA_REMOVE_OFFLOAD_CONNECTION_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_IPA_REMOVE_OFFLOAD_CONNECTION_RESP_V01; resp_desc.ei_array = ipa_remove_offload_connection_resp_msg_v01_ei; rc = ipa3_qmi_send_req_wait(ipa_q6_clnt, &req_desc, req, &resp_desc, &resp, QMI_SEND_REQ_TIMEOUT_MS); if (rc < 0) { IPAWANERR("QMI send Req %d failed, rc= %d\n", QMI_IPA_REMOVE_OFFLOAD_CONNECTION_REQ_V01, rc); return rc; } IPAWANDBG("left cached conntrack entries (%d)\n", ipa3_qmi_ctx->num_ipa_offload_connection); return ipa3_check_qmi_response(rc, QMI_IPA_REMOVE_OFFLOAD_CONNECTION_REQ_V01, resp.resp.result, resp.resp.error, "ipa_rmv_offload_connection"); } /* sending ul-filter-install-request to modem*/ int ipa3_qmi_ul_filter_request_send( struct ipa_configure_ul_firewall_rules_req_msg_v01 *req) Loading Loading @@ -1160,7 +1432,9 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) IPAWANDBG("Q6 QMI service available now\n"); if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { IPAWANDBG("Dun send QMI msg to modem\n"); ipa3_qmi_modem_init_fin = true; IPAWANDBG("QMI-client complete, ipa3_qmi_modem_init_fin : %d\n", ipa3_qmi_modem_init_fin); return; } Loading @@ -1170,7 +1444,7 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) if ((rc == -ENETRESET) || (rc == -ENODEV)) { IPAWANERR( "ipa3_qmi_init_modem_send_sync_msg failed due to SSR!\n"); /* Cleanup will take place when ipa3_wwan_remove is called */ /* Cleanup when ipa3_wwan_remove is called */ vfree(ipa_q6_clnt); ipa_q6_clnt = NULL; return; Loading @@ -1180,12 +1454,12 @@ static void ipa3_q6_clnt_svc_arrive(struct work_struct *work) IPAWANERR("ipa3_qmi_init_modem_send_sync_msg failed\n"); /* * Hardware not responding. * This is a very unexpected scenario, which requires a kernel * panic in order to force dumps for QMI/Q6 side analysis. * This is a very unexpected scenario * which requires a kernel panic in * order to force dumps for QMI/Q6 side analysis. */ BUG(); } ipa3_qmi_modem_init_fin = true; if ((ipa3_modem_init_cmplt == true) && Loading Loading @@ -1376,19 +1650,26 @@ static void ipa3_qmi_service_init_worker(struct work_struct *work) { int rc; /* Initialize QMI-service*/ IPAWANDBG("IPA A7 QMI init OK :>>>>\n"); /* start the QMI msg cache */ ipa3_qmi_ctx = vzalloc(sizeof(*ipa3_qmi_ctx)); if (!ipa3_qmi_ctx) { IPAWANERR("Failed to allocate the memory to ipa3_qmi_ctx\n"); IPAWANERR("Failed to allocate ipa3_qmi_ctx\n"); return; } if (ipa3_is_apq()) { /* Only start QMI-client */ IPAWANDBG("Only start IPA A7 QMI client\n"); goto qmi_client_start; } /* Initialize QMI-service*/ IPAWANDBG("IPA A7 QMI init OK :>>>>\n"); ipa3_qmi_ctx->modem_cfg_emb_pipe_flt = ipa3_get_modem_cfg_emb_pipe_flt(); ipa3_qmi_ctx->num_ipa_offload_connection = 0; ipa3_svc_handle = vzalloc(sizeof(*ipa3_svc_handle)); if (!ipa3_svc_handle) Loading @@ -1415,6 +1696,7 @@ static void ipa3_qmi_service_init_worker(struct work_struct *work) goto deregister_qmi_srv; } qmi_client_start: /* Initialize QMI-client */ ipa_clnt_req_workqueue = create_singlethread_workqueue("clnt_req"); if (!ipa_clnt_req_workqueue) { Loading Loading @@ -1461,14 +1743,17 @@ static void ipa3_qmi_service_init_worker(struct work_struct *work) destroy_workqueue(ipa_clnt_req_workqueue); ipa_clnt_req_workqueue = NULL; deregister_qmi_srv: if (!ipa3_is_apq()) qmi_handle_release(ipa3_svc_handle); destroy_qmi_handle: vfree(ipa3_qmi_ctx); destroy_ipa_A7_svc_wq: if (!ipa3_is_apq()) { vfree(ipa3_svc_handle); ipa3_qmi_ctx = NULL; ipa3_svc_handle = NULL; } ipa3_qmi_ctx = NULL; } int ipa3_qmi_service_init(uint32_t wan_platform_type) { Loading Loading @@ -1672,6 +1957,57 @@ int ipa3_qmi_set_data_quota(struct ipa_set_data_usage_quota_req_msg_v01 *req) resp.resp.error, "ipa_set_data_usage_quota_req_msg_v01"); } int ipa3_qmi_set_aggr_info(enum ipa_aggr_enum_type_v01 aggr_enum_type) { struct ipa_mhi_prime_aggr_info_resp_msg_v01 resp; struct ipa_msg_desc req_desc, resp_desc; int rc; IPAWANDBG("sending aggr_info_request\n"); /* replace to right qmap format */ aggr_req.aggr_info[1].aggr_type = aggr_enum_type; aggr_req.aggr_info[2].aggr_type = aggr_enum_type; aggr_req.aggr_info[2].pkt_count = 1; /*disable aggregation */ aggr_req.aggr_info[3].aggr_type = aggr_enum_type; aggr_req.aggr_info[4].aggr_type = aggr_enum_type; memset(&resp, 0, sizeof(struct ipa_mhi_prime_aggr_info_resp_msg_v01)); req_desc.max_msg_len = IPA_MHI_PRIME_AGGR_INFO_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_IPA_MHI_PRIME_AGGR_INFO_REQ_V01; req_desc.ei_array = ipa_mhi_prime_aggr_info_req_msg_v01_ei; resp_desc.max_msg_len = IPA_MHI_PRIME_AGGR_INFO_RESP_MSG_V01_MAX_MSG_LEN; resp_desc.msg_id = QMI_IPA_MHI_PRIME_AGGR_INFO_RESP_V01; resp_desc.ei_array = ipa_mhi_prime_aggr_info_resp_msg_v01_ei; IPAWANDBG("Sending QMI_IPA_MHI_PRIME_AGGR_INFO_REQ_V01(%d)\n", aggr_enum_type); if (unlikely(!ipa_q6_clnt)) { IPAWANERR(" ipa_q6_clnt not initialized\n"); return -ETIMEDOUT; } rc = ipa3_qmi_send_req_wait(ipa_q6_clnt, &req_desc, &aggr_req, &resp_desc, &resp, QMI_SEND_STATS_REQ_TIMEOUT_MS); if (rc < 0) { IPAWANERR("QMI send Req %d failed, rc= %d\n", QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01, rc); return rc; } IPAWANDBG_LOW("QMI_IPA_MHI_PRIME_AGGR_INFO_RESP_V01 received\n"); return ipa3_check_qmi_response(rc, QMI_IPA_SET_DATA_USAGE_QUOTA_REQ_V01, resp.resp.result, resp.resp.error, "ipa_mhi_prime_aggr_info_req_msg_v01"); } int ipa3_qmi_stop_data_qouta(void) { struct ipa_stop_data_usage_quota_req_msg_v01 req; Loading
drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h +49 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #define IPA_DFLT_WAN_RT_TBL_NAME "ipa_dflt_wan_rt" #define MAX_NUM_Q6_RULE 35 #define MAX_NUM_QMI_RULE_CACHE 10 #define MAX_NUM_QMI_MPM_AGGR_CACHE 3 #define DEV_NAME "ipa-wan" #define SUBSYS_LOCAL_MODEM "modem" #define SUBSYS_REMOTE_MODEM "esoc0" Loading Loading @@ -83,6 +84,13 @@ extern struct ipa3_qmi_context *ipa3_qmi_ctx; struct ipa_offload_connection_val { enum ipa_ip_type_enum_v01 ip_type; bool valid; uint32_t rule_id; uint32_t rule_hdl; }; struct ipa3_qmi_context { struct ipa_ioc_ext_intf_prop q6_ul_filter_rule[MAX_NUM_Q6_RULE]; u32 q6_ul_filter_rule_hdl[MAX_NUM_Q6_RULE]; Loading @@ -99,9 +107,15 @@ struct ipa3_qmi_context { struct ipa_configure_ul_firewall_rules_req_msg_v01 ipa_configure_ul_firewall_rules_req_msg_cache [MAX_NUM_QMI_RULE_CACHE]; struct ipa_mhi_prime_aggr_info_req_msg_v01 ipa_mhi_prime_aggr_info_req_msg_cache [MAX_NUM_QMI_MPM_AGGR_CACHE]; bool modem_cfg_emb_pipe_flt; struct sockaddr_qrtr client_sq; struct sockaddr_qrtr server_sq; int num_ipa_offload_connection; struct ipa_offload_connection_val ipa_offload_cache[QMI_IPA_MAX_FILTERS_V01]; }; struct ipa3_rmnet_mux_val { Loading Loading @@ -196,6 +210,14 @@ extern struct qmi_elem_info ipa_mhi_clk_vote_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_cleanup_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_cleanup_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_endp_desc_indication_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_prime_aggr_info_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_mhi_prime_aggr_info_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_add_offload_connection_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_add_offload_connection_resp_msg_v01_ei[]; extern struct qmi_elem_info ipa_remove_offload_connection_req_msg_v01_ei[]; extern struct qmi_elem_info ipa_remove_offload_connection_resp_msg_v01_ei[]; /** * struct ipa3_rmnet_context - IPA rmnet context * @ipa_rmnet_ssr: support modem SSR Loading Loading @@ -223,6 +245,12 @@ int ipa3_qmi_filter_request_send( int ipa3_qmi_filter_request_ex_send( struct ipa_install_fltr_rule_req_ex_msg_v01 *req); int ipa3_qmi_add_offload_request_send( struct ipa_add_offload_connection_req_msg_v01 *req); int ipa3_qmi_rmv_offload_request_send( struct ipa_remove_offload_connection_req_msg_v01 *req); int ipa3_qmi_ul_filter_request_send( struct ipa_configure_ul_firewall_rules_req_msg_v01 *req); Loading Loading @@ -294,6 +322,9 @@ int ipa3_qmi_get_network_stats(struct ipa_get_apn_data_stats_req_msg_v01 *req, int ipa3_qmi_set_data_quota(struct ipa_set_data_usage_quota_req_msg_v01 *req); int ipa3_qmi_set_aggr_info( enum ipa_aggr_enum_type_v01 aggr_enum_type); int ipa3_qmi_stop_data_qouta(void); void ipa3_q6_handshake_complete(bool ssr_bootup); Loading Loading @@ -334,6 +365,18 @@ static inline int ipa3_qmi_filter_request_send( return -EPERM; } static inline int ipa3_qmi_add_offload_request_send( struct ipa_add_offload_connection_req_msg_v01 *req) { return -EPERM; } static inline int ipa3_qmi_rmv_offload_request_send( struct ipa_rmv_offload_connection_req_msg_v01 *req) { return -EPERM; } static inline int ipa3_qmi_ul_filter_request_send( struct ipa_configure_ul_firewall_rules_req_msg_v01 *req) { Loading Loading @@ -467,6 +510,12 @@ static inline int ipa3_qmi_get_per_client_packet_stats( return -EPERM; } static inline int ipa3_qmi_set_aggr_info( enum ipa_aggr_enum_type_v01 aggr_enum_type) { return -EPERM; } static inline void ipa3_qmi_init(void) { Loading
drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +14 −0 Original line number Diff line number Diff line Loading @@ -7942,6 +7942,20 @@ void ipa3_read_mailbox_17(enum uc_state state) } } /** * ipa3_is_apq() - indicate apq platform or not * * Return value: true if apq, false if not apq platform * */ bool ipa3_is_apq(void) { if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) return true; else return false; } /** * ipa3_disable_prefetch() - disable\enable tx prefetch * Loading
drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +115 −35 Original line number Diff line number Diff line Loading @@ -169,6 +169,8 @@ struct rmnet_ipa3_context { [IPACM_MAX_CLIENT_DEVICE_TYPES]; bool dl_csum_offload_enabled; atomic_t ap_suspend; bool ipa_config_is_apq; bool ipa_mhi_aggr_formet_set; }; static struct rmnet_ipa3_context *rmnet_ipa3_ctx; Loading Loading @@ -339,9 +341,11 @@ static int ipa3_add_qmap_hdr(uint32_t mux_id, uint32_t *hdr_hdl) strlcpy(hdr_entry->name, hdr_name, IPA_RESOURCE_NAME_MAX); if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5 && rmnet_ipa3_ctx->dl_csum_offload_enabled) { hdr_entry->hdr_len = IPA_DL_CHECKSUM_LENGTH; /* 8 bytes */ if (rmnet_ipa3_ctx->dl_csum_offload_enabled) { if (rmnet_ipa3_ctx->ipa_config_is_apq || ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5) { hdr_entry->hdr_len = IPA_DL_CHECKSUM_LENGTH; /* 8 bytes */ /* new DL QMAP header format */ hdr_entry->hdr[0] = 0x40; hdr_entry->hdr[1] = (uint8_t) mux_id; Loading @@ -349,12 +353,18 @@ static int ipa3_add_qmap_hdr(uint32_t mux_id, uint32_t *hdr_hdl) hdr_entry->hdr[3] = 0; hdr_entry->hdr[4] = 0x4; /* * Need to set csum required/valid bit on which will be replaced * by HW if checksum is incorrect after validation * Need to set csum required/valid bit on * which will be replaced by HW if checksum * is incorrect after validation */ hdr_entry->hdr[5] = 0x80; hdr_entry->hdr[6] = 0; hdr_entry->hdr[7] = 0; } else { hdr_entry->hdr_len = IPA_QMAP_HEADER_LENGTH; /* 4 bytes */ hdr_entry->hdr[1] = (uint8_t) mux_id; } } else { hdr_entry->hdr_len = IPA_QMAP_HEADER_LENGTH; /* 4 bytes */ hdr_entry->hdr[1] = (uint8_t) mux_id; Loading Loading @@ -896,12 +906,18 @@ static int ipa3_wwan_register_to_ipa(int index) tx_properties.prop = tx_ioc_properties; tx_ipv4_property = &tx_properties.prop[0]; tx_ipv4_property->ip = IPA_IP_v4; if (rmnet_ipa3_ctx->ipa_config_is_apq) tx_ipv4_property->dst_pipe = IPA_CLIENT_MHI_PRIME_TETH_CONS; else tx_ipv4_property->dst_pipe = IPA_CLIENT_APPS_WAN_CONS; snprintf(tx_ipv4_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d", A2_MUX_HDR_NAME_V4_PREF, rmnet_ipa3_ctx->mux_channel[index].mux_id); tx_ipv6_property = &tx_properties.prop[1]; tx_ipv6_property->ip = IPA_IP_v6; if (rmnet_ipa3_ctx->ipa_config_is_apq) tx_ipv6_property->dst_pipe = IPA_CLIENT_MHI_PRIME_TETH_CONS; else tx_ipv6_property->dst_pipe = IPA_CLIENT_APPS_WAN_CONS; /* no need use A2_MUX_HDR_NAME_V6_PREF, same header */ snprintf(tx_ipv6_property->hdr_name, IPA_RESOURCE_NAME_MAX, "%s%d", Loading @@ -916,6 +932,9 @@ static int ipa3_wwan_register_to_ipa(int index) rx_ipv4_property->attrib.meta_data = rmnet_ipa3_ctx->mux_channel[index].mux_id << WWAN_METADATA_SHFT; rx_ipv4_property->attrib.meta_data_mask = WWAN_METADATA_MASK; if (rmnet_ipa3_ctx->ipa_config_is_apq) rx_ipv4_property->src_pipe = IPA_CLIENT_MHI_PRIME_TETH_PROD; else rx_ipv4_property->src_pipe = IPA_CLIENT_APPS_WAN_PROD; rx_ipv6_property = &rx_properties.prop[1]; rx_ipv6_property->ip = IPA_IP_v6; Loading @@ -923,16 +942,47 @@ static int ipa3_wwan_register_to_ipa(int index) rx_ipv6_property->attrib.meta_data = rmnet_ipa3_ctx->mux_channel[index].mux_id << WWAN_METADATA_SHFT; rx_ipv6_property->attrib.meta_data_mask = WWAN_METADATA_MASK; if (rmnet_ipa3_ctx->ipa_config_is_apq) rx_ipv6_property->src_pipe = IPA_CLIENT_MHI_PRIME_TETH_PROD; else rx_ipv6_property->src_pipe = IPA_CLIENT_APPS_WAN_PROD; rx_properties.num_props = 2; if (rmnet_ipa3_ctx->ipa_config_is_apq) { /* provide mux-id to ipacm in apq platform*/ pyld_sz = sizeof(struct ipa_ioc_ext_intf_prop); ext_ioc_properties = kmalloc(pyld_sz, GFP_KERNEL); if (!ext_ioc_properties) return -ENOMEM; ext_properties.prop = ext_ioc_properties; ext_properties.num_props = 1; ext_properties.prop[0].mux_id = rmnet_ipa3_ctx->mux_channel[index].mux_id; ext_properties.prop[0].ip = IPA_IP_MAX; IPAWANDBG("ip: %d mux:%d\n", ext_properties.prop[0].ip, ext_properties.prop[0].mux_id); ret = ipa3_register_intf_ext( rmnet_ipa3_ctx->mux_channel[index].vchannel_name, &tx_properties, &rx_properties, &ext_properties); if (ret) { IPAWANERR("[%d]ipa3_register_intf failed %d\n", index, ret); goto fail; } goto end; } /* non apq case */ pyld_sz = rmnet_ipa3_ctx->num_q6_rules * sizeof(struct ipa_ioc_ext_intf_prop); ext_ioc_properties = kmalloc(pyld_sz, GFP_KERNEL); if (!ext_ioc_properties) return -ENOMEM; ext_properties.prop = ext_ioc_properties; ext_properties.excp_pipe_valid = true; ext_properties.excp_pipe = IPA_CLIENT_APPS_WAN_CONS; Loading @@ -957,9 +1007,11 @@ static int ipa3_wwan_register_to_ipa(int index) &ext_properties); if (ret) { IPAWANERR("[%s]:ipa3_register_intf failed %d\n", rmnet_ipa3_ctx->mux_channel[index].vchannel_name, ret); rmnet_ipa3_ctx->mux_channel[index].vchannel_name, ret); goto fail; } end: rmnet_ipa3_ctx->mux_channel[index].ul_flt_reg = true; fail: kfree(ext_ioc_properties); Loading Loading @@ -1154,7 +1206,7 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev) struct ipa3_wwan_private *wwan_ptr = netdev_priv(dev); unsigned long flags; if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { if (rmnet_ipa3_ctx->ipa_config_is_apq) { IPAWANERR_RL("IPA embedded data on APQ platform\n"); dev_kfree_skb_any(skb); dev->stats.tx_dropped++; Loading Loading @@ -1502,7 +1554,24 @@ static int handle3_egress_format(struct net_device *dev, struct ipa_sys_connect_params *ipa_wan_ep_cfg; int ep_idx; IPAWANDBG("get RMNET_IOCTL_SET_EGRESS_DATA_FORMAT\n"); IPAWANDBG("get RMNET_IOCTL_SET_EGRESS_DATA_FORMAT %x\n", e->u.data); /* in APQ platform, only get QMAP format */ if (rmnet_ipa3_ctx->ipa_config_is_apq) { if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_CHECKSUM) { /* QMAPv5 */ rmnet_ipa3_ctx->dl_csum_offload_enabled = false; /* send aggr_info_qmi */ rc = ipa3_qmi_set_aggr_info(DATA_AGGR_TYPE_QMAP_V01); } else { /* QMAP */ rmnet_ipa3_ctx->dl_csum_offload_enabled = false; /* send aggr_info_qmi */ rc = ipa3_qmi_set_aggr_info(DATA_AGGR_TYPE_QMAP_V01); } rmnet_ipa3_ctx->ipa_mhi_aggr_formet_set = true; return rc; } ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_APPS_WAN_PROD); if (ep_idx == IPA_EP_NOT_ALLOCATED) { Loading Loading @@ -1836,7 +1905,8 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* check if UL filter rules coming*/ v_name = ext_ioctl_data.u.rmnet_mux_val.vchannel_name; if (rmnet_ipa3_ctx->num_q6_rules != 0) { if (rmnet_ipa3_ctx->num_q6_rules != 0 || (rmnet_ipa3_ctx->ipa_config_is_apq)) { mux_mutex_ptr = &rmnet_ipa3_ctx->add_mux_channel_lock; IPAWANERR_RL("dev(%s) register to IPA\n", Loading Loading @@ -2144,12 +2214,17 @@ int ipa3_wwan_set_modem_perf_profile(int throughput) { struct ipa_rm_perf_profile profile; int ret; int tether_bridge_handle = 0; if (ipa3_ctx->use_ipa_pm) { ret = ipa_pm_set_throughput(rmnet_ipa3_ctx->q6_pm_hdl, /* query rmnet-tethering handle */ tether_bridge_handle = ipa3_teth_bridge_get_pm_hdl(); if (tether_bridge_handle > 0) { /* only update with valid handle*/ ret = ipa_pm_set_throughput(tether_bridge_handle, throughput); if (ret) return ret; } /* for TETH MODEM on softap/rndis */ ret = ipa_pm_set_throughput(rmnet_ipa3_ctx->q6_teth_pm_hdl, throughput); } else { Loading Loading @@ -2543,6 +2618,10 @@ static int ipa3_wwan_probe(struct platform_device *pdev) ret = get_ipa_rmnet_dts_configuration(pdev, &ipa3_rmnet_res); ipa3_rmnet_ctx.ipa_rmnet_ssr = ipa3_rmnet_res.ipa_rmnet_ssr; /* check if booting as mhi-prime */ rmnet_ipa3_ctx->ipa_config_is_apq = ipa3_is_apq(); ret = ipa3_init_q6_smem(); if (ret) { IPAWANERR("ipa3_init_q6_smem failed\n"); Loading @@ -2561,6 +2640,7 @@ static int ipa3_wwan_probe(struct platform_device *pdev) rmnet_ipa3_ctx->rmnet_index = 0; rmnet_ipa3_ctx->egress_set = false; rmnet_ipa3_ctx->a7_ul_flt_set = false; rmnet_ipa3_ctx->ipa_mhi_aggr_formet_set = false; for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) memset(&rmnet_ipa3_ctx->mux_channel[i], 0, sizeof(struct ipa3_rmnet_mux_val)); Loading Loading @@ -2907,7 +2987,7 @@ static int ipa3_lcl_mdm_ssr_notifier_cb(struct notifier_block *this, return NOTIFY_DONE; } if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { if (rmnet_ipa3_ctx->ipa_config_is_apq) { IPAWANERR("Local modem SSR event=%lu on APQ platform\n", code); return NOTIFY_DONE; Loading Loading @@ -2984,7 +3064,7 @@ static int ipa3_rmt_mdm_ssr_notifier_cb(struct notifier_block *this, return NOTIFY_DONE; } if (ipa3_ctx->platform_type != IPA_PLAT_TYPE_APQ) { if (!rmnet_ipa3_ctx->ipa_config_is_apq) { IPAWANERR("Remote mdm SSR event=%lu on non-APQ platform=%d\n", code, ipa3_ctx->platform_type); return NOTIFY_DONE; Loading Loading @@ -4705,10 +4785,10 @@ static int __init ipa3_wwan_init(void) &ipa3_lcl_mdm_ssr_notifier); if (!IS_ERR(ssr_hdl)) rmnet_ipa3_ctx->lcl_mdm_subsys_notify_handle = ssr_hdl; else if (ipa3_ctx->platform_type != IPA_PLAT_TYPE_APQ) else if (!rmnet_ipa3_ctx->ipa_config_is_apq) return (int)PTR_ERR(ssr_hdl); if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_APQ) { if (rmnet_ipa3_ctx->ipa_config_is_apq) { /* Register for Remote Modem SSR */ ssr_hdl = subsys_notif_register_notifier(SUBSYS_REMOTE_MODEM, &ipa3_rmt_mdm_ssr_notifier); Loading