blob: d1ad781ed9296ab635ab752b99fdebe83ab2835f [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
2 * Copyright (c) 2015 The Linux Foundation. All rights reserved.
3 *
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;
69 hdd_context_t *hdd_ctx = data;
70 cdf_dma_addr_t paddr;
71 cdf_dma_addr_t dma_ctx = 0;
72 cdf_device_t cdf_ctx;
73
74 status = wlan_hdd_validate_context(hdd_ctx);
75 if (0 != status) {
76 hddLog(LOGE, FL("HDD context is not valid"));
77 return;
78 }
79
80 if (!hdd_ctx->fw_dump_loc) {
81 hddLog(LOG1, FL("Memory dump already freed"));
82 return;
83 }
84
85 cdf_ctx = cds_get_context(CDF_MODULE_ID_CDF_DEVICE);
86 if (!cdf_ctx) {
87 hddLog(LOGE, FL("CDF context is NULL"));
88 return;
89 }
90
91 paddr = hdd_ctx->dump_loc_paddr;
92 mutex_lock(&hdd_ctx->memdump_lock);
93 cdf_os_mem_free_consistent(cdf_ctx,
94 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
95 hdd_ctx->fw_dump_loc = NULL;
96 hdd_ctx->memdump_in_progress = false;
97 mutex_unlock(&hdd_ctx->memdump_lock);
98
99}
100
101/**
102 * wlan_hdd_cfg80211_fw_mem_dump_cb() - Callback to receive FW memory dump
103 * @ctx: pointer to HDD context.
104 * @rsp: pointer to fw dump copy complete response
105 *
106 * This is a callback function used to indicate user space about the
107 * availability for firmware memory dump via vendor event.
108 *
109 * Return: None
110 */
111static void wlan_hdd_cfg80211_fw_mem_dump_cb(void *ctx,
112 struct fw_dump_rsp *dump_rsp)
113{
114 hdd_context_t *hdd_ctx = ctx;
115 struct hdd_fw_dump_context *context;
116 int status;
117
118 status = wlan_hdd_validate_context(hdd_ctx);
119 if (0 != status) {
120 hddLog(LOGE, FL("HDD context is not valid"));
121 return;
122 }
123
124 spin_lock(&hdd_context_lock);
125 context = &fw_dump_context;
126 /* validate the response received */
127 if (!dump_rsp->dump_complete ||
128 context->request_id != dump_rsp->request_id) {
129 spin_unlock(&hdd_context_lock);
130 hddLog(LOGE,
131 FL("Error @ request_id: %d response_id: %d status: %d"),
132 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);
155 if (0 != status) {
156 hddLog(LOGE, FL("HDD context is not valid"));
157 return status;
158 }
159
160 skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
161 NLMSG_HDRLEN + NLA_HDRLEN + sizeof(uint32_t));
162
163 if (!skb) {
164 hddLog(LOGE, FL("cfg80211_vendor_cmd_alloc_reply_skb failed"));
165 return -ENOMEM;
166 }
167
168 if (nla_put_u32(skb, QCA_WLAN_VENDOR_ATTR_MEMDUMP_SIZE,
169 FW_MEM_DUMP_SIZE)) {
170 hddLog(LOGE, FL("nla put fail"));
171 goto nla_put_failure;
172 }
173
174 cfg80211_vendor_cmd_reply(skb);
175 hddLog(LOG1, FL("Memdump event sent successfully to user space"));
176 return 0;
177
178nla_put_failure:
179 kfree_skb(skb);
180 return -EINVAL;
181}
182
183/**
184 * __wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump
185 * @wiphy: pointer to wireless wiphy structure.
186 * @wdev: pointer to wireless_dev structure.
187 * @data: Pointer to the NL data.
188 * @data_len:Length of @data
189 *
190 * This is called when wlan driver needs to get the firmware memory dump
191 * via vendor specific command.
192 *
193 * Return: 0 on success, error number otherwise.
194 */
195static int __wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
196 struct wireless_dev *wdev,
197 const void *data, int data_len)
198{
199 int status;
200 CDF_STATUS sme_status;
201 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
202 struct fw_dump_req fw_mem_dump_req;
203 struct fw_dump_seg_req *seg_req;
204 uint8_t loop;
205 cdf_dma_addr_t paddr;
206 cdf_dma_addr_t dma_ctx = 0;
207 cdf_device_t cdf_ctx;
208 unsigned long rc;
209 struct hdd_fw_dump_context *context;
210
211 status = wlan_hdd_validate_context(hdd_ctx);
212 if (0 != status) {
213 hddLog(LOGE, FL("HDD context is invalid"));
214 return status;
215 }
216
217 cdf_ctx = cds_get_context(CDF_MODULE_ID_CDF_DEVICE);
218 if (!cdf_ctx) {
219 hddLog(LOGE, FL("CDF context is NULL"));
220 return -EINVAL;
221 }
222
223 if (hdd_ctx->memdump_in_progress) {
224 hddLog(LOGE, FL("Already a memdump req in progress."));
225 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) {
237 hdd_ctx->fw_dump_loc = cdf_os_mem_alloc_consistent(
238 cdf_ctx, FW_MEM_DUMP_SIZE, &paddr, dma_ctx);
239 if (!hdd_ctx->fw_dump_loc) {
240 mutex_unlock(&hdd_ctx->memdump_lock);
241 hddLog(LOGE, FL("cdf_os_mem_alloc_consistent failed"));
242 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
257 hddLog(LOG1, FL("request_id:%d num_seg:%d"),
258 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;
267 hddLog(LOG1, FL("seg_number:%d"), loop);
268 hddLog(LOG1,
269 FL("seg_id:%d start_addr_lo:0x%x start_addr_hi:0x%x"),
270 seg_req->seg_id, seg_req->seg_start_addr_lo,
271 seg_req->seg_start_addr_hi);
272 hddLog(LOG1,
273 FL("seg_length:%d dst_addr_lo:0x%x dst_addr_hi:0x%x"),
274 seg_req->seg_length, seg_req->dst_addr_lo,
275 seg_req->dst_addr_hi);
276 seg_req++;
277 }
278
279 /**
280 * Start the cleanup timer.
281 * Memory allocated for this request will be freed up
282 * once the timer expires. Memory dump request is expected to be
283 * completed by this time.
284 *
285 * User space will not be able to access the dump after this time.
286 * New request should be issued to get the dump again.
287 */
288 cdf_mc_timer_start(&hdd_ctx->memdump_cleanup_timer,
289 MEMDUMP_COMPLETION_TIME_MS);
290 hdd_ctx->memdump_in_progress = true;
291
292 spin_lock(&hdd_context_lock);
293 context = &fw_dump_context;
294 context->request_id = fw_mem_dump_req.request_id;
295 INIT_COMPLETION(context->response_event);
296 spin_unlock(&hdd_context_lock);
297
298 sme_status = sme_fw_mem_dump(hdd_ctx->hHal, &fw_mem_dump_req);
299 if (CDF_STATUS_SUCCESS != sme_status) {
300 hddLog(LOGE, FL("sme_fw_mem_dump Failed"));
301 mutex_lock(&hdd_ctx->memdump_lock);
302 cdf_os_mem_free_consistent(cdf_ctx,
303 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
304 hdd_ctx->fw_dump_loc = NULL;
305 mutex_unlock(&hdd_ctx->memdump_lock);
306 hdd_ctx->memdump_in_progress = false;
307 if (CDF_TIMER_STATE_RUNNING ==
308 cdf_mc_timer_get_current_state(
309 &hdd_ctx->memdump_cleanup_timer)) {
310 cdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
311 }
312 return -EINVAL;
313 }
314
315 rc = wait_for_completion_timeout(&context->response_event,
316 msecs_to_jiffies(MEMDUMP_COMPLETION_TIME_MS));
317 if (!rc) {
318 hddLog(LOGE, FL("Target response timed out for request_id: %d"),
319 context->request_id);
320 return -ETIMEDOUT;
321 }
322
323 status = wlan_hdd_send_memdump_rsp(hdd_ctx);
324 if (status)
325 hddLog(LOGE,
326 FL("Failed to send FW memory dump rsp to user space"));
327
328 return status;
329}
330
331/**
332 * wlan_hdd_cfg80211_get_fw_mem_dump() - Get FW memory dump
333 * @wiphy: pointer to wireless wiphy structure.
334 * @wdev: pointer to wireless_dev structure.
335 * @data: Pointer to the NL data.
336 * @data_len:Length of @data
337 *
338 * This is called when wlan driver needs to get the firmware memory dump
339 * via vendor specific command.
340 *
341 * Return: 0 on success, error number otherwise.
342 */
343int wlan_hdd_cfg80211_get_fw_mem_dump(struct wiphy *wiphy,
344 struct wireless_dev *wdev,
345 const void *data, int data_len)
346{
347 int ret;
348
349 cds_ssr_protect(__func__);
350 ret = __wlan_hdd_cfg80211_get_fw_mem_dump(wiphy, wdev, data, data_len);
351 cds_ssr_unprotect(__func__);
352
353 return ret;
354}
355
356#define PROCFS_MEMDUMP_DIR "debug"
357#define PROCFS_MEMDUMP_NAME "fwdump"
358#define PROCFS_MEMDUMP_PERM 0444
359
360static struct proc_dir_entry *proc_file, *proc_dir;
361
362/** memdump_get_file_data() - get data available in proc file
363 *
364 * @file - handle for the proc file.
365 *
366 * This function is used to retrieve the data passed while
367 * creating proc file entry.
368 *
369 * Return: void pointer to hdd_context
370 */
371#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) || defined(WITH_BACKPORTS)
372static void *memdump_get_file_data(struct file *file)
373{
374 void *hdd_ctx;
375
376 hdd_ctx = PDE_DATA(file_inode(file));
377 return hdd_ctx;
378}
379#else
380static void *memdump_get_file_data(struct file *file)
381{
382 void *hdd_ctx;
383
384 hdd_ctx = PDE(file->f_path.dentry->d_inode)->data;
385 return hdd_ctx;
386}
387#endif
388
389/**
390 * memdump_read() - perform read operation in memory dump proc file
391 *
392 * @file - handle for the proc file.
393 * @buf - pointer to user space buffer.
394 * @count - number of bytes to be read.
395 * @pos - offset in the from buffer.
396 *
397 * This function performs read operation for the memory dump proc file.
398 *
399 * Return: number of bytes read on success, error code otherwise.
400 */
401static ssize_t memdump_read(struct file *file, char __user *buf,
402 size_t count, loff_t *pos)
403{
404 int status;
405 hdd_context_t *hdd_ctx;
406 cdf_dma_addr_t paddr;
407 cdf_dma_addr_t dma_ctx = 0;
408 cdf_device_t cdf_ctx;
409
410 hdd_ctx = memdump_get_file_data(file);
411
412 hddLog(LOG1, FL("Read req for size:%zu pos:%llu"), count, *pos);
413 status = wlan_hdd_validate_context(hdd_ctx);
414 if (0 != status) {
415 hddLog(LOGE, FL("HDD context is not valid"));
416 return -EINVAL;
417 }
418 cdf_ctx = cds_get_context(CDF_MODULE_ID_CDF_DEVICE);
419 if (!cdf_ctx) {
420 hddLog(LOGE, FL("CDF context is NULL"));
421 return -EINVAL;
422 }
423
424 if (!hdd_ctx->memdump_in_progress) {
425 hddLog(LOGE, FL("Current mem dump request timed out/failed"));
426 return -EINVAL;
427 }
428
429 if (*pos < 0) {
430 hddLog(LOGE, FL("Invalid start offset for memdump read"));
431 return -EINVAL;
432 } else if (*pos >= FW_MEM_DUMP_SIZE || !count) {
433 hddLog(LOGE, FL("No more data to copy"));
434 return 0;
435 } else if (count > FW_MEM_DUMP_SIZE - *pos) {
436 count = FW_MEM_DUMP_SIZE - *pos;
437 }
438
439 if (!hdd_ctx->fw_dump_loc) {
440 hddLog(LOGE, FL("Invalid fw mem dump location"));
441 return -EINVAL;
442 }
443
444 if (copy_to_user(buf, hdd_ctx->fw_dump_loc + *pos, count)) {
445 hddLog(LOGE, FL("copy to user space failed"));
446 return -EFAULT;
447 }
448
449 /* offset(pos) should be updated here based on the copy done*/
450 *pos += count;
451
452 /* Entire FW memory dump copy completed */
453 if (*pos >= FW_MEM_DUMP_SIZE) {
454 paddr = hdd_ctx->dump_loc_paddr;
455 mutex_lock(&hdd_ctx->memdump_lock);
456 cdf_os_mem_free_consistent(cdf_ctx,
457 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
458 hdd_ctx->fw_dump_loc = NULL;
459 hdd_ctx->memdump_in_progress = false;
460 if (CDF_TIMER_STATE_RUNNING ==
461 cdf_mc_timer_get_current_state(
462 &hdd_ctx->memdump_cleanup_timer)) {
463 cdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
464 }
465 mutex_unlock(&hdd_ctx->memdump_lock);
466 }
467
468 return count;
469}
470
471/**
472 * struct memdump_fops - file operations for memory dump feature
473 * @read - read function for memory dump operation.
474 *
475 * This structure initialize the file operation handle for memory
476 * dump feature
477 */
478static const struct file_operations memdump_fops = {
479 read: memdump_read
480};
481
482/**
483 * memdump_procfs_init() - Initialize procfs for memory dump
484 *
485 * This function create file under proc file system to be used later for
486 * processing firmware memory dump
487 *
488 * Return: 0 on success, error code otherwise.
489 */
490static int memdump_procfs_init(void)
491{
492 hdd_context_t *hdd_ctx;
493
494 hdd_ctx = cds_get_context(CDF_MODULE_ID_HDD);
495 if (!hdd_ctx) {
496 hddLog(LOGE , FL("Invalid HDD context"));
497 return -EINVAL;
498 }
499
500 proc_dir = proc_mkdir(PROCFS_MEMDUMP_DIR, NULL);
501 if (proc_dir == NULL) {
502 remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL);
503 pr_debug("Error: Could not initialize /proc/%s\n",
504 PROCFS_MEMDUMP_DIR);
505 return -ENOMEM;
506 }
507
508 proc_file = proc_create_data(PROCFS_MEMDUMP_NAME,
509 PROCFS_MEMDUMP_PERM, proc_dir,
510 &memdump_fops, hdd_ctx);
511 if (proc_file == NULL) {
512 remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir);
513 pr_debug("Error: Could not initialize /proc/%s\n",
514 PROCFS_MEMDUMP_NAME);
515 return -ENOMEM;
516 }
517
518 pr_debug("/proc/%s/%s created\n", PROCFS_MEMDUMP_DIR,
519 PROCFS_MEMDUMP_NAME);
520 return 0;
521}
522
523/**
524 * memdump_procfs_remove() - Remove file/dir under procfs for memory dump
525 *
526 * This function removes file/dir under proc file system that was
527 * processing firmware memory dump
528 *
529 * Return: None
530 */
531static void memdump_procfs_remove(void)
532{
533 remove_proc_entry(PROCFS_MEMDUMP_NAME, proc_dir);
534 pr_debug("/proc/%s/%s removed\n", PROCFS_MEMDUMP_DIR,
535 PROCFS_MEMDUMP_NAME);
536 remove_proc_entry(PROCFS_MEMDUMP_DIR, NULL);
537 pr_debug("/proc/%s removed\n", PROCFS_MEMDUMP_DIR);
538}
539
540/**
541 * memdump_init() - Intialization function for memory dump feature
542 *
543 * This function creates proc file for memdump feature and registers
544 * HDD callback function with SME.
545 *
546 * Return - 0 on success, error otherwise
547 */
548int memdump_init(void)
549{
550 hdd_context_t *hdd_ctx;
551 int status = 0;
552 CDF_STATUS cb_status;
553 CDF_STATUS cdf_status;
554
555 hdd_ctx = cds_get_context(CDF_MODULE_ID_HDD);
556 if (!hdd_ctx) {
557 hddLog(LOGE , FL("Invalid HDD context"));
558 return -EINVAL;
559 }
560
561 if (CDF_FTM_MODE == hdd_get_conparam()) {
562 hddLog(LOGE, FL("Not initializing memdump in FTM mode"));
563 return -EINVAL;
564 }
565
566 cb_status = sme_fw_mem_dump_register_cb(hdd_ctx->hHal,
567 wlan_hdd_cfg80211_fw_mem_dump_cb);
568 if (CDF_STATUS_SUCCESS != cb_status) {
569 hddLog(LOGE , FL("Failed to register the callback"));
570 return -EINVAL;
571 }
572
573 status = memdump_procfs_init();
574 if (status) {
575 hddLog(LOGE , FL("Failed to create proc file"));
576 return status;
577 }
578
579 init_completion(&fw_dump_context.response_event);
580
581 cdf_status = cdf_mc_timer_init(&hdd_ctx->memdump_cleanup_timer,
582 CDF_TIMER_TYPE_SW, memdump_cleanup_timer_cb,
583 (void *)hdd_ctx);
584 if (!CDF_IS_STATUS_SUCCESS(cdf_status)) {
585 hddLog(LOGE, FL("Failed to init memdump cleanup timer"));
586 return -EINVAL;
587 }
588
589 mutex_init(&hdd_ctx->memdump_lock);
590
591 return 0;
592}
593
594/**
595 * memdump_deinit() - De initialize memdump feature
596 *
597 * This function removes proc file created for memdump feature.
598 *
599 * Return: None
600 */
601void memdump_deinit(void)
602{
603 hdd_context_t *hdd_ctx;
604 cdf_dma_addr_t paddr;
605 cdf_dma_addr_t dma_ctx = 0;
606 cdf_device_t cdf_ctx;
607 CDF_STATUS cdf_status;
608
609 hdd_ctx = cds_get_context(CDF_MODULE_ID_HDD);
610 if (!hdd_ctx) {
611 hddLog(LOGE , FL("Invalid HDD context"));
612 return;
613 }
614
615 if (CDF_FTM_MODE == hdd_get_conparam()) {
616 hddLog(LOGE, FL("Not deinitializing memdump in FTM mode"));
617 return;
618 }
619
620 cdf_ctx = cds_get_context(CDF_MODULE_ID_CDF_DEVICE);
621 if (!cdf_ctx) {
622 hddLog(LOGE, FL("CDF context is NULL"));
623 return;
624 }
625
626 memdump_procfs_remove();
627 sme_fw_mem_dump_unregister_cb(hdd_ctx->hHal);
628
629 mutex_lock(&hdd_ctx->memdump_lock);
630 if (hdd_ctx->fw_dump_loc) {
631 paddr = hdd_ctx->dump_loc_paddr;
632 cdf_os_mem_free_consistent(cdf_ctx,
633 FW_MEM_DUMP_SIZE, hdd_ctx->fw_dump_loc, paddr, dma_ctx);
634 hdd_ctx->fw_dump_loc = NULL;
635 hdd_ctx->memdump_in_progress = false;
636 }
637 mutex_unlock(&hdd_ctx->memdump_lock);
638
639 if (CDF_TIMER_STATE_RUNNING ==
640 cdf_mc_timer_get_current_state(&hdd_ctx->memdump_cleanup_timer)) {
641 cdf_mc_timer_stop(&hdd_ctx->memdump_cleanup_timer);
642 }
643
644 cdf_status = cdf_mc_timer_destroy(&hdd_ctx->memdump_cleanup_timer);
645 if (!CDF_IS_STATUS_SUCCESS(cdf_status))
646 hddLog(LOGE, FL("Failed to deallocate timer"));
647}