blob: 1afe3fd7569c09bb79c34821a314448d28195565 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Anurag Chouhanfb54ab02016-02-18 18:00:46 +05302 * Copyright (c) 2016 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
Jeff Johnsonca1b32b2016-08-02 15:25:05 -070035/* denote that this file does not allow legacy hddLog */
36#define HDD_DISALLOW_LEGACY_HDDLOG 1
37
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080038#include <sme_api.h>
39#include <wlan_hdd_includes.h>
40#include "wlan_hdd_memdump.h"
41#include <linux/module.h>
42#include <linux/kernel.h>
43#include <linux/version.h>
44#include <linux/proc_fs.h> /* Necessary because we use the proc fs */
45#include <linux/uaccess.h> /* for copy_to_user */
46
47/**
48 * hdd_fw_dump_context - hdd firmware memory dump context
49 *
50 * @request_id: userspace assigned firmware memory dump request ID
51 * @response_event: firmware memory dump request wait event
52 */
53struct hdd_fw_dump_context {
54 uint32_t request_id;
55 struct completion response_event;
56};
57static struct hdd_fw_dump_context fw_dump_context;
58
59/**
60 * memdump_cleanup_timer_cb() - Timer callback function for memory dump cleanup.
61 *
62 * @data: Callback data (used to stored HDD context)
63 *
64 * Callback function registered for memory dump cleanup VOS timer.
65 *
66 * Return: none
67 */
68
69static void memdump_cleanup_timer_cb(void *data)
70{
71 int status;
72 hdd_context_t *hdd_ctx = data;
Anurag Chouhan6d760662016-02-20 16:05:43 +053073 qdf_dma_addr_t paddr;
74 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +053075 qdf_device_t qdf_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080076
77 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +053078 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080079 return;
Abhishek Singh23edd1c2016-05-05 11:56:06 +053080
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080081
82 if (!hdd_ctx->fw_dump_loc) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -070083 hdd_notice("Memory dump already freed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080084 return;
85 }
86
Anurag Chouhandf2b2682016-02-29 14:15:27 +053087 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
88 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -070089 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080090 return;
91 }
92
93 paddr = hdd_ctx->dump_loc_paddr;
94 mutex_lock(&hdd_ctx->memdump_lock);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +053095 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080096 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
97 hdd_ctx->fw_dump_loc = NULL;
98 hdd_ctx->memdump_in_progress = false;
99 mutex_unlock(&hdd_ctx->memdump_lock);
100
101}
102
103/**
104 * wlan_hdd_cfg80211_fw_mem_dump_cb() - Callback to receive FW memory dump
105 * @ctx: pointer to HDD context.
Arun Khandavalli4b55da72016-07-19 19:55:01 +0530106 * @dump_rsp: pointer to fw dump copy complete response
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800107 *
108 * This is a callback function used to indicate user space about the
109 * availability for firmware memory dump via vendor event.
110 *
111 * Return: None
112 */
Arun Khandavalli4b55da72016-07-19 19:55:01 +0530113void wlan_hdd_cfg80211_fw_mem_dump_cb(void *ctx,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800114 struct fw_dump_rsp *dump_rsp)
115{
116 hdd_context_t *hdd_ctx = ctx;
117 struct hdd_fw_dump_context *context;
118 int status;
119
120 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530121 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800122 return;
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530123
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800124
125 spin_lock(&hdd_context_lock);
126 context = &fw_dump_context;
127 /* validate the response received */
128 if (!dump_rsp->dump_complete ||
129 context->request_id != dump_rsp->request_id) {
130 spin_unlock(&hdd_context_lock);
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700131 hdd_err("Error @ request_id: %d response_id: %d status: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800132 context->request_id, dump_rsp->request_id,
133 dump_rsp->dump_complete);
134 return;
135 } else {
136 complete(&context->response_event);
137 }
138 spin_unlock(&hdd_context_lock);
139
140 return;
141}
142
143/**
144 * wlan_hdd_send_memdump_rsp - send memory dump response to user space
145 * @hdd_ctx: Pointer to hdd context
146 *
147 * Return: 0 for success; non-zero for failure
148 */
149static int wlan_hdd_send_memdump_rsp(hdd_context_t *hdd_ctx)
150{
151 struct sk_buff *skb;
152 int status;
153
154 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530155 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800156 return status;
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530157
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800158
159 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
160 NLMSG_HDRLEN + NLA_HDRLEN + sizeof(uint32_t));
161
162 if (!skb) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700163 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800164 return -ENOMEM;
165 }
166
167 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MEMDUMP_SIZE,
168 FW_MEM_DUMP_SIZE)) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700169 hdd_err("nla put fail");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800170 goto nla_put_failure;
171 }
172
173 cfg80211_vendor_cmd_reply(skb);
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700174 hdd_notice("Memdump event sent successfully to user space");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800175 return 0;
176
177nla_put_failure:
178 kfree_skb(skb);
179 return -EINVAL;
180}
181
182/**
183 * __wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump
184 * @wiphy: pointer to wireless wiphy structure.
185 * @wdev: pointer to wireless_dev structure.
186 * @data: Pointer to the NL data.
187 * @data_len:Length of @data
188 *
189 * This is called when wlan driver needs to get the firmware memory dump
190 * via vendor specific command.
191 *
192 * Return: 0 on success, error number otherwise.
193 */
194static int __wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
195 struct wireless_dev *wdev,
196 const void *data, int data_len)
197{
198 int status;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530199 QDF_STATUS sme_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800200 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
201 struct fw_dump_req fw_mem_dump_req;
202 struct fw_dump_seg_req *seg_req;
203 uint8_t loop;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530204 qdf_dma_addr_t paddr;
205 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530206 qdf_device_t qdf_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800207 unsigned long rc;
208 struct hdd_fw_dump_context *context;
209
Jeff Johnson1f61b612016-02-12 16:28:33 -0800210 ENTER_DEV(wdev->netdev);
211
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800212 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530213 if (status)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800214 return status;
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530215
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800216
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530217 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
218 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700219 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800220 return -EINVAL;
221 }
222
223 if (hdd_ctx->memdump_in_progress) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700224 hdd_err("Already a memdump req in progress.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800225 return -EBUSY;
226 }
227
228 /*
229 * Allocate memory for fw memory dump. Memory allocated should be
230 * contiguous. Physical address of the allocated memory is passed
231 * to the FW for copy
232 *
233 * Reuse the memory if available.
234 */
235 mutex_lock(&hdd_ctx->memdump_lock);
236 if (!hdd_ctx->fw_dump_loc) {
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530237 hdd_ctx->fw_dump_loc = qdf_mem_alloc_consistent(
238 qdf_ctx, qdf_ctx->dev, FW_MEM_DUMP_SIZE, &paddr);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800239 if (!hdd_ctx->fw_dump_loc) {
240 mutex_unlock(&hdd_ctx->memdump_lock);
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700241 hdd_err("qdf_mem_alloc_consistent failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800242 return -ENOMEM;
243 }
244 hdd_ctx->dump_loc_paddr = paddr;
245 }
246 mutex_unlock(&hdd_ctx->memdump_lock);
247
248 /*
249 * Currently request_id and num_seg is assumed to be default(1)
250 * It is assumed that firmware dump requested is for DRAM section
251 * only
252 */
253
254 fw_mem_dump_req.request_id = FW_MEM_DUMP_REQ_ID;
255 fw_mem_dump_req.num_seg = FW_MEM_DUMP_NUM_SEG;
256
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700257 hdd_notice("request_id:%d num_seg:%d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800258 fw_mem_dump_req.request_id, fw_mem_dump_req.num_seg);
259 seg_req = (struct fw_dump_seg_req *) fw_mem_dump_req.segment;
260 for (loop = 0; loop < fw_mem_dump_req.num_seg; loop++) {
261 seg_req->seg_id = 1;
262 seg_req->seg_start_addr_lo = FW_DRAM_LOCATION;
263 seg_req->seg_start_addr_hi = 0;
264 seg_req->seg_length = FW_MEM_DUMP_SIZE;
265 seg_req->dst_addr_lo = hdd_ctx->dump_loc_paddr;
266 seg_req->dst_addr_hi = 0;
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700267 hdd_notice("seg_number:%d", loop);
268 hdd_notice("seg_id:%d start_addr_lo:0x%x start_addr_hi:0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800269 seg_req->seg_id, seg_req->seg_start_addr_lo,
270 seg_req->seg_start_addr_hi);
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700271 hdd_notice("seg_length:%d dst_addr_lo:0x%x dst_addr_hi:0x%x",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800272 seg_req->seg_length, seg_req->dst_addr_lo,
273 seg_req->dst_addr_hi);
274 seg_req++;
275 }
276
277 /**
278 * Start the cleanup timer.
279 * Memory allocated for this request will be freed up
280 * once the timer expires. Memory dump request is expected to be
281 * completed by this time.
282 *
283 * User space will not be able to access the dump after this time.
284 * New request should be issued to get the dump again.
285 */
Anurag Chouhan210db072016-02-22 18:42:15 +0530286 qdf_mc_timer_start(&hdd_ctx->memdump_cleanup_timer,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800287 MEMDUMP_COMPLETION_TIME_MS);
288 hdd_ctx->memdump_in_progress = true;
289
290 spin_lock(&hdd_context_lock);
291 context = &fw_dump_context;
292 context->request_id = fw_mem_dump_req.request_id;
293 INIT_COMPLETION(context->response_event);
294 spin_unlock(&hdd_context_lock);
295
296 sme_status = sme_fw_mem_dump(hdd_ctx->hHal, &fw_mem_dump_req);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530297 if (QDF_STATUS_SUCCESS != sme_status) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700298 hdd_err("sme_fw_mem_dump Failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800299 mutex_lock(&hdd_ctx->memdump_lock);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530300 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800301 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
302 hdd_ctx->fw_dump_loc = NULL;
303 mutex_unlock(&hdd_ctx->memdump_lock);
304 hdd_ctx->memdump_in_progress = false;
Anurag Chouhan210db072016-02-22 18:42:15 +0530305 if (QDF_TIMER_STATE_RUNNING ==
306 qdf_mc_timer_get_current_state(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800307 &hdd_ctx->memdump_cleanup_timer)) {
Anurag Chouhan210db072016-02-22 18:42:15 +0530308 qdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800309 }
310 return -EINVAL;
311 }
312
313 rc = wait_for_completion_timeout(&context->response_event,
314 msecs_to_jiffies(MEMDUMP_COMPLETION_TIME_MS));
315 if (!rc) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700316 hdd_err("Target response timed out for request_id: %d",
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800317 context->request_id);
318 return -ETIMEDOUT;
319 }
320
321 status = wlan_hdd_send_memdump_rsp(hdd_ctx);
322 if (status)
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700323 hdd_err("Failed to send FW memory dump rsp to user space");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800324
325 return status;
326}
327
328/**
329 * wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump
330 * @wiphy: pointer to wireless wiphy structure.
331 * @wdev: pointer to wireless_dev structure.
332 * @data: Pointer to the NL data.
333 * @data_len:Length of @data
334 *
335 * This is called when wlan driver needs to get the firmware memory dump
336 * via vendor specific command.
337 *
338 * Return: 0 on success, error number otherwise.
339 */
340int wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
341 struct wireless_dev *wdev,
342 const void *data, int data_len)
343{
344 int ret;
345
346 cds_ssr_protect(__func__);
347 ret = __wlan_hdd_cfg80211_get_fw_mem_dump(wiphy, wdev, data, data_len);
348 cds_ssr_unprotect(__func__);
349
350 return ret;
351}
352
353#define PROCFS_MEMDUMP_DIR "debug"
354#define PROCFS_MEMDUMP_NAME "fwdump"
355#define PROCFS_MEMDUMP_PERM 0444
356
357static struct proc_dir_entry *proc_file, *proc_dir;
358
359/** memdump_get_file_data() - get data available in proc file
360 *
361 * @file - handle for the proc file.
362 *
363 * This function is used to retrieve the data passed while
364 * creating proc file entry.
365 *
366 * Return: void pointer to hdd_context
367 */
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800368static void *memdump_get_file_data(struct file *file)
369{
370 void *hdd_ctx;
371
372 hdd_ctx = PDE_DATA(file_inode(file));
373 return hdd_ctx;
374}
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800375
376/**
377 * memdump_read() - perform read operation in memory dump proc file
378 *
379 * @file - handle for the proc file.
380 * @buf - pointer to user space buffer.
381 * @count - number of bytes to be read.
382 * @pos - offset in the from buffer.
383 *
384 * This function performs read operation for the memory dump proc file.
385 *
386 * Return: number of bytes read on success, error code otherwise.
387 */
388static ssize_t memdump_read(struct file *file, char __user *buf,
389 size_t count, loff_t *pos)
390{
391 int status;
392 hdd_context_t *hdd_ctx;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530393 qdf_dma_addr_t paddr;
394 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530395 qdf_device_t qdf_ctx;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800396
397 hdd_ctx = memdump_get_file_data(file);
398
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700399 hdd_notice("Read req for size:%zu pos:%llu", count, *pos);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800400 status = wlan_hdd_validate_context(hdd_ctx);
Abhishek Singh23edd1c2016-05-05 11:56:06 +0530401 if (status)
402 return status;
403
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530404 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
405 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700406 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800407 return -EINVAL;
408 }
409
410 if (!hdd_ctx->memdump_in_progress) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700411 hdd_err("Current mem dump request timed out/failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800412 return -EINVAL;
413 }
414
415 if (*pos < 0) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700416 hdd_err("Invalid start offset for memdump read");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800417 return -EINVAL;
418 } else if (*pos >= FW_MEM_DUMP_SIZE || !count) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700419 hdd_err("No more data to copy");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800420 return 0;
421 } else if (count > FW_MEM_DUMP_SIZE - *pos) {
422 count = FW_MEM_DUMP_SIZE - *pos;
423 }
424
425 if (!hdd_ctx->fw_dump_loc) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700426 hdd_err("Invalid fw mem dump location");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800427 return -EINVAL;
428 }
429
430 if (copy_to_user(buf, hdd_ctx->fw_dump_loc + *pos, count)) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700431 hdd_err("copy to user space failed");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800432 return -EFAULT;
433 }
434
435 /* offset(pos) should be updated here based on the copy done*/
436 *pos += count;
437
438 /* Entire FW memory dump copy completed */
439 if (*pos >= FW_MEM_DUMP_SIZE) {
440 paddr = hdd_ctx->dump_loc_paddr;
441 mutex_lock(&hdd_ctx->memdump_lock);
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530442 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800443 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
444 hdd_ctx->fw_dump_loc = NULL;
445 hdd_ctx->memdump_in_progress = false;
Anurag Chouhan210db072016-02-22 18:42:15 +0530446 if (QDF_TIMER_STATE_RUNNING ==
447 qdf_mc_timer_get_current_state(
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800448 &hdd_ctx->memdump_cleanup_timer)) {
Anurag Chouhan210db072016-02-22 18:42:15 +0530449 qdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800450 }
451 mutex_unlock(&hdd_ctx->memdump_lock);
452 }
453
454 return count;
455}
456
457/**
458 * struct memdump_fops - file operations for memory dump feature
459 * @read - read function for memory dump operation.
460 *
461 * This structure initialize the file operation handle for memory
462 * dump feature
463 */
464static const struct file_operations memdump_fops = {
465 read: memdump_read
466};
467
468/**
469 * memdump_procfs_init() - Initialize procfs for memory dump
470 *
471 * This function create file under proc file system to be used later for
472 * processing firmware memory dump
473 *
474 * Return: 0 on success, error code otherwise.
475 */
476static int memdump_procfs_init(void)
477{
478 hdd_context_t *hdd_ctx;
479
Anurag Chouhan6d760662016-02-20 16:05:43 +0530480 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800481 if (!hdd_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700482 hdd_err("Invalid HDD context");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800483 return -EINVAL;
484 }
485
486 proc_dir = proc_mkdir(PROCFS_MEMDUMP_DIR, NULL);
487 if (proc_dir == NULL) {
488 remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL);
489 pr_debug("Error: Could not initialize /proc/%s\n",
490 PROCFS_MEMDUMP_DIR);
491 return -ENOMEM;
492 }
493
494 proc_file = proc_create_data(PROCFS_MEMDUMP_NAME,
495 PROCFS_MEMDUMP_PERM, proc_dir,
496 &memdump_fops, hdd_ctx);
497 if (proc_file == NULL) {
498 remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir);
499 pr_debug("Error: Could not initialize /proc/%s\n",
500 PROCFS_MEMDUMP_NAME);
501 return -ENOMEM;
502 }
503
504 pr_debug("/proc/%s/%s created\n", PROCFS_MEMDUMP_DIR,
505 PROCFS_MEMDUMP_NAME);
506 return 0;
507}
508
509/**
510 * memdump_procfs_remove() - Remove file/dir under procfs for memory dump
511 *
512 * This function removes file/dir under proc file system that was
513 * processing firmware memory dump
514 *
515 * Return: None
516 */
517static void memdump_procfs_remove(void)
518{
519 remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir);
520 pr_debug("/proc/%s/%s removed\n", PROCFS_MEMDUMP_DIR,
521 PROCFS_MEMDUMP_NAME);
522 remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL);
523 pr_debug("/proc/%s removed\n", PROCFS_MEMDUMP_DIR);
524}
525
526/**
527 * memdump_init() - Intialization function for memory dump feature
528 *
529 * This function creates proc file for memdump feature and registers
530 * HDD callback function with SME.
531 *
532 * Return - 0 on success, error otherwise
533 */
534int memdump_init(void)
535{
536 hdd_context_t *hdd_ctx;
537 int status = 0;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530538 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800539
Anurag Chouhan6d760662016-02-20 16:05:43 +0530540 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800541 if (!hdd_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700542 hdd_err("Invalid HDD context");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800543 return -EINVAL;
544 }
545
Anurag Chouhan6d760662016-02-20 16:05:43 +0530546 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700547 hdd_err("Not initializing memdump in FTM mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800548 return -EINVAL;
549 }
550
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800551 status = memdump_procfs_init();
552 if (status) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700553 hdd_err("Failed to create proc file");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800554 return status;
555 }
556
557 init_completion(&fw_dump_context.response_event);
558
Anurag Chouhan210db072016-02-22 18:42:15 +0530559 qdf_status = qdf_mc_timer_init(&hdd_ctx->memdump_cleanup_timer,
Anurag Chouhan6d760662016-02-20 16:05:43 +0530560 QDF_TIMER_TYPE_SW, memdump_cleanup_timer_cb,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800561 (void *)hdd_ctx);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530562 if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700563 hdd_err("Failed to init memdump cleanup timer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800564 return -EINVAL;
565 }
566
567 mutex_init(&hdd_ctx->memdump_lock);
Sachin Ahuja02dd2e72016-09-03 16:17:51 +0530568 hdd_ctx->memdump_init_done = true;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800569
570 return 0;
571}
572
573/**
574 * memdump_deinit() - De initialize memdump feature
575 *
576 * This function removes proc file created for memdump feature.
577 *
578 * Return: None
579 */
580void memdump_deinit(void)
581{
582 hdd_context_t *hdd_ctx;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530583 qdf_dma_addr_t paddr;
584 qdf_dma_addr_t dma_ctx = 0;
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530585 qdf_device_t qdf_ctx;
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530586 QDF_STATUS qdf_status;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800587
Anurag Chouhan6d760662016-02-20 16:05:43 +0530588 hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800589 if (!hdd_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700590 hdd_err("Invalid HDD context");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800591 return;
592 }
593
Anurag Chouhan6d760662016-02-20 16:05:43 +0530594 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700595 hdd_err("Not deinitializing memdump in FTM mode");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800596 return;
597 }
598
Sachin Ahuja02dd2e72016-09-03 16:17:51 +0530599 if (!hdd_ctx->memdump_init_done) {
600 hdd_err("MemDump not initialized");
601 return;
602 }
603 hdd_ctx->memdump_init_done = false;
604
Anurag Chouhandf2b2682016-02-29 14:15:27 +0530605 qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
606 if (!qdf_ctx) {
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700607 hdd_err("QDF context is NULL");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800608 return;
609 }
610
611 memdump_procfs_remove();
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800612
613 mutex_lock(&hdd_ctx->memdump_lock);
614 if (hdd_ctx->fw_dump_loc) {
615 paddr = hdd_ctx->dump_loc_paddr;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530616 qdf_mem_free_consistent(qdf_ctx, qdf_ctx->dev,
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800617 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
618 hdd_ctx->fw_dump_loc = NULL;
619 hdd_ctx->memdump_in_progress = false;
620 }
621 mutex_unlock(&hdd_ctx->memdump_lock);
Sachin Ahuja02dd2e72016-09-03 16:17:51 +0530622 mutex_destroy(&hdd_ctx->memdump_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800623
Anurag Chouhan210db072016-02-22 18:42:15 +0530624 if (QDF_TIMER_STATE_RUNNING ==
625 qdf_mc_timer_get_current_state(&hdd_ctx->memdump_cleanup_timer)) {
626 qdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800627 }
628
Anurag Chouhan210db072016-02-22 18:42:15 +0530629 qdf_status = qdf_mc_timer_destroy(&hdd_ctx->memdump_cleanup_timer);
Anurag Chouhanfb54ab02016-02-18 18:00:46 +0530630 if (!QDF_IS_STATUS_SUCCESS(qdf_status))
Jeff Johnsonca1b32b2016-08-02 15:25:05 -0700631 hdd_err("Failed to deallocate timer");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800632}