qcacld-3.0: Add debugfs framework support for CSR

Add debugfs framework support to get connect, scan,
roam scan statistics and offload info from debugfs.

Change-Id: I86bdd7e449488d2bcda1b2eaaeb07aac7465770b
CRs-Fixed: 2203626
This commit is contained in:
Rajeev Kumar Sirasanagandla 2018-02-15 19:03:29 +05:30 committed by nshrivas
parent ac7764bd5e
commit 197d417733
9 changed files with 663 additions and 6 deletions

1
Kbuild
View File

@ -70,6 +70,7 @@ ifeq ($(CONFIG_WLAN_DEBUGFS), y)
HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs.o
ifeq ($(CONFIG_WLAN_FEATURE_LINK_LAYER_STATS), y)
HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_llstat.o
HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_csr.o
endif
endif

View File

@ -31,6 +31,8 @@
#include <net/cfg80211.h>
#include <linux/ieee80211.h>
#define HDD_TIME_STRING_LEN 24
/* Preprocessor Definitions and Constants */
#ifdef FEATURE_WLAN_TDLS
#define HDD_MAX_NUM_TDLS_STA 8

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2017 The Linux Foundation. All rights reserved.
* Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
@ -20,8 +20,77 @@
#define _WLAN_HDD_DEBUGFS_H
#ifdef WLAN_DEBUGFS
#define HDD_DEBUGFS_FILE_NAME_MAX 24
/**
* enum hdd_debugfs_file_id - Debugfs file Identifier
* @HDD_DEBUFS_FILE_ID_CONNECT_INFO: connect_info file id
* @HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO: roam_scan_stats file id
* @HDD_DEBUFS_FILE_ID_OFFLOAD_INFO: offload_info file id
* @HDD_DEBUGFS_FILE_ID_MAX: maximum id of csr debugfs file
*/
enum hdd_debugfs_file_id {
HDD_DEBUFS_FILE_ID_CONNECT_INFO = 0,
HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO = 1,
HDD_DEBUFS_FILE_ID_OFFLOAD_INFO = 2,
HDD_DEBUGFS_FILE_ID_MAX,
};
/**
* struct hdd_debugfs_file_info - Debugfs file info
* @name: name of debugfs file
* @id: id from enum hdd_debugfs_file_id used to identify file
* @buf_max_size: max size of buffer from which debugfs file is updated
* @entry: dentry pointer to debugfs file
*/
struct hdd_debugfs_file_info {
uint8_t name[HDD_DEBUGFS_FILE_NAME_MAX];
enum hdd_debugfs_file_id id;
ssize_t buf_max_size;
struct dentry *entry;
};
QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter);
void hdd_debugfs_exit(struct hdd_adapter *adapter);
/**
* hdd_wait_for_debugfs_threads_completion() - Wait for debugfs threads
* completion before proceeding further to stop modules
*
* Return: true if there is no debugfs open
* false if there is at least one debugfs open
*/
bool hdd_wait_for_debugfs_threads_completion(void);
/**
* hdd_return_debugfs_threads_count() - Return active debugfs threads
*
* Return: total number of active debugfs threads in driver
*/
int hdd_return_debugfs_threads_count(void);
/**
* hdd_debugfs_thread_increment() - Increment debugfs thread count
*
* This function is used to increment and keep track of debugfs thread count.
* This is invoked for every file open operation.
*
* Return: None
*/
void hdd_debugfs_thread_increment(void);
/**
* hdd_debugfs_thread_decrement() - Decrement debugfs thread count
*
* This function is used to decrement and keep track of debugfs thread count.
* This is invoked for every file release operation.
*
* Return: None
*/
void hdd_debugfs_thread_decrement(void);
#else
static inline QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter)
{
@ -31,5 +100,56 @@ static inline QDF_STATUS hdd_debugfs_init(struct hdd_adapter *adapter)
static inline void hdd_debugfs_exit(struct hdd_adapter *adapter)
{
}
/**
* hdd_wait_for_debugfs_threads_completion() - Wait for debugfs threads
* completion before proceeding further to stop modules
*
* Return: true if there is no debugfs open
* false if there is at least one debugfs open
*/
static inline
bool hdd_wait_for_debugfs_threads_completion(void)
{
return true;
}
/**
* hdd_return_debugfs_threads_count() - Return active debugfs threads
*
* Return: total number of active debugfs threads in driver
*/
static inline
int hdd_return_debugfs_threads_count(void)
{
return 0;
}
/**
* hdd_debugfs_thread_increment() - Increment debugfs thread count
*
* This function is used to increment and keep track of debugfs thread count.
* This is invoked for every file open operation.
*
* Return: None
*/
static inline
void hdd_debugfs_thread_increment(void)
{
}
/**
* hdd_debugfs_thread_decrement() - Decrement debugfs thread count
*
* This function is used to decrement and keep track of debugfs thread count.
* This is invoked for every file release operation.
*
* Return: None
*/
static inline
void hdd_debugfs_thread_decrement(void)
{
}
#endif /* #ifdef WLAN_DEBUGFS */
#endif /* #ifndef _WLAN_HDD_DEBUGFS_H */

View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
/**
* DOC: wlan_hdd_debugfs_csr.h
*
* WLAN Host Device Driver implementation to update
* debugfs with connect, scan and roam information
*/
#ifndef _WLAN_HDD_DEBUGFS_CSR_H
#define _WLAN_HDD_DEBUGFS_CSR_H
#include <wlan_hdd_includes.h>
#ifdef WLAN_DEBUGFS
/**
* struct wlan_hdd_debugfs_buffer_info - Debugfs buffer info
* @length: current length of the debugfs buffer
* @max_buf_len: maximum buffer length of the debugfs buffer
* @id: id from enum hdd_debugfs_file_id used to identify file
* @data: start of debugfs buffer from which file read starts
* @adapter: pointer to adapter
*
* This structure is used to hold the debugfs buffer details and is stored in
* private data of file argument in file open operation.
*/
struct wlan_hdd_debugfs_buffer_info {
ssize_t length;
ssize_t max_buf_len;
enum hdd_debugfs_file_id id;
uint8_t *data;
struct hdd_adapter *adapter;
};
/**
* wlan_hdd_debugfs_csr_init() - Create wifi diagnostic debugfs files
* @adapter: pointer to adapter for which debugfs files are to be created
*
* Return: None
*/
void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter);
/**
* wlan_hdd_debugfs_csr_deinit() - Remove wifi diagnostic debugfs files
* @adapter: pointer to adapter for which debugfs files are to be removed
*
* Return: None
*/
void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter);
/**
* wlan_hdd_current_time_info_debugfs() - API to get time into user buffer
* @buf: output buffer to hold current time when queried
* @buf_avail_len: available buffer length
*
* Return: No.of bytes copied
*/
ssize_t
wlan_hdd_current_time_info_debugfs(uint8_t *buf, ssize_t buf_avail_len);
#else
/**
* wlan_hdd_debugfs_csr_init() - Create wifi diagnostic debugfs files
* @adapter: pointer to adapter for which debugfs files are to be created
*
* Return: None
*/
static inline void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter)
{
}
/**
* wlan_hdd_debugfs_csr_deinit() - Remove wifi diagnostic debugfs files
* @adapter: pointer to adapter for which debugfs files are to be removed
*
* Return: None
*/
static inline void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter)
{
}
/**
* wlan_hdd_current_time_info_debugfs() - API to get time into user buffer
* @buf: output buffer to hold current time when queried
* @buf_avail_len: available buffer length
*
* Return: No.of bytes copied
*/
static inline ssize_t
wlan_hdd_current_time_info_debugfs(uint8_t *buf, ssize_t buf_avail_len)
{
return 0;
}
#endif
#endif /* _WLAN_HDD_DEBUGFS_CSR_H */

