summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrishna Kumaar Natarajan <kknatara@qca.qualcomm.com>2015-04-16 12:29:20 -0700
committerAnjaneeDevi Kapparapu <c_akappa@qti.qualcomm.com>2015-04-24 19:52:35 +0530
commitbdf0f67439baf5dec556eca56607f01cb420dbe0 (patch)
tree65f90fcdfbb3fb9189b959ceea9b4cda69b8978a
parent30f08dba71d9ec8cf08ef0e43d015eea162d148d (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.h3
-rw-r--r--CORE/HDD/inc/wlan_hdd_main.h7
-rw-r--r--CORE/HDD/inc/wlan_hdd_memdump.h72
-rw-r--r--CORE/HDD/src/wlan_hdd_cfg80211.c18
-rwxr-xr-xCORE/HDD/src/wlan_hdd_main.c6
-rw-r--r--CORE/HDD/src/wlan_hdd_memdump.c507
-rw-r--r--CORE/MAC/inc/sirApi.h55
-rw-r--r--CORE/MAC/inc/wniApi.h1
-rw-r--r--CORE/MAC/src/include/sirParams.h2
-rw-r--r--CORE/SERVICES/WMA/wma.c204
-rw-r--r--CORE/SME/inc/smeInternal.h3
-rw-r--r--CORE/SME/inc/sme_Api.h5
-rw-r--r--CORE/SME/src/sme_common/sme_Api.c173
-rw-r--r--CORE/WDA/inc/wlan_qct_wda.h2
-rw-r--r--Kbuild10
-rw-r--r--Kconfig4
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
diff --git a/Kbuild b/Kbuild
index 9d897fa04de4..c06a2db1908a 100644
--- a/Kbuild
+++ b/Kbuild
@@ -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
diff --git a/Kconfig b/Kconfig
index 31a48a0c90cc..d917ada85892 100644
--- a/Kconfig
+++ b/Kconfig
@@ -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