diff options
| author | Krishna Kumaar Natarajan <kknatara@qca.qualcomm.com> | 2015-04-16 12:29:20 -0700 |
|---|---|---|
| committer | AnjaneeDevi Kapparapu <c_akappa@qti.qualcomm.com> | 2015-04-24 19:52:35 +0530 |
| commit | bdf0f67439baf5dec556eca56607f01cb420dbe0 (patch) | |
| tree | 65f90fcdfbb3fb9189b959ceea9b4cda69b8978a | |
| parent | 30f08dba71d9ec8cf08ef0e43d015eea162d148d (diff) | |
qcacld: Support to send memory dump to user space
Add support to send firmware memory dump to user space
application for debugging
Change-Id: I426be14c701d9d523b30704580700cc9914d4f66
CRs-Fixed: 822917
| -rw-r--r-- | CORE/HDD/inc/wlan_hdd_cfg80211.h | 3 | ||||
| -rw-r--r-- | CORE/HDD/inc/wlan_hdd_main.h | 7 | ||||
| -rw-r--r-- | CORE/HDD/inc/wlan_hdd_memdump.h | 72 | ||||
| -rw-r--r-- | CORE/HDD/src/wlan_hdd_cfg80211.c | 18 | ||||
| -rwxr-xr-x | CORE/HDD/src/wlan_hdd_main.c | 6 | ||||
| -rw-r--r-- | CORE/HDD/src/wlan_hdd_memdump.c | 507 | ||||
| -rw-r--r-- | CORE/MAC/inc/sirApi.h | 55 | ||||
| -rw-r--r-- | CORE/MAC/inc/wniApi.h | 1 | ||||
| -rw-r--r-- | CORE/MAC/src/include/sirParams.h | 2 | ||||
| -rw-r--r-- | CORE/SERVICES/WMA/wma.c | 204 | ||||
| -rw-r--r-- | CORE/SME/inc/smeInternal.h | 3 | ||||
| -rw-r--r-- | CORE/SME/inc/sme_Api.h | 5 | ||||
| -rw-r--r-- | CORE/SME/src/sme_common/sme_Api.c | 173 | ||||
| -rw-r--r-- | CORE/WDA/inc/wlan_qct_wda.h | 2 | ||||
| -rw-r--r-- | Kbuild | 10 | ||||
| -rw-r--r-- | Kconfig | 4 |
16 files changed, 1071 insertions, 1 deletions
diff --git a/CORE/HDD/inc/wlan_hdd_cfg80211.h b/CORE/HDD/inc/wlan_hdd_cfg80211.h index 571ec6c03852..f55459cc97f6 100644 --- a/CORE/HDD/inc/wlan_hdd_cfg80211.h +++ b/CORE/HDD/inc/wlan_hdd_cfg80211.h @@ -301,6 +301,9 @@ enum qca_nl80211_vendor_subcmds_index { #endif /* OCB events */ QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT_INDEX, +#ifdef WLAN_FEATURE_MEMDUMP + QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP_INDEX, +#endif /* WLAN_FEATURE_MEMDUMP */ }; /* EXT TDLS */ diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h index 061a2d337a5a..01a01a6b4d82 100644 --- a/CORE/HDD/inc/wlan_hdd_main.h +++ b/CORE/HDD/inc/wlan_hdd_main.h @@ -1512,6 +1512,13 @@ struct hdd_context_s struct hdd_ll_stats_context ll_stats_context; #endif /* End of WLAN_FEATURE_LINK_LAYER_STATS */ +#ifdef WLAN_FEATURE_MEMDUMP + uint8_t *fw_dump_loc; + uint32_t dump_loc_paddr; + vos_timer_t memdump_cleanup_timer; + struct mutex memdump_lock; + bool memdump_in_progress; +#endif /* WLAN_FEATURE_MEMDUMP */ }; /*--------------------------------------------------------------------------- diff --git a/CORE/HDD/inc/wlan_hdd_memdump.h b/CORE/HDD/inc/wlan_hdd_memdump.h new file mode 100644 index 000000000000..9cf488067830 --- /dev/null +++ b/CORE/HDD/inc/wlan_hdd_memdump.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015 Qualcomm Atheros, Inc. + * All Rights Reserved. + * Qualcomm Atheros Confidential and Proprietary. + * + */ + +/** + * DOC : wlan_hdd_memdump.h + * + * WLAN Host Device Driver file for dumping firmware memory + * + */ + +#if !defined(WLAN_HDD_MEMDUMP_H) +#define WLAN_HDD_MEMDUMP_H + +#include "wlan_hdd_main.h" + +#ifdef WLAN_FEATURE_MEMDUMP +/** + * enum qca_wlan_vendor_attr_memory_dump - values for memory dump attributes + * @QCA_WLAN_VENDOR_ATTR_MEMORY_DUMP_INVALID - Invalid + * @QCA_WLAN_VENDOR_ATTR_REQUEST_ID - Indicate request ID + * @QCA_WLAN_VENDOR_ATTR_MEMDUMP_SIZE - Indicate size of the memory dump + * @QCA_WLAN_VENDOR_ATTR_MEMORY_DUMP_AFTER_LAST - To keep track of the last enum + * @QCA_WLAN_VENDOR_ATTR_MEMORY_DUMP_MAX - max value possible for this type + * + * enum values are used for NL attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP sub command. + */ +enum qca_wlan_vendor_attr_memory_dump { + QCA_WLAN_VENDOR_ATTR_MEMORY_DUMP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_REQUEST_ID = 1, + QCA_WLAN_VENDOR_ATTR_MEMDUMP_SIZE = 2, + + QCA_WLAN_VENDOR_ATTR_MEMORY_DUMP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MEMORY_DUMP_MAX = + QCA_WLAN_VENDOR_ATTR_MEMORY_DUMP_AFTER_LAST - 1, +}; + +/* Size of fw memory dump is estimated to be 327680 bytes */ +#define FW_MEM_DUMP_SIZE 327680 +#define FW_DRAM_LOCATION 0x00400000 +#define FW_MEM_DUMP_REQ_ID 1 +#define FW_MEM_DUMP_NUM_SEG 1 +#define MEMDUMP_COMPLETION_TIME_MS 5000 + +int memdump_init(void); +void memdump_deinit(void); +int wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); +#else +static inline int memdump_init(void) +{ + return -ENOTSUPP; +} + +static inline void memdump_deinit(void) +{ +} + +static inline int wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + return -ENOTSUPP; +} +#endif + +#endif /* if !defined(WLAN_HDD_MEMDUMP_H)*/ diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c index f764deb02b10..3140a4c9f924 100644 --- a/CORE/HDD/src/wlan_hdd_cfg80211.c +++ b/CORE/HDD/src/wlan_hdd_cfg80211.c @@ -109,6 +109,8 @@ #include "wlan_hdd_ocb.h" #include "qwlan_version.h" +#include "wlan_hdd_memdump.h" + #define g_mode_rates_size (12) #define a_mode_rates_size (8) #define FREQ_BASE_80211G (2407) @@ -1223,6 +1225,12 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] = .vendor_id = QCA_NL80211_VENDOR_ID, .subcmd = QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT }, +#ifdef WLAN_FEATURE_MEMDUMP + [QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP + }, +#endif /* WLAN_FEATURE_MEMDUMP */ }; static int is_driver_dfs_capable(struct wiphy *wiphy, @@ -7342,6 +7350,16 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = .doit = wlan_hdd_cfg80211_get_logger_supp_feature }, +#ifdef WLAN_FEATURE_MEMDUMP + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlan_hdd_cfg80211_get_fw_mem_dump + }, +#endif /* WLAN_FEATURE_MEMDUMP */ }; diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c index 4e30b30eb7a2..c4e50fa14e45 100755 --- a/CORE/HDD/src/wlan_hdd_main.c +++ b/CORE/HDD/src/wlan_hdd_main.c @@ -110,6 +110,8 @@ #ifdef CONFIG_CNSS #include <net/cnss.h> #endif +#include "wlan_hdd_memdump.h" + extern int hdd_hostapd_stop (struct net_device *dev); void hdd_ch_avoid_cb(void *hdd_context,void *indi_param); #endif /* FEATURE_WLAN_CH_AVOID */ @@ -12282,6 +12284,7 @@ static int hdd_driver_init( void) break; } else { pr_info("%s: driver loaded\n", WLAN_MODULE_NAME); + memdump_init(); return 0; } @@ -12302,7 +12305,7 @@ static int hdd_driver_init( void) #ifdef WLAN_LOGGING_SOCK_SVC_ENABLE wlan_logging_sock_deinit_svc(); #endif - + memdump_deinit(); pr_err("%s: driver load failure\n", WLAN_MODULE_NAME); } else @@ -12408,6 +12411,7 @@ static void hdd_driver_exit(void) } vos_wait_for_work_thread_completion(__func__); + memdump_deinit(); hif_unregister_driver(); diff --git a/CORE/HDD/src/wlan_hdd_memdump.c b/CORE/HDD/src/wlan_hdd_memdump.c new file mode 100644 index 000000000000..399576b5f7ba --- /dev/null +++ b/CORE/HDD/src/wlan_hdd_memdump.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2015 Qualcomm Atheros, Inc. + * All Rights Reserved. + * Qualcomm Atheros Confidential and Proprietary. + * + */ + +/** + * DOC : wlan_hdd_memdump.c + * + * WLAN Host Device Driver file for dumping firmware memory + * + */ + +#include <sme_Api.h> +#include <wlan_hdd_includes.h> +#include "wlan_hdd_memdump.h" + +/** + * memdump_cleanup_timer_cb() - Timer callback function for memory dump cleanup. + * + * @data: Callback data (used to stored HDD context) + * + * Callback function registered for memory dump cleanup VOS timer. + * + * Return: none + */ + +static void memdump_cleanup_timer_cb(void *data) +{ + int status; + hdd_context_t *hdd_ctx = data; + adf_os_dma_addr_t paddr; + adf_os_dma_addr_t dma_ctx; + adf_os_device_t adf_ctx; + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) { + hddLog(LOGE, FL("HDD context is not valid")); + return; + } + + if (!hdd_ctx->fw_dump_loc) { + hddLog(LOG1, FL("Memory dump already freed")); + return; + } + + adf_ctx = vos_get_context(VOS_MODULE_ID_ADF, hdd_ctx->pvosContext); + if (!adf_ctx) { + hddLog(LOGE, FL("ADF context is NULL")); + return; + } + + paddr = hdd_ctx->dump_loc_paddr; + mutex_lock(&hdd_ctx->memdump_lock); + adf_os_mem_free_consistent(adf_ctx, + FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx); + hdd_ctx->fw_dump_loc = NULL; + hdd_ctx->memdump_in_progress = false; + mutex_unlock(&hdd_ctx->memdump_lock); + +} + +/** + * wlan_hdd_cfg80211_fw_mem_dump_cb() - Callback to receive FW memory dump + * @ctx: pointer to HDD context. + * @rsp: pointer to fw dump copy complete response + * + * This is a callback function used to indicate user space about the + * availability for firmware memory dump via vendor event. + * + * Return: None + */ +static void wlan_hdd_cfg80211_fw_mem_dump_cb(void *ctx, + struct fw_dump_rsp *dump_rsp) +{ + hdd_context_t *pHddCtx = (hdd_context_t *)ctx; + int status; + struct sk_buff *skb = NULL; + + status = wlan_hdd_validate_context(pHddCtx); + if (0 != status) { + hddLog(LOGE, FL("HDD context is not valid")); + return; + } + + if (!dump_rsp->dump_complete) { + hddLog(LOGE, FL("fw dump copy failed status from FW.")); + return; + } + + skb = cfg80211_vendor_event_alloc(pHddCtx->wiphy, + NULL, sizeof(uint32_t) + NLA_HDRLEN + NLMSG_HDRLEN, + QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP_INDEX, + GFP_KERNEL); + + if (!skb) { + hddLog(LOGE, FL("cfg80211_vendor_event_alloc failed")); + return; + } + + if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MEMDUMP_SIZE, + FW_MEM_DUMP_SIZE)) { + hddLog(LOGE, FL("nla put fail")); + goto nla_put_failure; + } + + cfg80211_vendor_event(skb, GFP_KERNEL); + hddLog(LOG1, FL("Memdump event sent successfully to user space")); + return; + +nla_put_failure: + kfree_skb(skb); + return; +} + +/** + * wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump + * @wiphy: pointer to wireless wiphy structure. + * @wdev: pointer to wireless_dev structure. + * @data: Pointer to the NL data. + * @data_len:Length of @data + * + * This is called when wlan driver needs to get the firmware memory dump + * via vendor specific command. + * + * Return: 0 on success, error number otherwise. + */ +int wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + int status; + VOS_STATUS sme_status; + hdd_context_t *hdd_ctx = wiphy_priv(wiphy); + struct fw_dump_req fw_mem_dump_req; + struct fw_dump_seg_req* seg_req; + uint8_t loop; + adf_os_dma_addr_t paddr; + adf_os_dma_addr_t dma_ctx; + adf_os_device_t adf_ctx; + + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) { + hddLog(LOGE, FL("HDD context is invalid")); + return status; + } + + adf_ctx = vos_get_context(VOS_MODULE_ID_ADF, hdd_ctx->pvosContext); + if (!adf_ctx) { + hddLog(LOGE, FL("ADF context is NULL")); + return -EINVAL; + } + + if (hdd_ctx->memdump_in_progress) { + hddLog(LOGE, FL("Already a memdump req in progress.")); + return -EBUSY; + } + + /* + * Allocate memory for fw memory dump. Memory allocated should be + * contiguous. Physical address of the allocated memory is passed + * to the FW for copy + * + * Reuse the memory if available. + */ + mutex_lock(&hdd_ctx->memdump_lock); + if (!hdd_ctx->fw_dump_loc) { + hdd_ctx->fw_dump_loc = adf_os_mem_alloc_consistent( + adf_ctx, FW_MEM_DUMP_SIZE, &paddr, dma_ctx); + if (!hdd_ctx->fw_dump_loc) { + mutex_unlock(&hdd_ctx->memdump_lock); + hddLog(LOGE, FL("adf_os_mem_alloc_consistent failed")); + return -ENOMEM; + } + hdd_ctx->dump_loc_paddr = paddr; + } + mutex_unlock(&hdd_ctx->memdump_lock); + + /* + * Currently request_id and num_seg is assumed to be default(1) + * It is assumed that firmware dump requested is for DRAM section + * only + */ + + fw_mem_dump_req.request_id = FW_MEM_DUMP_REQ_ID; + fw_mem_dump_req.num_seg = FW_MEM_DUMP_NUM_SEG; + + hddLog(LOG1, FL("request_id:%d num_seg:%d"), + fw_mem_dump_req.request_id, fw_mem_dump_req.num_seg); + seg_req = (struct fw_dump_seg_req *) fw_mem_dump_req.segment; + for (loop = 0; loop < fw_mem_dump_req.num_seg; loop++) { + seg_req->seg_id = 1; + seg_req->seg_start_addr_lo = FW_DRAM_LOCATION; + seg_req->seg_start_addr_hi = 0; + seg_req->seg_length = FW_MEM_DUMP_SIZE; + seg_req->dst_addr_lo = hdd_ctx->dump_loc_paddr; + seg_req->dst_addr_hi = 0; + hddLog(LOG1, FL("seg_number:%d"), loop); + hddLog(LOG1, + FL("seg_id:%d start_addr_lo:0x%x start_addr_hi:0x%x"), + seg_req->seg_id, seg_req->seg_start_addr_lo, + seg_req->seg_start_addr_hi); + hddLog(LOG1, + FL("seg_length:%d dst_addr_lo:0x%x dst_addr_hi:0x%x"), + seg_req->seg_length, seg_req->dst_addr_lo, + seg_req->dst_addr_hi); + seg_req++; + } + + /** + * Start the cleanup timer. + * Memory allocated for this request will be freed up + * once the timer expires. Memory dump request is expected to be + * completed by this time. + * + * User space will not be able to access the dump after this time. + * New request should be issued to get the dump again. + */ + vos_timer_start(&hdd_ctx->memdump_cleanup_timer, + MEMDUMP_COMPLETION_TIME_MS); + hdd_ctx->memdump_in_progress = true; + + sme_status = sme_fw_mem_dump(hdd_ctx->hHal, &fw_mem_dump_req); + if (VOS_STATUS_SUCCESS != sme_status) { + hddLog(LOGE, FL("sme_fw_mem_dump Failed")); + mutex_lock(&hdd_ctx->memdump_lock); + adf_os_mem_free_consistent(adf_ctx, + FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx); + hdd_ctx->fw_dump_loc = NULL; + mutex_unlock(&hdd_ctx->memdump_lock); + hdd_ctx->memdump_in_progress = false; + if (VOS_TIMER_STATE_RUNNING == + vos_timer_getCurrentState(&hdd_ctx->memdump_cleanup_timer)) { + vos_timer_stop(&hdd_ctx->memdump_cleanup_timer); + } + return -EINVAL; + } + + return 0; +} + +#define PROCFS_MEMDUMP_DIR "debug" +#define PROCFS_MEMDUMP_NAME "fwdump" + +static struct proc_dir_entry *proc_file, *proc_dir; + +/** + * memdump_read() - perform read operation in memory dump proc file + * + * @file - handle for the proc file. + * @buf - pointer to user space buffer. + * @count - number of bytes to be read. + * @pos - offset in the from buffer. + * + * This function performs read operation for the memory dump proc file. + * + * Return: number of bytes read on success, error code otherwise. + */ +static ssize_t memdump_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + int status; + hdd_context_t *hdd_ctx = (hdd_context_t *)PDE_DATA(file_inode(file)); + adf_os_dma_addr_t paddr; + adf_os_dma_addr_t dma_ctx; + adf_os_device_t adf_ctx; + + hddLog(LOG1, FL("Read req for size:%zu pos:%llu"), count, *pos); + status = wlan_hdd_validate_context(hdd_ctx); + if (0 != status) { + hddLog(LOGE, FL("HDD context is not valid")); + return -EINVAL; + } + adf_ctx = vos_get_context(VOS_MODULE_ID_ADF, hdd_ctx->pvosContext); + if (!adf_ctx) { + hddLog(LOGE, FL("ADF context is NULL")); + return -EINVAL; + } + + if (!hdd_ctx->memdump_in_progress) { + hddLog(LOGE, FL("Current mem dump request timed out/failed")); + return -EINVAL; + } + + if (*pos < 0) { + hddLog(LOGE, FL("Invalid start offset for memdump read")); + return -EINVAL; + } else if (*pos >= FW_MEM_DUMP_SIZE || !count) { + hddLog(LOGE, FL("No more data to copy")); + return 0; + } else if (count > FW_MEM_DUMP_SIZE - *pos) { + count = FW_MEM_DUMP_SIZE - *pos; + } + + if (!hdd_ctx->fw_dump_loc) { + hddLog(LOGE, FL("Invalid fw mem dump location")); + return -EINVAL; + } + + if (copy_to_user(buf, hdd_ctx->fw_dump_loc + *pos, count)) { + hddLog(LOGE, FL("copy to user space failed")); + return -EFAULT; + } + + /* offset(pos) should be updated here based on the copy done*/ + *pos += count; + + /* Entire FW memory dump copy completed */ + if (*pos >= FW_MEM_DUMP_SIZE) { + paddr = hdd_ctx->dump_loc_paddr; + mutex_lock(&hdd_ctx->memdump_lock); + adf_os_mem_free_consistent(adf_ctx, + FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx); + hdd_ctx->fw_dump_loc = NULL; + hdd_ctx->memdump_in_progress = false; + if (VOS_TIMER_STATE_RUNNING == + vos_timer_getCurrentState(&hdd_ctx->memdump_cleanup_timer)) { + vos_timer_stop(&hdd_ctx->memdump_cleanup_timer); + } + mutex_unlock(&hdd_ctx->memdump_lock); + } + + return count; +} + +/** + * struct memdump_fops - file operations for memory dump feature + * @read - read function for memory dump operation. + * + * This structure initialize the file operation handle for memory + * dump feature + */ +static const struct file_operations memdump_fops = { + read: memdump_read +}; + +/** + * memdump_procfs_init() - Initialize procfs for memory dump + * + * @vos_ctx - Global vos context. + * + * This function create file under proc file system to be used later for + * processing firmware memory dump + * + * Return: 0 on success, error code otherwise. + */ +static int memdump_procfs_init(void *vos_ctx) +{ + hdd_context_t *hdd_ctx; + + hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); + if (!hdd_ctx) { + hddLog(LOGE , FL("Invalid HDD context")); + return -EINVAL; + } + + proc_dir = proc_mkdir(PROCFS_MEMDUMP_DIR, NULL); + if (proc_dir == NULL) { + remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL); + pr_debug("Error: Could not initialize /proc/%s\n", + PROCFS_MEMDUMP_DIR); + return -ENOMEM; + } + + proc_file = proc_create_data(PROCFS_MEMDUMP_NAME, + S_IRUSR | S_IWUSR, proc_dir, + &memdump_fops, hdd_ctx); + if (proc_file == NULL) { + remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir); + pr_debug("Error: Could not initialize /proc/%s\n", + PROCFS_MEMDUMP_NAME); + return -ENOMEM; + } + + pr_debug("/proc/%s/%s created\n", PROCFS_MEMDUMP_DIR, + PROCFS_MEMDUMP_NAME); + return 0; +} + +/** + * memdump_procfs_remove() - Remove file/dir under procfs for memory dump + * + * This function removes file/dir under proc file system that was + * processing firmware memory dump + * + * Return: None + */ +static void memdump_procfs_remove(void) +{ + remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir); + pr_debug("/proc/%s/%s removed\n", PROCFS_MEMDUMP_DIR, + PROCFS_MEMDUMP_NAME); + remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL); + pr_debug("/proc/%s removed\n", PROCFS_MEMDUMP_DIR); +} + +/** + * memdump_init() - Intialization function for memory dump feature + * + * This function creates proc file for memdump feature and registers + * HDD callback function with SME. + * + * Return - 0 on success, error otherwise + */ +int memdump_init(void) +{ + hdd_context_t *hdd_ctx; + void *vos_ctx; + int status = 0; + eHalStatus cb_status; + VOS_STATUS vos_status; + + vos_ctx = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); + if (!vos_ctx) { + hddLog(LOGE, FL("Invalid VOS context")); + return -EINVAL; + } + + hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); + if (!hdd_ctx) { + hddLog(LOGE , FL("Invalid HDD context")); + return -EINVAL; + } + + cb_status = sme_fw_mem_dump_register_cb(hdd_ctx->hHal, + wlan_hdd_cfg80211_fw_mem_dump_cb); + if (eHAL_STATUS_SUCCESS != cb_status) { + hddLog(LOGE , FL("Failed to register the callback")); + return -EINVAL; + } + + status = memdump_procfs_init(vos_ctx); + if (status) { + hddLog(LOGE , FL("Failed to create proc file")); + return status; + } + + vos_status = vos_timer_init(&hdd_ctx->memdump_cleanup_timer, + VOS_TIMER_TYPE_SW, memdump_cleanup_timer_cb, + (void *)hdd_ctx); + if (!VOS_IS_STATUS_SUCCESS(vos_status)) { + hddLog(LOGE, FL("Failed to init memdump cleanup timer")); + return -EINVAL; + } + + mutex_init(&hdd_ctx->memdump_lock); + + return 0; +} + +/** + * memdump_deinit() - De initialize memdump feature + * + * This function removes proc file created for memdump feature. + * + * Return: None + */ +void memdump_deinit(void) { + hdd_context_t *hdd_ctx; + void *vos_ctx; + adf_os_dma_addr_t paddr; + adf_os_dma_addr_t dma_ctx; + adf_os_device_t adf_ctx; + VOS_STATUS vos_status; + + vos_ctx = vos_get_global_context(VOS_MODULE_ID_SYS, NULL); + if (!vos_ctx) { + hddLog(LOGE, FL("Invalid VOS context")); + return; + } + + hdd_ctx = vos_get_context(VOS_MODULE_ID_HDD, vos_ctx); + if(!hdd_ctx) { + hddLog(LOGE , FL("Invalid HDD context")); + return; + } + + adf_ctx = vos_get_context(VOS_MODULE_ID_ADF, hdd_ctx->pvosContext); + if (!adf_ctx) { + hddLog(LOGE, FL("ADF context is NULL")); + return; + } + + memdump_procfs_remove(); + sme_fw_mem_dump_unregister_cb(hdd_ctx->hHal); + + mutex_lock(&hdd_ctx->memdump_lock); + if (hdd_ctx->fw_dump_loc) { + paddr = hdd_ctx->dump_loc_paddr; + adf_os_mem_free_consistent(adf_ctx, + FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx); + hdd_ctx->fw_dump_loc = NULL; + hdd_ctx->memdump_in_progress = false; + } + mutex_unlock(&hdd_ctx->memdump_lock); + + if (VOS_TIMER_STATE_RUNNING == + vos_timer_getCurrentState(&hdd_ctx->memdump_cleanup_timer)) { + vos_timer_stop(&hdd_ctx->memdump_cleanup_timer); + } + + vos_status = vos_timer_destroy(&hdd_ctx->memdump_cleanup_timer); + if (!VOS_IS_STATUS_SUCCESS(vos_status)) { + hddLog(LOGE, FL("Failed to deallocate timer")); + } +} diff --git a/CORE/MAC/inc/sirApi.h b/CORE/MAC/inc/sirApi.h index 8e85808b4c03..bd16fe8bbdad 100644 --- a/CORE/MAC/inc/sirApi.h +++ b/CORE/MAC/inc/sirApi.h @@ -6053,4 +6053,59 @@ struct sir_guard_time_request { /* Max number of rates allowed in Supported Rates IE */ #define MAX_NUM_SUPPORTED_RATES (8) +#define MAX_NUM_FW_SEGMENTS 4 + +/** + * struct fw_dump_seg_req - individual segment details + * @seg_id - segment id. + * @seg_start_addr_lo - lower address of the segment. + * @seg_start_addr_hi - higher address of the segment. + * @seg_length - length of the segment. + * @dst_addr_lo - lower address of the destination buffer. + * @dst_addr_hi - higher address of the destination buffer. + * + * This structure carries the information to firmware about the + * individual segments. This structure is part of firmware memory + * dump request. + */ +struct fw_dump_seg_req +{ + uint8_t seg_id; + uint32_t seg_start_addr_lo; + uint32_t seg_start_addr_hi; + uint32_t seg_length; + uint32_t dst_addr_lo; + uint32_t dst_addr_hi; +}; + +/** + * struct fw_dump_req - firmware memory dump request details. + * @request_id - request id. + * @num_seg - requested number of segments. + * @fw_dump_seg_req - individual segment information. + * + * This structure carries information about the firmware + * memory dump request. + */ +struct fw_dump_req +{ + uint32_t request_id; + uint32_t num_seg; + struct fw_dump_seg_req segment[MAX_NUM_FW_SEGMENTS]; +}; + +/** + * struct fw_dump_rsp - firmware dump response details. + * @request_id - request id. + * @dump_complete - copy completion status. + * + * This structure is used to store the firmware dump copy complete + * response from the firmware. + */ +struct fw_dump_rsp +{ + uint32_t request_id; + uint32_t dump_complete; +}; + #endif /* __SIR_API_H */ diff --git a/CORE/MAC/inc/wniApi.h b/CORE/MAC/inc/wniApi.h index 553711dc386b..89896b527d73 100644 --- a/CORE/MAC/inc/wniApi.h +++ b/CORE/MAC/inc/wniApi.h @@ -392,6 +392,7 @@ enum eWniMsgTypes eWNI_SME_DCC_STATS_EVENT, eWNI_SME_TSF_EVENT, + eWNI_SME_FW_DUMP_IND, eWNI_SME_MSG_TYPES_END }; diff --git a/CORE/MAC/src/include/sirParams.h b/CORE/MAC/src/include/sirParams.h index c44f4a052b76..785968b5fb20 100644 --- a/CORE/MAC/src/include/sirParams.h +++ b/CORE/MAC/src/include/sirParams.h @@ -708,6 +708,8 @@ typedef struct sSirMbMsgP2p #define SIR_HAL_DCC_CLEAR_STATS_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 325) #define SIR_HAL_DCC_UPDATE_NDL_CMD (SIR_HAL_ITC_MSG_TYPES_BEGIN + 326) +#define SIR_HAL_FW_MEM_DUMP_REQ (SIR_HAL_ITC_MSG_TYPES_BEGIN + 327) + #define SIR_HAL_MSG_TYPES_END (SIR_HAL_MSG_TYPES_BEGIN + 0x1FF) // CFG message types diff --git a/CORE/SERVICES/WMA/wma.c b/CORE/SERVICES/WMA/wma.c index 581cd7eaf785..22c3b5165dca 100644 --- a/CORE/SERVICES/WMA/wma.c +++ b/CORE/SERVICES/WMA/wma.c @@ -2353,6 +2353,50 @@ static void wma_post_link_status(tAniGetLinkStatus *pGetLinkStatus, } } +#ifdef WLAN_FEATURE_MEMDUMP +/** + * wma_fw_mem_dump_rsp() - send fw mem dump response to SME + * + * @req_id - request id. + * @status - copy status from the firmware. + * + * This function is called by the memory dump response handler to + * indicate SME that firmware dump copy is complete + */ +static VOS_STATUS wma_fw_mem_dump_rsp(uint32_t req_id, uint32_t status) +{ + struct fw_dump_rsp *dump_rsp; + vos_msg_t sme_msg = {0} ; + VOS_STATUS vos_status = VOS_STATUS_SUCCESS; + + dump_rsp = vos_mem_malloc(sizeof(*dump_rsp)); + + if (!dump_rsp) { + WMA_LOGE(FL("Memory allocation failed.")); + vos_status = VOS_STATUS_E_NOMEM; + return vos_status; + } + + WMA_LOGI(FL("FW memory dump copy complete status: %d for request: %d"), + status, req_id); + + dump_rsp->request_id = req_id; + dump_rsp->dump_complete = status; + + sme_msg.type = eWNI_SME_FW_DUMP_IND; + sme_msg.bodyptr = dump_rsp; + sme_msg.bodyval = 0; + + vos_status = vos_mq_post_message(VOS_MODULE_ID_SME, &sme_msg); + if (!VOS_IS_STATUS_SUCCESS(vos_status)) { + WMA_LOGE(FL("Fail to post fw mem dump ind msg")); + vos_mem_free(dump_rsp); + } + + return vos_status; +} +#endif /* WLAN_FEATURE_MEMDUMP */ + static int wma_link_status_rsp(tp_wma_handle wma, u_int8_t *buf) { wmi_vdev_rate_stats_event_fixed_param *event; @@ -4019,6 +4063,49 @@ static int wma_unified_link_radio_stats_event_handler(void *handle, #endif /* WLAN_FEATURE_LINK_LAYER_STATS */ +/** + * wma_fw_mem_dump_event_handler() - handles fw memory dump event + * + * handle - pointer to wma handle. + * cmd_param_info - pointer to TLV info received in the event. + * len - length of data in @cmd_param_info + * + * This function is a handler for firmware memory dump event. + */ +#ifdef WLAN_FEATURE_MEMDUMP +static int wma_fw_mem_dump_event_handler(void *handle, u_int8_t *cmd_param_info, + u_int32_t len) +{ + WMI_UPDATE_FW_MEM_DUMP_EVENTID_param_tlvs *param_buf; + wmi_update_fw_mem_dump_fixed_param *event; + VOS_STATUS status; + + param_buf = + (WMI_UPDATE_FW_MEM_DUMP_EVENTID_param_tlvs *) cmd_param_info; + if (!param_buf) { + WMA_LOGA("%s: Invalid stats event", __func__); + return -EINVAL; + } + + event = param_buf->fixed_param; + + status = wma_fw_mem_dump_rsp(event->request_id, + event->fw_mem_dump_complete); + if (VOS_STATUS_SUCCESS != status) { + return -EINVAL; + } + + WMA_LOGI("FW MEM DUMP RSP posted successfully"); + return 0; +} +#else +static int wma_fw_mem_dump_event_handler(void *handle, u_int8_t *cmd_param_info, + u_int32_t len) +{ + return 0; +} +#endif /* WLAN_FEATURE_MEMDUMP */ + u_int8_t *wma_add_p2p_ie(u_int8_t *frm) { u_int8_t wfa_oui[3] = WMA_P2P_WFA_OUI; @@ -6014,6 +6101,12 @@ VOS_STATUS WDA_open(v_VOID_t *vos_context, v_VOID_t *os_ctx, wma_register_ll_stats_event_handler(wma_handle); #endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + /* Register event handler to receive firmware mem dump + * copy complete indication + */ + wmi_unified_register_event_handler(wma_handle->wmi_handle, + WMI_UPDATE_FW_MEM_DUMP_EVENTID, + wma_fw_mem_dump_event_handler); /* Firmware debug log */ vos_status = dbglog_init(wma_handle->wmi_handle); @@ -24464,6 +24557,112 @@ VOS_STATUS wma_config_guard_time(tp_wma_handle wma, return ret; } +#ifdef WLAN_FEATURE_MEMDUMP +/* + * wma_process_fw_mem_dump_req() - Function to request fw memory dump from + * firmware + * @wma: Pointer to WMA handle + * @mem_dump_req: Pointer for mem_dump_req + * + * This function sends memory dump request to firmware + * + * Return: VOS_STATUS_SUCCESS for success otherwise failure + * + */ +static VOS_STATUS wma_process_fw_mem_dump_req(tp_wma_handle wma, + struct fw_dump_req* mem_dump_req) +{ + wmi_get_fw_mem_dump_fixed_param *cmd; + wmi_fw_mem_dump *dump_params; + struct fw_dump_seg_req *seg_req; + int32_t len; + wmi_buf_t buf; + u_int8_t *buf_ptr; + int ret, loop; + + if (!mem_dump_req || !wma) { + WMA_LOGE(FL("input pointer is NULL")); + return VOS_STATUS_E_FAILURE; + } + + /* + * len = sizeof(fixed param) that includes tlv header + + * tlv header for array of struc + + * sizeof (each struct) + */ + len = sizeof(*cmd) + WMI_TLV_HDR_SIZE; + len += mem_dump_req->num_seg * sizeof(wmi_fw_mem_dump); + buf = wmi_buf_alloc(wma->wmi_handle, len); + + if (!buf) { + WMA_LOGE(FL("Failed allocate wmi buffer")); + return VOS_STATUS_E_NOMEM; + } + + buf_ptr = (u_int8_t *) wmi_buf_data(buf); + vos_mem_zero(buf_ptr, len); + cmd = (wmi_get_fw_mem_dump_fixed_param *) buf_ptr; + + WMITLV_SET_HDR(&cmd->tlv_header, + WMITLV_TAG_STRUC_wmi_get_fw_mem_dump_fixed_param, + WMITLV_GET_STRUCT_TLVLEN(wmi_get_fw_mem_dump_fixed_param)); + + cmd->request_id = mem_dump_req->request_id; + cmd->num_fw_mem_dump_segs = mem_dump_req->num_seg; + + /* TLV indicating array of structures to follow */ + buf_ptr += sizeof(wmi_get_fw_mem_dump_fixed_param); + WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC, + sizeof(wmi_fw_mem_dump) * + cmd->num_fw_mem_dump_segs); + + buf_ptr += WMI_TLV_HDR_SIZE; + dump_params = (wmi_fw_mem_dump *) buf_ptr; + + WMA_LOGI(FL("request_id:%d num_seg:%d"), + mem_dump_req->request_id, mem_dump_req->num_seg); + for (loop = 0; loop < cmd->num_fw_mem_dump_segs; loop++) { + seg_req = (struct fw_dump_seg_req *) + ((uint8_t *)(mem_dump_req->segment) + + loop * sizeof(*seg_req)); + WMITLV_SET_HDR(&dump_params->tlv_header, + WMITLV_TAG_STRUC_wmi_fw_mem_dump_params, + WMITLV_GET_STRUCT_TLVLEN(wmi_fw_mem_dump)); + dump_params->seg_id = seg_req->seg_id; + dump_params->seg_start_addr_lo = seg_req-> seg_start_addr_lo; + dump_params->seg_start_addr_hi = seg_req->seg_start_addr_hi; + dump_params->seg_length = seg_req->seg_length; + dump_params->dest_addr_lo = seg_req->dst_addr_lo; + dump_params->dest_addr_hi = seg_req->dst_addr_hi; + WMA_LOGI(FL("seg_number:%d"), loop); + WMA_LOGI(FL("seg_id:%d start_addr_lo:0x%x start_addr_hi:0x%x"), + dump_params->seg_id, dump_params->seg_start_addr_lo, + dump_params->seg_start_addr_hi); + WMA_LOGI(FL("seg_length:%d dst_addr_lo:0x%x dst_addr_hi:0x%x"), + dump_params->seg_length, dump_params->dest_addr_lo, + dump_params->dest_addr_hi); + dump_params++; + } + + ret = wmi_unified_cmd_send(wma->wmi_handle, buf, len, + WMI_GET_FW_MEM_DUMP_CMDID); + if (ret) { + WMA_LOGE(FL("Failed to send get firmware mem dump request")); + wmi_buf_free(buf); + return VOS_STATUS_E_FAILURE; + } + + WMA_LOGI(FL("Get firmware mem dump request sent successfully")); + return VOS_STATUS_SUCCESS; +} +#else +static VOS_STATUS wma_process_fw_mem_dump_req(tp_wma_handle wma, + void *mem_dump_req) +{ + return VOS_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_MEMDUMP */ + /* * function : wma_mc_process_msg * Description : @@ -25213,6 +25412,11 @@ VOS_STATUS wma_mc_process_msg(v_VOID_t *vos_context, vos_msg_t *msg) (struct sir_guard_time_request *)msg->bodyptr); vos_mem_free(msg->bodyptr); break; + case WDA_FW_MEM_DUMP_REQ: + wma_process_fw_mem_dump_req(wma_handle, + (struct fw_dump_req*)msg->bodyptr); + vos_mem_free(msg->bodyptr); + break; default: WMA_LOGD("unknow msg type %x", msg->type); /* Do Nothing? MSG Body should be freed at here */ diff --git a/CORE/SME/inc/smeInternal.h b/CORE/SME/inc/smeInternal.h index 9e63ec5e89cb..110bfdd8b91a 100644 --- a/CORE/SME/inc/smeInternal.h +++ b/CORE/SME/inc/smeInternal.h @@ -204,6 +204,9 @@ typedef struct tagSmeStruct ocb_callback dcc_update_ndl_callback; void *dcc_stats_event_context; ocb_callback dcc_stats_event_callback; +#ifdef WLAN_FEATURE_MEMDUMP + void (*fw_dump_callback)(void *context, struct fw_dump_rsp *rsp); +#endif } tSmeStruct, *tpSmeStruct; diff --git a/CORE/SME/inc/sme_Api.h b/CORE/SME/inc/sme_Api.h index 1e887fe6cc67..ae37014faaa0 100644 --- a/CORE/SME/inc/sme_Api.h +++ b/CORE/SME/inc/sme_Api.h @@ -4026,6 +4026,11 @@ eHalStatus sme_SetLinkLayerStatsIndCB #endif /* WLAN_FEATURE_LINK_LAYER_STATS */ +eHalStatus sme_fw_mem_dump(tHalHandle hHal, void *recvd_req); +eHalStatus sme_fw_mem_dump_register_cb(tHalHandle hHal, + void (*callback_routine)(void *cb_context, struct fw_dump_rsp *rsp)); +eHalStatus sme_fw_mem_dump_unregister_cb(tHalHandle hHal); + #ifdef WLAN_FEATURE_ROAM_OFFLOAD /*-------------------------------------------------------------------------- \brief sme_UpdateRoamOffloadEnabled() - enable/disable roam offload feature diff --git a/CORE/SME/src/sme_common/sme_Api.c b/CORE/SME/src/sme_common/sme_Api.c index 87384683d409..199640b03e8b 100644 --- a/CORE/SME/src/sme_common/sme_Api.c +++ b/CORE/SME/src/sme_common/sme_Api.c @@ -2363,6 +2363,31 @@ eHalStatus sme_SetEseBeaconRequest(tHalHandle hHal, const tANI_U8 sessionId, #endif /* FEATURE_WLAN_ESE && FEATURE_WLAN_ESE_UPLOAD */ +/** + * sme_process_fw_mem_dump_rsp - process fw memory dump response from WMA + * + * @pMac - pointer to MAC handle. + * @pMsg - pointer to received SME msg. + * + * This function process the received SME message and calls the corresponding + * callback which was already registered with SME. + */ +#ifdef WLAN_FEATURE_MEMDUMP +static void sme_process_fw_mem_dump_rsp(tpAniSirGlobal pMac, vos_msg_t* pMsg) +{ + if (pMsg->bodyptr) { + if (pMac->sme.fw_dump_callback) + pMac->sme.fw_dump_callback(pMac->hHdd, + (struct fw_dump_rsp*) pMsg->bodyptr); + vos_mem_free(pMsg->bodyptr); + } +} +#else +static void sme_process_fw_mem_dump_rsp(tpAniSirGlobal pMac, vos_msg_t* pMsg) +{ +} +#endif + /*-------------------------------------------------------------------------- \brief sme_ProcessMsg() - The main message processor for SME. @@ -3016,6 +3041,9 @@ eHalStatus sme_ProcessMsg(tHalHandle hHal, vos_msg_t* pMsg) } vos_mem_free(pMsg->bodyptr); break; + case eWNI_SME_FW_DUMP_IND: + sme_process_fw_mem_dump_rsp(pMac, pMsg); + break; default: if ( ( pMsg->type >= eWNI_SME_MSG_TYPES_BEGIN ) @@ -15327,6 +15355,78 @@ eHalStatus sme_SetLinkLayerStatsIndCB } #endif /* WLAN_FEATURE_LINK_LAYER_STATS */ + +/** + * sme_fw_mem_dump_register_cb() - Register fw memory dump callback + * + * @hHal - MAC global handle + * @callback_routine - callback routine from HDD + * + * This API is invoked by HDD to register its callback in SME + * + * Return: eHalStatus + */ +#ifdef WLAN_FEATURE_MEMDUMP +eHalStatus sme_fw_mem_dump_register_cb(tHalHandle hal, + void (*callback_routine)(void *cb_context, + struct fw_dump_rsp *rsp)) +{ + eHalStatus status = eHAL_STATUS_SUCCESS; + tpAniSirGlobal pmac = PMAC_STRUCT(hal); + + status = sme_AcquireGlobalLock(&pmac->sme); + if (eHAL_STATUS_SUCCESS == status) { + pmac->sme.fw_dump_callback = callback_routine; + sme_ReleaseGlobalLock(&pmac->sme); + } else { + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, + FL("sme_AcquireGlobalLock error")); + } + + return status; +} +#else +eHalStatus sme_fw_mem_dump_register_cb(tHalHandle hal, + void (*callback_routine)(void *cb_context, + struct fw_dump_rsp *rsp)) +{ + return eHAL_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_MEMDUMP */ + +/** + * sme_fw_mem_dump_unregister_cb() - Unregister fw memory dump callback + * + * @hHal - MAC global handle + * + * This API is invoked by HDD to unregister its callback in SME + * + * Return: eHalStatus + */ +#ifdef WLAN_FEATURE_MEMDUMP +eHalStatus sme_fw_mem_dump_unregister_cb(tHalHandle hal) +{ + eHalStatus status; + tpAniSirGlobal pmac = PMAC_STRUCT(hal); + + status = sme_AcquireGlobalLock(&pmac->sme); + if (eHAL_STATUS_SUCCESS == status) { + pmac->sme.fw_dump_callback = NULL; + sme_ReleaseGlobalLock(&pmac->sme); + } else { + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, + FL("sme_AcquireGlobalLock error")); + } + + return status; +} +#else +eHalStatus sme_fw_mem_dump_unregister_cb(tHalHandle hal) +{ + return eHAL_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_MEMDUMP */ + #ifdef WLAN_FEATURE_ROAM_OFFLOAD /*-------------------------------------------------------------------------- \brief sme_UpdateRoamOffloadEnabled() - enable/disable roam offload feature @@ -15859,7 +15959,80 @@ VOS_STATUS sme_apfind_set_cmd(struct sme_ap_find_request_req *input) return VOS_STATUS_SUCCESS; } #endif /* WLAN_FEATURE_APFIND */ +/** + * sme_fw_mem_dump() - Get FW memory dump + * + * This API is invoked by HDD to indicate FW to start + * dumping firmware memory. + * + * Return: eHalStatus + */ +#ifdef WLAN_FEATURE_MEMDUMP +eHalStatus sme_fw_mem_dump(tHalHandle hHal, void *recvd_req) +{ + eHalStatus status = eHAL_STATUS_SUCCESS; + VOS_STATUS vos_status = VOS_STATUS_SUCCESS; + tpAniSirGlobal pMac = PMAC_STRUCT(hHal); + vos_msg_t msg; + struct fw_dump_req* send_req; + struct fw_dump_seg_req seg_req; + int loop; + + send_req = vos_mem_malloc(sizeof(*send_req)); + if(!send_req) { + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, + FL("Memory allocation failed for WDA_FW_MEM_DUMP")); + return eHAL_STATUS_FAILURE; + } + vos_mem_copy(send_req, recvd_req, sizeof(*send_req)); + + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, + FL("request_id:%d num_seg:%d"), + send_req->request_id, send_req->num_seg); + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, + FL("Segment Information")); + for (loop = 0; loop < send_req->num_seg; loop++) { + seg_req = send_req->segment[loop]; + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, + FL("seg_number:%d"), loop); + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, + FL("seg_id:%d start_addr_lo:0x%x start_addr_hi:0x%x"), + seg_req.seg_id, seg_req.seg_start_addr_lo, + seg_req.seg_start_addr_hi); + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO, + FL("seg_length:%d dst_addr_lo:0x%x dst_addr_hi:0x%x"), + seg_req.seg_length, seg_req.dst_addr_lo, + seg_req.dst_addr_hi); + } + + if (eHAL_STATUS_SUCCESS == sme_AcquireGlobalLock(&pMac->sme)) { + msg.bodyptr = send_req; + msg.type = WDA_FW_MEM_DUMP_REQ; + msg.reserved = 0; + + vos_status = vos_mq_post_message(VOS_MODULE_ID_WDA, &msg); + if (VOS_STATUS_SUCCESS != vos_status) { + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, + FL("Not able to post WDA_FW_MEM_DUMP")); + vos_mem_free(send_req); + status = eHAL_STATUS_FAILURE; + } + sme_ReleaseGlobalLock(&pMac->sme); + } else { + VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_ERROR, + FL("Failed to acquire SME Global Lock")); + vos_mem_free(send_req); + status = eHAL_STATUS_FAILURE; + } + return status; +} +#else +eHalStatus sme_fw_mem_dump(tHalHandle hHal, void *recvd_req) +{ + return eHAL_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_MEMDUMP */ #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH /* * sme_validate_sap_channel_switch() - validate target channel switch w.r.t diff --git a/CORE/WDA/inc/wlan_qct_wda.h b/CORE/WDA/inc/wlan_qct_wda.h index 37bedfe4dd85..50e1ac485ef9 100644 --- a/CORE/WDA/inc/wlan_qct_wda.h +++ b/CORE/WDA/inc/wlan_qct_wda.h @@ -1033,6 +1033,8 @@ tSirRetStatus uMacPostCtrlMsg(void* pSirGlobal, tSirMbMsg* pMb); #define WDA_UPDATE_Q2Q_IE_IND SIR_HAL_UPDATE_Q2Q_IE_IND #endif /* FEATURE_AP_MCC_CH_AVOIDANCE */ +#define WDA_FW_MEM_DUMP_REQ SIR_HAL_FW_MEM_DUMP_REQ + tSirRetStatus wdaPostCtrlMsg(tpAniSirGlobal pMac, tSirMsgQ *pMsg); #define HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME 0x40 // Bit 6 will be used to control BD rate for Management frames @@ -97,6 +97,8 @@ ifeq ($(KERNEL_BUILD), 0) ifeq ($(CONFIG_ROME_IF),usb) CONFIG_LINUX_QCMBR :=y endif + #Flag to enable memdump feature + CONFIG_FEATURE_MEMDUMP := y endif ifdef CPTCFG_QCA_CLD_WLAN @@ -385,6 +387,10 @@ ifeq ($(CONFIG_WLAN_SYNC_TSF),y) HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_tsf.o endif +ifeq ($(CONFIG_FEATURE_MEMDUMP),y) +HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_memdump.o +endif + ############ EPPING ############ EPPING_DIR := CORE/EPPING EPPING_INC_DIR := $(EPPING_DIR)/inc @@ -1371,6 +1377,10 @@ ifeq ($(CONFIG_STATICALLY_ADD_11P_CHANNELS),y) CDEFINES += -DFEATURE_STATICALLY_ADD_11P_CHANNELS endif +ifeq ($(CONFIG_FEATURE_MEMDUMP),y) +CDEFINES += -DWLAN_FEATURE_MEMDUMP +endif + KBUILD_CPPFLAGS += $(CDEFINES) # Currently, for versions of gcc which support it, the kernel Makefile @@ -49,4 +49,8 @@ config CONFIG_WLAN_SYNC_TSF bool "Enable QCOM sync multi devices tsf feature" default n +config CONFIG_FEATURE_MEMDUMP + bool "Enable MEMDUMP feature" + default n + endif # QCA_CLD_WLAN |
