blob: c70d8d561eb5a68e3f0703dc982b836957ed554d [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Jeff Johnson68755312017-02-10 11:46:55 -08002 * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * DOC : wlan_hdd_memdump.c
30 *
31 * WLAN Host Device Driver file for dumping firmware memory
32 *
33 */
34
35#include <sme_api.h>
36#include <wlan_hdd_includes.h>
37#include "wlan_hdd_memdump.h"
38#include <linux/module.h>
39#include <linux/kernel.h>
40#include <linux/version.h>
41#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
42#include <linux/uaccess.h> /* for copy_to_user */
43
44/**
45 * hdd_fw_dump_context - hdd firmware memory dump context
46 *
47 * @request_id: userspace assigned firmware memory dump request ID
48 * @response_event: firmware memory dump request wait event
49 */
50struct hdd_fw_dump_context {
51 uint32_t request_id;
52 struct completion response_event;
53};
54static struct hdd_fw_dump_context fw_dump_context;
55
56/**
57 * memdump_cleanup_timer_cb() - Timer callback function for memory dump cleanup.
58 *
59 * @data: Callback data (used to stored HDD context)
60 *
61 * Callback function registered for memory dump cleanup VOS timer.
62 *
63 * Return: none
64 */
65
66static void memdump_cleanup_timer_cb(void *data)
67{
68 int status;
Jeff Johnson66549172017-08-28 11:53:14 -070069 struct hdd_context *hdd_ctx = data;
Anurag Chouhan6d760662016-02-20 16:05:43 +053070 qdf_dma_addr_t paddr;
71 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +053072 qdf_device_t qdf_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080073
74 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +053075 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080076 return;
Abhishek Singh23edd1c2016-05-05 11:56:06 +053077
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080078
79 if (!hdd_ctx->fw_dump_loc) {
Srinivas Girigowda87150ac2017-03-06 17:48:02 -080080 hdd_debug("Memory dump already freed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080081 return;
82 }
83
Anurag Chouhandf2b2682016-02-29 14:15:27 +053084 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
85 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -070086 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080087 return;
88 }
89
90 paddr = hdd_ctx->dump_loc_paddr;
91 mutex_lock(&hdd_ctx->memdump_lock);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +053092 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080093 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
94 hdd_ctx->fw_dump_loc = NULL;
95 hdd_ctx->memdump_in_progress = false;
96 mutex_unlock(&hdd_ctx->memdump_lock);
97
98}
99
100/**
101 * wlan_hdd_cfg80211_fw_mem_dump_cb() - Callback to receive FW memory dump
102 * @ctx: pointer to HDD context.
Arun Khandavalli4b55da72016-07-19 19:55:01 +0530103 * @dump_rsp: pointer to fw dump copy complete response
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800104 *
105 * This is a callback function used to indicate user space about the
106 * availability for firmware memory dump via vendor event.
107 *
108 * Return: None
109 */
Arun Khandavalli4b55da72016-07-19 19:55:01 +0530110void wlan_hdd_cfg80211_fw_mem_dump_cb(void *ctx,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800111 struct fw_dump_rsp *dump_rsp)
112{
Jeff Johnson66549172017-08-28 11:53:14 -0700113 struct hdd_context *hdd_ctx = ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800114 struct hdd_fw_dump_context *context;
115 int status;
116
117 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530118 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800119 return;
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530120
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800121
122 spin_lock(&hdd_context_lock);
123 context = &fw_dump_context;
124 /* validate the response received */
125 if (!dump_rsp->dump_complete ||
126 context->request_id != dump_rsp->request_id) {
127 spin_unlock(&hdd_context_lock);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800128 hdd_err("request_id: %d response_id: %d status: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800129 context->request_id, dump_rsp->request_id,
130 dump_rsp->dump_complete);
131 return;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800132 }
Jeff Johnson68755312017-02-10 11:46:55 -0800133 complete(&context->response_event);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800134 spin_unlock(&hdd_context_lock);
135
136 return;
137}
138
139/**
140 * wlan_hdd_send_memdump_rsp - send memory dump response to user space
141 * @hdd_ctx: Pointer to hdd context
142 *
143 * Return: 0 for success; non-zero for failure
144 */
Jeff Johnson66549172017-08-28 11:53:14 -0700145static int wlan_hdd_send_memdump_rsp(struct hdd_context *hdd_ctx)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800146{
147 struct sk_buff *skb;
148 int status;
149
150 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530151 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800152 return status;
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530153
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800154
155 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
156 NLMSG_HDRLEN + NLA_HDRLEN + sizeof(uint32_t));
157
158 if (!skb) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700159 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800160 return -ENOMEM;
161 }
162
163 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MEMDUMP_SIZE,
164 FW_MEM_DUMP_SIZE)) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700165 hdd_err("nla put fail");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800166 goto nla_put_failure;
167 }
168
169 cfg80211_vendor_cmd_reply(skb);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800170 hdd_debug("Memdump event sent successfully to user space");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800171 return 0;
172
173nla_put_failure:
174 kfree_skb(skb);
175 return -EINVAL;
176}
177
178/**
179 * __wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump
180 * @wiphy: pointer to wireless wiphy structure.
181 * @wdev: pointer to wireless_dev structure.
182 * @data: Pointer to the NL data.
183 * @data_len:Length of @data
184 *
185 * This is called when wlan driver needs to get the firmware memory dump
186 * via vendor specific command.
187 *
188 * Return: 0 on success, error number otherwise.
189 */
190static int __wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
191 struct wireless_dev *wdev,
192 const void *data, int data_len)
193{
194 int status;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530195 QDF_STATUS sme_status;
Jeff Johnson66549172017-08-28 11:53:14 -0700196 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800197 struct fw_dump_req fw_mem_dump_req;
198 struct fw_dump_seg_req *seg_req;
199 uint8_t loop;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530200 qdf_dma_addr_t paddr;
201 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530202 qdf_device_t qdf_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800203 unsigned long rc;
204 struct hdd_fw_dump_context *context;
205
Jeff Johnson1f61b612016-02-12 16:28:33 -0800206 ENTER_DEV(wdev->netdev);
207
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800208 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530209 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800210 return status;
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530211
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530212 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
213 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700214 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800215 return -EINVAL;
216 }
217
218 if (hdd_ctx->memdump_in_progress) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700219 hdd_err("Already a memdump req in progress.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800220 return -EBUSY;
221 }
222
223 /*
224 * Allocate memory for fw memory dump. Memory allocated should be
225 * contiguous. Physical address of the allocated memory is passed
226 * to the FW for copy
227 *
228 * Reuse the memory if available.
229 */
230 mutex_lock(&hdd_ctx->memdump_lock);
231 if (!hdd_ctx->fw_dump_loc) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530232 hdd_ctx->fw_dump_loc = qdf_mem_alloc_consistent(
233 qdf_ctx, qdf_ctx->dev, FW_MEM_DUMP_SIZE, &paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800234 if (!hdd_ctx->fw_dump_loc) {
235 mutex_unlock(&hdd_ctx->memdump_lock);
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700236 hdd_err("qdf_mem_alloc_consistent failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800237 return -ENOMEM;
238 }
239 hdd_ctx->dump_loc_paddr = paddr;
Manjeet Singh0e617d62016-11-09 16:08:41 +0530240 } else {
241 paddr = hdd_ctx->dump_loc_paddr;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800242 }
243 mutex_unlock(&hdd_ctx->memdump_lock);
244
245 /*
246 * Currently request_id and num_seg is assumed to be default(1)
247 * It is assumed that firmware dump requested is for DRAM section
248 * only
249 */
250
251 fw_mem_dump_req.request_id = FW_MEM_DUMP_REQ_ID;
252 fw_mem_dump_req.num_seg = FW_MEM_DUMP_NUM_SEG;
253
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800254 hdd_debug("request_id:%d num_seg:%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800255 fw_mem_dump_req.request_id, fw_mem_dump_req.num_seg);
256 seg_req = (struct fw_dump_seg_req *) fw_mem_dump_req.segment;
257 for (loop = 0; loop < fw_mem_dump_req.num_seg; loop++) {
258 seg_req->seg_id = 1;
259 seg_req->seg_start_addr_lo = FW_DRAM_LOCATION;
260 seg_req->seg_start_addr_hi = 0;
261 seg_req->seg_length = FW_MEM_DUMP_SIZE;
262 seg_req->dst_addr_lo = hdd_ctx->dump_loc_paddr;
263 seg_req->dst_addr_hi = 0;
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800264 hdd_debug("seg_number:%d", loop);
265 hdd_debug("seg_id:%d start_addr_lo:0x%x start_addr_hi:0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800266 seg_req->seg_id, seg_req->seg_start_addr_lo,
267 seg_req->seg_start_addr_hi);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800268 hdd_debug("seg_length:%d dst_addr_lo:0x%x dst_addr_hi:0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800269 seg_req->seg_length, seg_req->dst_addr_lo,
270 seg_req->dst_addr_hi);
271 seg_req++;
272 }
273
274 /**
275 * Start the cleanup timer.
276 * Memory allocated for this request will be freed up
277 * once the timer expires. Memory dump request is expected to be
278 * completed by this time.
279 *
280 * User space will not be able to access the dump after this time.
281 * New request should be issued to get the dump again.
282 */
Anurag Chouhan210db072016-02-22 18:42:15 +0530283 qdf_mc_timer_start(&hdd_ctx->memdump_cleanup_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800284 MEMDUMP_COMPLETION_TIME_MS);
285 hdd_ctx->memdump_in_progress = true;
286
287 spin_lock(&hdd_context_lock);
288 context = &fw_dump_context;
289 context->request_id = fw_mem_dump_req.request_id;
290 INIT_COMPLETION(context->response_event);
291 spin_unlock(&hdd_context_lock);
292
293 sme_status = sme_fw_mem_dump(hdd_ctx->hHal, &fw_mem_dump_req);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530294 if (QDF_STATUS_SUCCESS != sme_status) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700295 hdd_err("sme_fw_mem_dump Failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800296 mutex_lock(&hdd_ctx->memdump_lock);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530297 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800298 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
299 hdd_ctx->fw_dump_loc = NULL;
300 mutex_unlock(&hdd_ctx->memdump_lock);
301 hdd_ctx->memdump_in_progress = false;
Anurag Chouhan210db072016-02-22 18:42:15 +0530302 if (QDF_TIMER_STATE_RUNNING ==
303 qdf_mc_timer_get_current_state(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800304 &hdd_ctx->memdump_cleanup_timer)) {
Anurag Chouhan210db072016-02-22 18:42:15 +0530305 qdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800306 }
307 return -EINVAL;
308 }
309
310 rc = wait_for_completion_timeout(&context->response_event,
311 msecs_to_jiffies(MEMDUMP_COMPLETION_TIME_MS));
312 if (!rc) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700313 hdd_err("Target response timed out for request_id: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800314 context->request_id);
315 return -ETIMEDOUT;
316 }
317
318 status = wlan_hdd_send_memdump_rsp(hdd_ctx);
319 if (status)
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700320 hdd_err("Failed to send FW memory dump rsp to user space");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800321
322 return status;
323}
324
325/**
326 * wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump
327 * @wiphy: pointer to wireless wiphy structure.
328 * @wdev: pointer to wireless_dev structure.
329 * @data: Pointer to the NL data.
330 * @data_len:Length of @data
331 *
332 * This is called when wlan driver needs to get the firmware memory dump
333 * via vendor specific command.
334 *
335 * Return: 0 on success, error number otherwise.
336 */
337int wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
338 struct wireless_dev *wdev,
339 const void *data, int data_len)
340{
341 int ret;
342
343 cds_ssr_protect(__func__);
344 ret = __wlan_hdd_cfg80211_get_fw_mem_dump(wiphy, wdev, data, data_len);
345 cds_ssr_unprotect(__func__);
346
347 return ret;
348}
349
350#define PROCFS_MEMDUMP_DIR "debug"
351#define PROCFS_MEMDUMP_NAME "fwdump"
352#define PROCFS_MEMDUMP_PERM 0444
353
354static struct proc_dir_entry *proc_file, *proc_dir;
355
356/** memdump_get_file_data() - get data available in proc file
357 *
358 * @file - handle for the proc file.
359 *
360 * This function is used to retrieve the data passed while
361 * creating proc file entry.
362 *
363 * Return: void pointer to hdd_context
364 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800365static void *memdump_get_file_data(struct file *file)
366{
367 void *hdd_ctx;
368
369 hdd_ctx = PDE_DATA(file_inode(file));
370 return hdd_ctx;
371}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800372
373/**
374 * memdump_read() - perform read operation in memory dump proc file
375 *
376 * @file - handle for the proc file.
377 * @buf - pointer to user space buffer.
378 * @count - number of bytes to be read.
379 * @pos - offset in the from buffer.
380 *
381 * This function performs read operation for the memory dump proc file.
382 *
383 * Return: number of bytes read on success, error code otherwise.
384 */
385static ssize_t memdump_read(struct file *file, char __user *buf,
386 size_t count, loff_t *pos)
387{
388 int status;
Jeff Johnson66549172017-08-28 11:53:14 -0700389 struct hdd_context *hdd_ctx;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530390 qdf_dma_addr_t paddr;
391 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530392 qdf_device_t qdf_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800393
394 hdd_ctx = memdump_get_file_data(file);
395
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800396 hdd_debug("Read req for size:%zu pos:%llu", count, *pos);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800397 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530398 if (status)
399 return status;
400
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530401 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
402 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700403 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800404 return -EINVAL;
405 }
406
Ashish Kumar Dhanotiya1f75a532017-04-07 13:03:36 +0530407 mutex_lock(&hdd_ctx->memdump_lock);
408
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800409 if (!hdd_ctx->memdump_in_progress) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700410 hdd_err("Current mem dump request timed out/failed");
Ashish Kumar Dhanotiya1f75a532017-04-07 13:03:36 +0530411 status = -EINVAL;
412 goto memdump_read_fail;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800413 }
414
415 if (*pos < 0) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700416 hdd_err("Invalid start offset for memdump read");
Ashish Kumar Dhanotiya1f75a532017-04-07 13:03:36 +0530417 status = -EINVAL;
418 goto memdump_read_fail;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800419 } else if (*pos >= FW_MEM_DUMP_SIZE || !count) {
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800420 hdd_debug("No more data to copy");
Ashish Kumar Dhanotiya1f75a532017-04-07 13:03:36 +0530421 status = 0;
422 goto memdump_read_fail;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800423 } else if (count > FW_MEM_DUMP_SIZE - *pos) {
424 count = FW_MEM_DUMP_SIZE - *pos;
425 }
426
427 if (!hdd_ctx->fw_dump_loc) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700428 hdd_err("Invalid fw mem dump location");
Ashish Kumar Dhanotiya1f75a532017-04-07 13:03:36 +0530429 status = -EINVAL;
430 goto memdump_read_fail;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800431 }
432
433 if (copy_to_user(buf, hdd_ctx->fw_dump_loc + *pos, count)) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700434 hdd_err("copy to user space failed");
Ashish Kumar Dhanotiya1f75a532017-04-07 13:03:36 +0530435 status = -EFAULT;
436 goto memdump_read_fail;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800437 }
438
439 /* offset(pos) should be updated here based on the copy done*/
440 *pos += count;
441
442 /* Entire FW memory dump copy completed */
443 if (*pos >= FW_MEM_DUMP_SIZE) {
444 paddr = hdd_ctx->dump_loc_paddr;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530445 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800446 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
447 hdd_ctx->fw_dump_loc = NULL;
448 hdd_ctx->memdump_in_progress = false;
Anurag Chouhan210db072016-02-22 18:42:15 +0530449 if (QDF_TIMER_STATE_RUNNING ==
450 qdf_mc_timer_get_current_state(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800451 &hdd_ctx->memdump_cleanup_timer)) {
Anurag Chouhan210db072016-02-22 18:42:15 +0530452 qdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800453 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800454
Ashish Kumar Dhanotiya1f75a532017-04-07 13:03:36 +0530455 }
456 status = count;
457memdump_read_fail:
458 mutex_unlock(&hdd_ctx->memdump_lock);
459 return status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800460}
461
462/**
463 * struct memdump_fops - file operations for memory dump feature
464 * @read - read function for memory dump operation.
465 *
466 * This structure initialize the file operation handle for memory
467 * dump feature
468 */
469static const struct file_operations memdump_fops = {
470 read: memdump_read
471};
472
473/**
474 * memdump_procfs_init() - Initialize procfs for memory dump
475 *
476 * This function create file under proc file system to be used later for
477 * processing firmware memory dump
478 *
479 * Return: 0 on success, error code otherwise.
480 */
481static int memdump_procfs_init(void)
482{
Jeff Johnson66549172017-08-28 11:53:14 -0700483 struct hdd_context *hdd_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800484
Anurag Chouhan6d760662016-02-20 16:05:43 +0530485 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800486 if (!hdd_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700487 hdd_err("Invalid HDD context");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800488 return -EINVAL;
489 }
490
491 proc_dir = proc_mkdir(PROCFS_MEMDUMP_DIR, NULL);
492 if (proc_dir == NULL) {
493 remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800494 pr_debug("Could not initialize /proc/%s\n",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800495 PROCFS_MEMDUMP_DIR);
496 return -ENOMEM;
497 }
498
499 proc_file = proc_create_data(PROCFS_MEMDUMP_NAME,
500 PROCFS_MEMDUMP_PERM, proc_dir,
501 &memdump_fops, hdd_ctx);
502 if (proc_file == NULL) {
503 remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800504 pr_debug("Could not initialize /proc/%s\n",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800505 PROCFS_MEMDUMP_NAME);
506 return -ENOMEM;
507 }
508
509 pr_debug("/proc/%s/%s created\n", PROCFS_MEMDUMP_DIR,
510 PROCFS_MEMDUMP_NAME);
511 return 0;
512}
513
514/**
515 * memdump_procfs_remove() - Remove file/dir under procfs for memory dump
516 *
517 * This function removes file/dir under proc file system that was
518 * processing firmware memory dump
519 *
520 * Return: None
521 */
522static void memdump_procfs_remove(void)
523{
524 remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir);
525 pr_debug("/proc/%s/%s removed\n", PROCFS_MEMDUMP_DIR,
526 PROCFS_MEMDUMP_NAME);
527 remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL);
528 pr_debug("/proc/%s removed\n", PROCFS_MEMDUMP_DIR);
529}
530
531/**
532 * memdump_init() - Intialization function for memory dump feature
533 *
534 * This function creates proc file for memdump feature and registers
535 * HDD callback function with SME.
536 *
537 * Return - 0 on success, error otherwise
538 */
539int memdump_init(void)
540{
Jeff Johnson66549172017-08-28 11:53:14 -0700541 struct hdd_context *hdd_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800542 int status = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530543 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800544
Anurag Chouhan6d760662016-02-20 16:05:43 +0530545 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800546 if (!hdd_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700547 hdd_err("Invalid HDD context");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800548 return -EINVAL;
549 }
550
Anurag Chouhan6d760662016-02-20 16:05:43 +0530551 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700552 hdd_err("Not initializing memdump in FTM mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800553 return -EINVAL;
554 }
555
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800556 status = memdump_procfs_init();
557 if (status) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700558 hdd_err("Failed to create proc file");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800559 return status;
560 }
561
562 init_completion(&fw_dump_context.response_event);
563
Anurag Chouhan210db072016-02-22 18:42:15 +0530564 qdf_status = qdf_mc_timer_init(&hdd_ctx->memdump_cleanup_timer,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530565 QDF_TIMER_TYPE_SW, memdump_cleanup_timer_cb,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800566 (void *)hdd_ctx);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530567 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700568 hdd_err("Failed to init memdump cleanup timer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800569 return -EINVAL;
570 }
571
572 mutex_init(&hdd_ctx->memdump_lock);
Sachin Ahuja02dd2e72016-09-03 16:17:51 +0530573 hdd_ctx->memdump_init_done = true;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800574
575 return 0;
576}
577
578/**
579 * memdump_deinit() - De initialize memdump feature
580 *
581 * This function removes proc file created for memdump feature.
582 *
583 * Return: None
584 */
585void memdump_deinit(void)
586{
Jeff Johnson66549172017-08-28 11:53:14 -0700587 struct hdd_context *hdd_ctx;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530588 qdf_dma_addr_t paddr;
589 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530590 qdf_device_t qdf_ctx;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530591 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800592
Anurag Chouhan6d760662016-02-20 16:05:43 +0530593 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800594 if (!hdd_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700595 hdd_err("Invalid HDD context");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800596 return;
597 }
598
Anurag Chouhan6d760662016-02-20 16:05:43 +0530599 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700600 hdd_err("Not deinitializing memdump in FTM mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800601 return;
602 }
603
Sachin Ahuja02dd2e72016-09-03 16:17:51 +0530604 if (!hdd_ctx->memdump_init_done) {
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800605 hdd_warn("MemDump not initialized");
Sachin Ahuja02dd2e72016-09-03 16:17:51 +0530606 return;
607 }
608 hdd_ctx->memdump_init_done = false;
609
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530610 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
611 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700612 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800613 return;
614 }
615
616 memdump_procfs_remove();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800617
618 mutex_lock(&hdd_ctx->memdump_lock);
619 if (hdd_ctx->fw_dump_loc) {
620 paddr = hdd_ctx->dump_loc_paddr;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530621 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800622 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
623 hdd_ctx->fw_dump_loc = NULL;
624 hdd_ctx->memdump_in_progress = false;
625 }
626 mutex_unlock(&hdd_ctx->memdump_lock);
Sachin Ahuja02dd2e72016-09-03 16:17:51 +0530627 mutex_destroy(&hdd_ctx->memdump_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800628
Anurag Chouhan210db072016-02-22 18:42:15 +0530629 if (QDF_TIMER_STATE_RUNNING ==
630 qdf_mc_timer_get_current_state(&hdd_ctx->memdump_cleanup_timer)) {
631 qdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800632 }
633
Anurag Chouhan210db072016-02-22 18:42:15 +0530634 qdf_status = qdf_mc_timer_destroy(&hdd_ctx->memdump_cleanup_timer);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530635 if (!QDF_IS_STATUS_SUCCESS(qdf_status))
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700636 hdd_err("Failed to deallocate timer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800637}
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530638
639#ifdef MULTI_IF_NAME
640#define PROCFS_DRIVER_DUMP_DIR "debugdriver" MULTI_IF_NAME
641#else
642#define PROCFS_DRIVER_DUMP_DIR "debugdriver"
643#endif
644#define PROCFS_DRIVER_DUMP_NAME "driverdump"
645#define PROCFS_DRIVER_DUMP_PERM 0444
646
647static struct proc_dir_entry *proc_file_driver, *proc_dir_driver;
648
649/**
650 * hdd_driver_mem_cleanup() - Frees memory allocated for
651 * driver dump
652 *
653 * This function unallocates driver dump memory.
654 *
655 * Return: None
656 */
657static void hdd_driver_mem_cleanup(void)
658{
Jeff Johnson66549172017-08-28 11:53:14 -0700659 struct hdd_context *hdd_ctx;
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530660
661 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
662 if (!hdd_ctx) {
663 hdd_err("Invalid HDD context");
664 return;
665 }
666
667 if (hdd_ctx->driver_dump_mem) {
668 qdf_mem_free(hdd_ctx->driver_dump_mem);
669 hdd_ctx->driver_dump_mem = NULL;
670 }
671}
672
673
674/**
675 * hdd_driver_memdump_read() - perform read operation in driver
676 * memory dump proc file
677 * @file - handle for the proc file.
678 * @buf - pointer to user space buffer.
679 * @count - number of bytes to be read.
680 * @pos - offset in the from buffer.
681 *
682 * This function performs read operation for the driver memory dump proc file.
683 *
684 * Return: number of bytes read on success, error code otherwise.
685 */
686static ssize_t hdd_driver_memdump_read(struct file *file, char __user *buf,
687 size_t count, loff_t *pos)
688{
689 int status;
690 QDF_STATUS qdf_status;
Jeff Johnson66549172017-08-28 11:53:14 -0700691 struct hdd_context *hdd_ctx;
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530692 size_t no_of_bytes_read = 0;
693
694 hdd_ctx = memdump_get_file_data(file);
695
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800696 hdd_debug("Read req for size:%zu pos:%llu", count, *pos);
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530697 status = wlan_hdd_validate_context(hdd_ctx);
698 if (status != 0)
699 return -EINVAL;
700
SaidiReddy Yenugac3a74ba2017-03-08 16:17:55 +0530701 mutex_lock(&hdd_ctx->memdump_lock);
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530702 if (*pos < 0) {
703 hdd_err("Invalid start offset for memdump read");
SaidiReddy Yenugac3a74ba2017-03-08 16:17:55 +0530704 mutex_unlock(&hdd_ctx->memdump_lock);
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530705 return -EINVAL;
706 } else if (!count || (hdd_ctx->driver_dump_size &&
707 (*pos >= hdd_ctx->driver_dump_size))) {
SaidiReddy Yenugac3a74ba2017-03-08 16:17:55 +0530708 mutex_unlock(&hdd_ctx->memdump_lock);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800709 hdd_debug("No more data to copy");
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530710 return 0;
711 } else if ((*pos == 0) || (hdd_ctx->driver_dump_mem == NULL)) {
712 /*
713 * Allocate memory for Driver memory dump.
714 */
715 if (!hdd_ctx->driver_dump_mem) {
716 hdd_ctx->driver_dump_mem =
717 qdf_mem_malloc(DRIVER_MEM_DUMP_SIZE);
718 if (!hdd_ctx->driver_dump_mem) {
719 hdd_err("qdf_mem_malloc failed");
SaidiReddy Yenugac3a74ba2017-03-08 16:17:55 +0530720 mutex_unlock(&hdd_ctx->memdump_lock);
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530721 return -ENOMEM;
722 }
723 }
724
725 qdf_status = qdf_state_info_dump_all(hdd_ctx->driver_dump_mem,
726 DRIVER_MEM_DUMP_SIZE,
727 &hdd_ctx->driver_dump_size);
728 /*
729 * If qdf_status is QDF_STATUS_E_NOMEM, then memory allocated is
730 * insufficient to dump driver information. This print can give
731 * information to allocate more memory if more information from
732 * each layer is added in future.
733 */
734 if (qdf_status != QDF_STATUS_SUCCESS)
735 hdd_err("Error in dump driver information, status %d",
736 qdf_status);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800737 hdd_debug("driver_dump_size: %d",
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530738 hdd_ctx->driver_dump_size);
739 }
740
741 if (count > hdd_ctx->driver_dump_size - *pos)
742 no_of_bytes_read = hdd_ctx->driver_dump_size - *pos;
743 else
744 no_of_bytes_read = count;
745
746 if (copy_to_user(buf, hdd_ctx->driver_dump_mem + *pos,
747 no_of_bytes_read)) {
748 hdd_err("copy to user space failed");
SaidiReddy Yenugac3a74ba2017-03-08 16:17:55 +0530749 mutex_unlock(&hdd_ctx->memdump_lock);
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530750 return -EFAULT;
751 }
752
753 /* offset(pos) should be updated here based on the copy done */
754 *pos += no_of_bytes_read;
755
756 /* Entire driver memory dump copy completed */
757 if (*pos >= hdd_ctx->driver_dump_size)
758 hdd_driver_mem_cleanup();
759
SaidiReddy Yenugac3a74ba2017-03-08 16:17:55 +0530760 mutex_unlock(&hdd_ctx->memdump_lock);
761
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530762 return no_of_bytes_read;
763}
764
765
766/**
767 * struct driver_dump_fops - file operations for driver dump feature
768 * @read - read function for driver dump operation.
769 *
770 * This structure initialize the file operation handle for memory
771 * dump feature
772 */
773static const struct file_operations driver_dump_fops = {
774read: hdd_driver_memdump_read
775};
776
777/**
778 * hdd_driver_memdump_procfs_init() - Initialize procfs for driver memory dump
779 *
780 * This function create file under proc file system to be used later for
781 * processing driver memory dump
782 *
783 * Return: 0 on success, error code otherwise.
784 */
785static int hdd_driver_memdump_procfs_init(void)
786{
Jeff Johnson66549172017-08-28 11:53:14 -0700787 struct hdd_context *hdd_ctx;
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530788
789 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
790 if (!hdd_ctx) {
791 hdd_err("Invalid HDD context");
792 return -EINVAL;
793 }
794
795 proc_dir_driver = proc_mkdir(PROCFS_DRIVER_DUMP_DIR, NULL);
796 if (proc_dir_driver == NULL) {
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800797 pr_debug("Could not initialize /proc/%s\n",
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530798 PROCFS_DRIVER_DUMP_DIR);
799 return -ENOMEM;
800 }
801
802 proc_file_driver = proc_create_data(PROCFS_DRIVER_DUMP_NAME,
803 PROCFS_DRIVER_DUMP_PERM, proc_dir_driver,
804 &driver_dump_fops, hdd_ctx);
805 if (proc_file_driver == NULL) {
806 remove_proc_entry(PROCFS_DRIVER_DUMP_NAME, proc_dir_driver);
Srinivas Girigowda87150ac2017-03-06 17:48:02 -0800807 pr_debug("Could not initialize /proc/%s\n",
Padma, Santhosh Kumar9aba02f2016-08-11 16:30:25 +0530808 PROCFS_DRIVER_DUMP_NAME);
809 return -ENOMEM;
810 }
811
812 pr_debug("/proc/%s/%s created\n", PROCFS_DRIVER_DUMP_DIR,
813 PROCFS_DRIVER_DUMP_NAME);
814 return 0;
815}
816
817/**
818 * hdd_driver_memdump_procfs_remove() - Remove file/dir under procfs
819 * for driver memory dump
820 *
821 * This function removes file/dir under proc file system that was
822 * processing driver memory dump
823 *
824 * Return: None
825 */
826static void hdd_driver_memdump_procfs_remove(void)
827{
828 remove_proc_entry(PROCFS_DRIVER_DUMP_NAME, proc_dir_driver);
829 pr_debug("/proc/%s/%s removed\n", PROCFS_DRIVER_DUMP_DIR,
830 PROCFS_DRIVER_DUMP_NAME);
831 remove_proc_entry(PROCFS_DRIVER_DUMP_DIR, NULL);
832 pr_debug("/proc/%s removed\n", PROCFS_DRIVER_DUMP_DIR);
833}
834
835/**
836 * hdd_driver_memdump_init() - Intialization function for driver
837 * memory dump feature
838 *
839 * This function creates proc file for driver memdump feature
840 *
841 * Return - 0 on success, error otherwise
842 */
843int hdd_driver_memdump_init(void)
844{
845 int status;
846
847 if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
848 hdd_err("Not initializing memdump in FTM mode");
849 return -EINVAL;
850 }
851
852 status = hdd_driver_memdump_procfs_init();
853 if (status) {
854 hdd_err("Failed to create proc file");
855 return status;
856 }
857
858 return 0;
859}
860
861/**
862 * hdd_driver_memdump_deinit() - De initialize driver memdump feature
863 *
864 * This function removes proc file created for driver memdump feature.
865 *
866 * Return: None
867 */
868void hdd_driver_memdump_deinit(void)
869{
870 if (hdd_get_conparam() == QDF_GLOBAL_FTM_MODE) {
871 hdd_err("Not deinitializing memdump in FTM mode");
872 return;
873 }
874
875 hdd_driver_memdump_procfs_remove();
876
877 hdd_driver_mem_cleanup();
878}