View File

@ -63,6 +63,7 @@
#include "wlan_hdd_tdls.h"
#include "wlan_hdd_tsf.h"
#include "wlan_hdd_cfg80211.h"
#include "wlan_hdd_debugfs.h"
#include <qdf_defer.h>
#include "sap_api.h"
#include <wlan_hdd_lro.h>
@ -254,6 +255,8 @@ enum hdd_driver_flags {
#define WLAN_WAIT_TIME_APF 1000
#define WLAN_WAIT_TIME_FW_ROAM_STATS 1000
/* Maximum time(ms) to wait for RSO CMD status event */
#define WAIT_TIME_RSO_CMD_STATUS 2000
@ -1470,6 +1473,10 @@ struct hdd_adapter {
#ifdef FEATURE_WLAN_APF
struct hdd_apf_context apf_context;
#endif /* FEATURE_WLAN_APF */
#ifdef WLAN_DEBUGFS
struct hdd_debugfs_file_info csr_file[HDD_DEBUGFS_FILE_ID_MAX];
#endif /* WLAN_DEBUGFS */
};
#define WLAN_HDD_GET_STATION_CTX_PTR(adapter) (&(adapter)->session.station)

View File

@ -41,6 +41,51 @@
#define POWER_DEBUGFS_BUFFER_MAX_LEN 4096
#endif
#define MAX_DEBUGFS_WAIT_ITERATIONS 20
#define DEBUGFS_WAIT_SLEEP_TIME 100
static qdf_atomic_t debugfs_thread_count;
void hdd_debugfs_thread_increment(void)
{
qdf_atomic_inc(&debugfs_thread_count);
}
void hdd_debugfs_thread_decrement(void)
{
qdf_atomic_dec(&debugfs_thread_count);
}
int hdd_return_debugfs_threads_count(void)
{
return qdf_atomic_read(&debugfs_thread_count);
}
bool hdd_wait_for_debugfs_threads_completion(void)
{
int count = MAX_DEBUGFS_WAIT_ITERATIONS;
int r;
while (count) {
r = hdd_return_debugfs_threads_count();
if (!r)
break;
if (--count) {
hdd_debug("Waiting for %d debugfs threads to exit", r);
qdf_sleep(DEBUGFS_WAIT_SLEEP_TIME);
}
}
/* at least one debugfs thread is executing */
if (!count) {
hdd_err("Timed-out waiting for debugfs threads");
return false;
}
return true;
}
/**
* __wcnss_wowpattern_write() - wow_pattern debugfs handler
* @file: debugfs file handle

View File

@ -0,0 +1,340 @@
/*
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: wlan_hdd_debugfs_csr.c
*
* WLAN Host Device Driver implementation to update
* debugfs with roaming related information
*/
#include <wlan_hdd_debugfs_csr.h>
#include <wlan_hdd_main.h>
#include <cds_sched.h>
#include <wma_api.h>
#include "qwlan_version.h"
#include "wmi_unified_param.h"
#include "wlan_hdd_debugfs.h"
ssize_t
wlan_hdd_current_time_info_debugfs(uint8_t *buf, ssize_t buf_avail_len)
{
ssize_t length;
char time_buffer[HDD_TIME_STRING_LEN];
int ret_val;
qdf_get_time_of_the_day_in_hr_min_sec_usec(time_buffer,
sizeof(time_buffer));
ret_val = scnprintf(buf, buf_avail_len,
"\nTime at which this file generated = %s\n",
time_buffer);
if (ret_val < 0)
return 0;
length = ret_val;
return length;
}
/**
* wlan_hdd_debugfs_update_csr() - Function to update internal debugfs buffer
* and write into user-space buffer
* @hdd_ctx: hdd context
* @adapter: adapter
* @id: used to identify file for which this info has to be read
* @buf: output buffer to write
* @buf_avail_len: length of the available buffer
*
* Return: Number of bytes read on success, zero otherwise
*/
static ssize_t
wlan_hdd_debugfs_update_csr(struct hdd_context *hdd_ctx,
struct hdd_adapter *adapter,
enum hdd_debugfs_file_id id,
uint8_t *buf,
ssize_t buf_avail_len)
{
ssize_t len = 0;
switch (id) {
case HDD_DEBUFS_FILE_ID_CONNECT_INFO:
/* populate connect info */
break;
case HDD_DEBUFS_FILE_ID_ROAM_SCAN_STATS_INFO:
/* populate roam scan stats info */
break;
case HDD_DEBUFS_FILE_ID_OFFLOAD_INFO:
/* populate offload info */
break;
default:
hdd_err("Failed to fetch stats, unknown stats type");
}
return len;
}
/**
* __wlan_hdd_read_debugfs_csr() - Function to read debug stats
* @file: file pointer
* @buf: buffer
* @count: count
* @pos: position pointer
*
* Return: Number of bytes read on success, zero otherwise
*/
static ssize_t
__wlan_hdd_read_debugfs_csr(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
struct wlan_hdd_debugfs_buffer_info *info;
struct hdd_adapter *adapter;
struct hdd_context *hdd_ctx;
int ret;
ssize_t length;
hdd_enter();
info = file->private_data;
if (!info || !info->data) {
hdd_err("No valid private data");
return 0;
}
adapter = info->adapter;
if ((!adapter) || (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) {
hdd_err("Invalid adapter or adapter has invalid magic");
return 0;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return 0;
if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) {
hdd_err("Interface is not enabled");
return 0;
}
if (*pos == 0) {
info->length = wlan_hdd_debugfs_update_csr(hdd_ctx, adapter,
info->id,
info->data,
info->max_buf_len);
}
length = simple_read_from_buffer(buf, count, pos,
info->data, info->length);
hdd_debug("length written = %zu, count: %zu, pos: %lld",
length, count, *pos);
hdd_exit();
return length;
}
/**
* wlan_hdd_read_debugfs_csr() - SSR wrapper function to read stats
* @file: file pointer
* @buf: buffer
* @count: count
* @pos: position pointer
*
* Return: Number of bytes read on success, zero otherwise
*/
static ssize_t
wlan_hdd_read_debugfs_csr(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_read_debugfs_csr(file, buf, count, pos);
cds_ssr_unprotect(__func__);
return ret;
}
/**
* __wlan_hdd_open_debugfs_csr() - Allocates memory for private data
* @inode: Pointer to inode structure
* @file: file pointer
*
* Return: zero
*/
static int __wlan_hdd_open_debugfs_csr(struct inode *inode,
struct file *file)
{
struct wlan_hdd_debugfs_buffer_info *info;
struct hdd_debugfs_file_info *csr;
struct hdd_adapter *adapter = NULL;
struct hdd_context *hdd_ctx;
int ret;
hdd_enter();
csr = inode->i_private;
if (!csr) {
hdd_err("No private data");
return -EINVAL;
}
adapter = qdf_container_of(csr, struct hdd_adapter,
csr_file[csr->id]);
if (!adapter || (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) {
hdd_err("Invalid adapter or adapter has invalid magic");
return -EINVAL;
}
hdd_ctx = WLAN_HDD_GET_CTX(adapter);
ret = wlan_hdd_validate_context(hdd_ctx);
if (ret)
return -EINVAL;
if (!test_bit(DEVICE_IFACE_OPENED, &adapter->event_flags)) {
hdd_err("Interface is not enabled");
return -EINVAL;
}
info = qdf_mem_malloc(sizeof(*info));
if (!info) {
hdd_err("Not enough memory for file private data");
return -ENOMEM;
}
info->data = qdf_mem_malloc(csr->buf_max_size);
if (!info->data) {
hdd_err("roam stats debugfs buffer allocation failed");
qdf_mem_free(info);
return -ENOMEM;
}
info->length = 0;
info->max_buf_len = csr->buf_max_size;
info->id = csr->id;
info->adapter = adapter;
file->private_data = info;
hdd_exit();
return 0;
}
/**
* wlan_hdd_open_debugfs_csr() - SSR wrapper function to allocate memory for
* private data on file open
* @inode: Pointer to inode structure
* @file: file pointer
*
* Return: zero
*/
static int wlan_hdd_open_debugfs_csr(struct inode *inode,
struct file *file)
{
int ret;
cds_ssr_protect(__func__);
hdd_debugfs_thread_increment();
ret = __wlan_hdd_open_debugfs_csr(inode, file);
if (ret)
hdd_debugfs_thread_decrement();
cds_ssr_unprotect(__func__);
return ret;
}
/**
* __wlan_hdd_release_debugfs_csr() - Function to free private memory on
* release
* @inode: Pointer to inode structure
* @file: file pointer
*
* Return: zero
*/
static int __wlan_hdd_release_debugfs_csr(struct inode *inode,
struct file *file)
{
struct wlan_hdd_debugfs_buffer_info *info = file->private_data;
hdd_enter();
if (!info)
return 0;
file->private_data = NULL;
qdf_mem_free(info->data);
qdf_mem_free(info);
hdd_exit();
return 0;
}
/**
* wlan_hdd_release_debugfs_csr() - SSR wrapper function to free
* private data on release
* @inode: Pointer to inode structure
* @file: file pointer
*
* Return: zero
*/
static int wlan_hdd_release_debugfs_csr(struct inode *inode, struct file *file)
{
int ret;
cds_ssr_protect(__func__);
ret = __wlan_hdd_release_debugfs_csr(inode, file);
hdd_debugfs_thread_decrement();
cds_ssr_unprotect(__func__);
return ret;
}
static const struct file_operations fops_csr_debugfs = {
.read = wlan_hdd_read_debugfs_csr,
.open = wlan_hdd_open_debugfs_csr,
.release = wlan_hdd_release_debugfs_csr,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
void wlan_hdd_debugfs_csr_init(struct hdd_adapter *adapter)
{
/*
* Create debufs diagnostic files for connect, offload info
* and roam info and store in csr_file member of adapter
*/
}
void wlan_hdd_debugfs_csr_deinit(struct hdd_adapter *adapter)
{
uint32_t i;
struct dentry *entry;
for (i = 0; i < HDD_DEBUGFS_FILE_ID_MAX; i++) {
entry = adapter->csr_file[i].entry;
if (!entry)
continue;
adapter->csr_file[i].entry = NULL;
debugfs_remove(entry);
entry = NULL;
}
}

View File

@ -39,6 +39,7 @@
#include "pld_common.h"
#include "wlan_hdd_driver_ops.h"
#include "wlan_ipa_ucfg_api.h"
#include "wlan_hdd_debugfs.h"
#ifdef MODULE
#define WLAN_MODULE_NAME module_name(THIS_MODULE)
@ -494,6 +495,9 @@ static void wlan_hdd_remove(struct device *dev)
if (!cds_wait_for_external_threads_completion(__func__))
hdd_warn("External threads are still active attempting driver unload anyway");
if (!hdd_wait_for_debugfs_threads_completion())
hdd_warn("Debugfs threads are still active attempting driver unload anyway");
mutex_lock(&hdd_init_deinit_lock);
hdd_start_driver_ops_timer(eHDD_DRV_OP_REMOVE);
if (QDF_IS_EPPING_ENABLED(cds_get_conparam())) {
@ -596,6 +600,9 @@ static void wlan_hdd_shutdown(void)
if (!cds_wait_for_external_threads_completion(__func__))
hdd_err("Host is not ready for SSR, attempting anyway");
if (!hdd_wait_for_debugfs_threads_completion())
hdd_err("Debufs threads are still pending, attempting SSR anyway");
if (!QDF_IS_EPPING_ENABLED(cds_get_conparam())) {
hif_disable_isr(hif_ctx);
hdd_wlan_shutdown();

View File

@ -91,6 +91,7 @@
#include "wlan_hdd_ocb.h"
#include "wlan_hdd_nan.h"
#include "wlan_hdd_debugfs.h"
#include "wlan_hdd_debugfs_csr.h"
#include "wlan_hdd_driver_ops.h"
#include "epping_main.h"
#include "wlan_hdd_data_stall_detection.h"
@ -4327,6 +4328,8 @@ static void hdd_cleanup_adapter(struct hdd_context *hdd_ctx, struct hdd_adapter
qdf_mutex_destroy(&adapter->disconnection_status_lock);
hdd_apf_context_destroy(adapter);
wlan_hdd_debugfs_csr_deinit(adapter);
hdd_debugfs_exit(adapter);
/*
@ -4994,6 +4997,9 @@ struct hdd_adapter *hdd_open_adapter(struct hdd_context *hdd_ctx, uint8_t sessio
hdd_info("%s interface created. iftype: %d", netdev_name(adapter->dev),
session_type);
if (adapter->device_mode == QDF_STA_MODE)
wlan_hdd_debugfs_csr_init(adapter);
return adapter;
err_free_netdev:
@ -5380,7 +5386,6 @@ QDF_STATUS hdd_stop_adapter_ext(struct hdd_context *hdd_ctx,
}
hdd_exit();
return QDF_STATUS_SUCCESS;
}
@ -9245,6 +9250,7 @@ int hdd_start_station_adapter(struct hdd_adapter *adapter)
hdd_tx_flow_control_is_pause);
hdd_exit();
return 0;
}
@ -10926,6 +10932,7 @@ int hdd_wlan_stop_modules(struct hdd_context *hdd_ctx, bool ftm_mode)
bool is_recovery_stop = cds_is_driver_recovering();
int ret = 0;
int active_threads;
int debugfs_threads;
struct target_psoc_info *tgt_hdl;
hdd_enter();
@ -10943,11 +10950,16 @@ int hdd_wlan_stop_modules(struct hdd_context *hdd_ctx, bool ftm_mode)
cds_set_module_stop_in_progress(true);
active_threads = cds_return_external_threads_count();
if (active_threads > 0 || hdd_ctx->is_wiphy_suspended) {
hdd_warn("External threads %d wiphy suspend %d",
active_threads, hdd_ctx->is_wiphy_suspended);
debugfs_threads = hdd_return_debugfs_threads_count();
cds_print_external_threads();
if (active_threads > 0 || debugfs_threads > 0 ||
hdd_ctx->is_wiphy_suspended) {
hdd_warn("External threads %d, Debugfs threads %d, wiphy suspend %d",
active_threads, debugfs_threads,
hdd_ctx->is_wiphy_suspended);
if (active_threads)
cds_print_external_threads();
if (IS_IDLE_STOP && !ftm_mode) {
mutex_unlock(&hdd_ctx->iface_change_lock);