diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 023f3de71db8..ceb98ece043c 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -533,8 +533,8 @@ static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) return "WLFW_TWC_CFG_IND"; case CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM: return "QDSS_TRACE_REQ_MEM"; - case CNSS_DRIVER_EVENT_QDSS_TRACE_SAVE: - return "QDSS_TRACE_SAVE"; + case CNSS_DRIVER_EVENT_FW_MEM_FILE_SAVE: + return "FW_MEM_FILE_SAVE"; case CNSS_DRIVER_EVENT_QDSS_TRACE_FREE: return "QDSS_TRACE_FREE"; case CNSS_DRIVER_EVENT_MAX: @@ -1504,89 +1504,89 @@ static int cnss_qdss_trace_req_mem_hdlr(struct cnss_plat_data *plat_priv) return cnss_wlfw_qdss_trace_mem_info_send_sync(plat_priv); } -static void *cnss_qdss_trace_pa_to_va(struct cnss_plat_data *plat_priv, - u64 pa, u32 size, int *seg_id) +static void *cnss_get_fw_mem_pa_to_va(struct cnss_fw_mem *fw_mem, + u32 mem_seg_len, u64 pa, u32 size) { int i = 0; - struct cnss_fw_mem *qdss_mem = plat_priv->qdss_mem; u64 offset = 0; void *va = NULL; u64 local_pa; u32 local_size; - for (i = 0; i < plat_priv->qdss_mem_seg_len; i++) { - local_pa = (u64)qdss_mem[i].pa; - local_size = (u32)qdss_mem[i].size; + for (i = 0; i < mem_seg_len; i++) { + local_pa = (u64)fw_mem[i].pa; + local_size = (u32)fw_mem[i].size; if (pa == local_pa && size <= local_size) { - va = qdss_mem[i].va; + va = fw_mem[i].va; break; } if (pa > local_pa && pa < local_pa + local_size && pa + size <= local_pa + local_size) { offset = pa - local_pa; - va = qdss_mem[i].va + offset; + va = fw_mem[i].va + offset; break; } } - - *seg_id = i; return va; } -static int cnss_qdss_trace_save_hdlr(struct cnss_plat_data *plat_priv, - void *data) +static int cnss_fw_mem_file_save_hdlr(struct cnss_plat_data *plat_priv, + void *data) { - struct cnss_qmi_event_qdss_trace_save_data *event_data = data; - struct cnss_fw_mem *qdss_mem = plat_priv->qdss_mem; - int ret = 0; - int i; + struct cnss_qmi_event_fw_mem_file_save_data *event_data = data; + struct cnss_fw_mem *fw_mem_seg; + int ret = 0L; void *va = NULL; - u64 pa; - u32 size; - int seg_id = 0; + u32 i, fw_mem_seg_len; - if (!plat_priv->qdss_mem_seg_len) { - cnss_pr_err("Memory for QDSS trace is not available\n"); - return -ENOMEM; + switch (event_data->mem_type) { + case QMI_WLFW_MEM_TYPE_DDR_V01: + if (!plat_priv->fw_mem_seg_len) + goto invalid_mem_save; + + fw_mem_seg = plat_priv->fw_mem; + fw_mem_seg_len = plat_priv->fw_mem_seg_len; + break; + case QMI_WLFW_MEM_QDSS_V01: + if (!plat_priv->qdss_mem_seg_len) + goto invalid_mem_save; + + fw_mem_seg = plat_priv->qdss_mem; + fw_mem_seg_len = plat_priv->qdss_mem_seg_len; + break; + default: + goto invalid_mem_save; } - if (event_data->mem_seg_len == 0) { - for (i = 0; i < plat_priv->qdss_mem_seg_len; i++) { - ret = cnss_genl_send_msg(qdss_mem[i].va, - CNSS_GENL_MSG_TYPE_QDSS, - event_data->file_name, - qdss_mem[i].size); - if (ret < 0) { - cnss_pr_err("Fail to save QDSS data: %d\n", - ret); - break; - } + for (i = 0; i < event_data->mem_seg_len; i++) { + va = cnss_get_fw_mem_pa_to_va(fw_mem_seg, fw_mem_seg_len, + event_data->mem_seg[i].addr, + event_data->mem_seg[i].size); + if (!va) { + cnss_pr_err("Fail to find matching va of pa %pa for mem type: %d\n", + &event_data->mem_seg[i].addr, + event_data->mem_type); + ret = -EINVAL; + break; } - } else { - for (i = 0; i < event_data->mem_seg_len; i++) { - pa = event_data->mem_seg[i].addr; - size = event_data->mem_seg[i].size; - va = cnss_qdss_trace_pa_to_va(plat_priv, pa, - size, &seg_id); - if (!va) { - cnss_pr_err("Fail to find matching va for pa %pa\n", - &pa); - ret = -EINVAL; - break; - } - ret = cnss_genl_send_msg(va, CNSS_GENL_MSG_TYPE_QDSS, - event_data->file_name, size); - if (ret < 0) { - cnss_pr_err("Fail to save QDSS data: %d\n", - ret); - break; - } + ret = cnss_genl_send_msg(va, CNSS_GENL_MSG_TYPE_QDSS, + event_data->file_name, + event_data->mem_seg[i].size); + if (ret < 0) { + cnss_pr_err("Fail to save fw mem data: %d\n", + ret); + break; } } - kfree(data); return ret; + +invalid_mem_save: + cnss_pr_err("FW Mem type %d not allocated. Invalid save request\n", + event_data->mem_type); + kfree(data); + return -EINVAL; } static int cnss_qdss_trace_free_hdlr(struct cnss_plat_data *plat_priv) @@ -1689,9 +1689,9 @@ static void cnss_driver_event_work(struct work_struct *work) case CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM: ret = cnss_qdss_trace_req_mem_hdlr(plat_priv); break; - case CNSS_DRIVER_EVENT_QDSS_TRACE_SAVE: - ret = cnss_qdss_trace_save_hdlr(plat_priv, - event->data); + case CNSS_DRIVER_EVENT_FW_MEM_FILE_SAVE: + ret = cnss_fw_mem_file_save_hdlr(plat_priv, + event->data); break; case CNSS_DRIVER_EVENT_QDSS_TRACE_FREE: ret = cnss_qdss_trace_free_hdlr(plat_priv); diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 09c2d2bff74f..8f677f03b7d6 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -259,7 +259,7 @@ enum cnss_driver_event_type { CNSS_DRIVER_EVENT_IMS_WFC_CALL_IND, CNSS_DRIVER_EVENT_WLFW_TWT_CFG_IND, CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM, - CNSS_DRIVER_EVENT_QDSS_TRACE_SAVE, + CNSS_DRIVER_EVENT_FW_MEM_FILE_SAVE, CNSS_DRIVER_EVENT_QDSS_TRACE_FREE, CNSS_DRIVER_EVENT_MAX, }; diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index a8f717503aaa..82e7f897e973 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -1919,63 +1919,73 @@ static void cnss_wlfw_qdss_trace_req_mem_ind_cb(struct qmi_handle *qmi_wlfw, 0, NULL); } -static void cnss_wlfw_qdss_trace_save_ind_cb(struct qmi_handle *qmi_wlfw, - struct sockaddr_qrtr *sq, - struct qmi_txn *txn, - const void *data) +/** + * cnss_wlfw_fw_mem_file_save_ind_cb: Save given FW mem to filesystem + * + * QDSS_TRACE_SAVE_IND feature is overloaded to provide any host allocated + * fw memory segment for dumping to file system. Only one type of mem can be + * saved per indication and is provided in mem seg index 0. + * + * Return: None + */ +static void cnss_wlfw_fw_mem_file_save_ind_cb(struct qmi_handle *qmi_wlfw, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, + const void *data) { struct cnss_plat_data *plat_priv = container_of(qmi_wlfw, struct cnss_plat_data, qmi_wlfw); const struct wlfw_qdss_trace_save_ind_msg_v01 *ind_msg = data; - struct cnss_qmi_event_qdss_trace_save_data *event_data; + struct cnss_qmi_event_fw_mem_file_save_data *event_data; int i = 0; - cnss_pr_dbg("Received QMI WLFW QDSS trace save indication\n"); - - if (!txn) { + if (!txn || !data) { cnss_pr_err("Spurious indication\n"); return; } - - cnss_pr_dbg("QDSS_trace_save info: source %u, total_size %u, file_name_valid %u, file_name %s\n", - ind_msg->source, ind_msg->total_size, - ind_msg->file_name_valid, ind_msg->file_name); - - if (ind_msg->source == 1) + cnss_pr_dbg("QMI fw_mem_file_save: source: %d mem_seg: %d type: %u len: %u\n", + ind_msg->source, ind_msg->mem_seg_valid, + ind_msg->mem_seg[0].type, ind_msg->mem_seg_len); + if (ind_msg->source == 1 || !ind_msg->mem_seg_valid || + !ind_msg->mem_seg_len || + ind_msg->mem_seg_len > QMI_WLFW_MAX_STR_LEN_V01) { + cnss_pr_err("Invalid FW mem file save indication\n"); return; + } event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (!event_data) return; - if (ind_msg->mem_seg_valid) { - if (ind_msg->mem_seg_len > QDSS_TRACE_SEG_LEN_MAX) { - cnss_pr_err("Invalid seg len %u\n", - ind_msg->mem_seg_len); - goto free_event_data; - } - cnss_pr_dbg("QDSS_trace_save seg len %u\n", - ind_msg->mem_seg_len); - event_data->mem_seg_len = ind_msg->mem_seg_len; - for (i = 0; i < ind_msg->mem_seg_len; i++) { - event_data->mem_seg[i].addr = ind_msg->mem_seg[i].addr; - event_data->mem_seg[i].size = ind_msg->mem_seg[i].size; - cnss_pr_dbg("seg-%d: addr 0x%llx size 0x%x\n", - i, ind_msg->mem_seg[i].addr, - ind_msg->mem_seg[i].size); - } - } - + event_data->mem_type = ind_msg->mem_seg[0].type; + event_data->mem_seg_len = ind_msg->mem_seg_len; event_data->total_size = ind_msg->total_size; - if (ind_msg->file_name_valid) - strlcpy(event_data->file_name, ind_msg->file_name, - QDSS_TRACE_FILE_NAME_MAX + 1); - else - strlcpy(event_data->file_name, "qdss_trace", - QDSS_TRACE_FILE_NAME_MAX + 1); + for (i = 0; i < ind_msg->mem_seg_len; i++) { + event_data->mem_seg[i].addr = ind_msg->mem_seg[i].addr; + event_data->mem_seg[i].size = ind_msg->mem_seg[i].size; + if (event_data->mem_type != ind_msg->mem_seg[i].type) { + cnss_pr_err("FW Mem file save ind cannot have multiple mem types\n"); + goto free_event_data; + } + cnss_pr_dbg("seg-%d: addr 0x%llx size 0x%x\n", + i, ind_msg->mem_seg[i].addr, + ind_msg->mem_seg[i].size); + } - cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_QDSS_TRACE_SAVE, + if (ind_msg->file_name_valid) { + strlcpy(event_data->file_name, ind_msg->file_name, + QMI_WLFW_MAX_STR_LEN_V01 + 1); + } else { + if (event_data->mem_type == QMI_WLFW_MEM_QDSS_V01) + strlcpy(event_data->file_name, "qdss_trace", + QMI_WLFW_MAX_STR_LEN_V01 + 1); + else + strlcpy(event_data->file_name, "fw_mem_dump", + QMI_WLFW_MAX_STR_LEN_V01 + 1); + } + + cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_FW_MEM_FILE_SAVE, 0, event_data); return; @@ -2207,7 +2217,7 @@ static struct qmi_msg_handler qmi_wlfw_msg_handlers[] = { .ei = wlfw_qdss_trace_save_ind_msg_v01_ei, .decoded_size = sizeof(struct wlfw_qdss_trace_save_ind_msg_v01), - .fn = cnss_wlfw_qdss_trace_save_ind_cb + .fn = cnss_wlfw_fw_mem_file_save_ind_cb }, { .type = QMI_INDICATION, diff --git a/drivers/net/wireless/cnss2/qmi.h b/drivers/net/wireless/cnss2/qmi.h index 4d74ad11dc33..0583f2c76f99 100644 --- a/drivers/net/wireless/cnss2/qmi.h +++ b/drivers/net/wireless/cnss2/qmi.h @@ -13,19 +13,17 @@ struct cnss_qmi_event_server_arrive_data { unsigned int port; }; -#define QDSS_TRACE_SEG_LEN_MAX 32 -#define QDSS_TRACE_FILE_NAME_MAX 16 - struct cnss_mem_seg { u64 addr; u32 size; }; -struct cnss_qmi_event_qdss_trace_save_data { +struct cnss_qmi_event_fw_mem_file_save_data { u32 total_size; u32 mem_seg_len; - struct cnss_mem_seg mem_seg[QDSS_TRACE_SEG_LEN_MAX]; - char file_name[QDSS_TRACE_FILE_NAME_MAX + 1]; + enum wlfw_mem_type_enum_v01 mem_type; + struct cnss_mem_seg mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; + char file_name[QMI_WLFW_MAX_STR_LEN_V01 + 1]; }; #ifdef CONFIG_CNSS2_QMI