/*
 * Copyright (c) 2013-2017 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.c
 *
 * This driver currently supports the following debugfs files:
 * wlan_wcnss/wow_enable to enable/disable WoWL.
 * wlan_wcnss/wow_pattern to configure WoWL patterns.
 * wlan_wcnss/pattern_gen to configure periodic TX patterns.
 */

#ifdef WLAN_OPEN_SOURCE
#include <wlan_hdd_includes.h>
#include <wlan_hdd_debugfs.h>
#include <wlan_hdd_request_manager.h>
#include <wlan_hdd_wowl.h>
#include <cds_sched.h>
#include <wlan_hdd_debugfs_llstat.h>

#define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8
#define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512
#define MAX_USER_COMMAND_SIZE_FRAME 4096

#ifdef WLAN_POWER_DEBUGFS
#define POWER_DEBUGFS_BUFFER_MAX_LEN 4096
#endif

/**
 * __wcnss_wowenable_write() - wow_enable debugfs handler
 * @file: debugfs file handle
 * @buf: text being written to the debugfs
 * @count: size of @buf
 * @ppos: (unused) offset into the virtual file system
 *
 * Return: number of bytes processed
 */
static ssize_t __wcnss_wowenable_write(struct file *file,
				     const char __user *buf, size_t count,
				     loff_t *ppos)
{
	hdd_adapter_t *pAdapter;
	hdd_context_t *hdd_ctx;
	char cmd[MAX_USER_COMMAND_SIZE_WOWL_ENABLE + 1];
	char *sptr, *token;
	uint8_t wow_enable = 0;
	uint8_t wow_mp = 0;
	uint8_t wow_pbm = 0;
	int ret;

	ENTER();

	pAdapter = (hdd_adapter_t *)file->private_data;
	if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
		hdd_err("Invalid adapter or adapter has invalid magic");
		return -EINVAL;
	}

	hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
	ret = wlan_hdd_validate_context(hdd_ctx);
	if (0 != ret)
		return ret;

	if (!wlan_hdd_validate_modules_state(hdd_ctx))
		return -EINVAL;

	if (!sme_is_feature_supported_by_fw(WOW)) {
		hdd_err("Wake-on-Wireless feature is not supported in firmware!");
		return -EINVAL;
	}

	if (count > MAX_USER_COMMAND_SIZE_WOWL_ENABLE) {
		hdd_err("Command length is larger than %d bytes",
			MAX_USER_COMMAND_SIZE_WOWL_ENABLE);
		return -EINVAL;
	}

	/* Get command from user */
	if (copy_from_user(cmd, buf, count))
		return -EFAULT;
	cmd[count] = '\0';
	sptr = cmd;

	/* Get enable or disable wow */
	token = strsep(&sptr, " ");
	if (!token)
		return -EINVAL;
	if (kstrtou8(token, 0, &wow_enable))
		return -EINVAL;

	/* Disable wow */
	if (!wow_enable) {
		if (!hdd_exit_wowl(pAdapter)) {
			hdd_err("hdd_exit_wowl failed!");
			return -EFAULT;
		}

		return count;
	}

	/* Get enable or disable magic packet mode */
	token = strsep(&sptr, " ");
	if (!token)
		return -EINVAL;
	if (kstrtou8(token, 0, &wow_mp))
		return -EINVAL;
	if (wow_mp > 1)
		wow_mp = 1;

	/* Get enable or disable pattern byte matching mode */
	token = strsep(&sptr, " ");
	if (!token)
		return -EINVAL;
	if (kstrtou8(token, 0, &wow_pbm))
		return -EINVAL;
	if (wow_pbm > 1)
		wow_pbm = 1;

	if (!hdd_enter_wowl(pAdapter, wow_mp, wow_pbm)) {
		hdd_err("hdd_enter_wowl failed!");
		return -EFAULT;
	}
	EXIT();
	return count;
}

/**
 * wcnss_wowenable_write() - SSR wrapper for wcnss_wowenable_write
 * @file: file pointer
 * @buf: buffer
 * @count: count
 * @ppos: position pointer
 *
 * Return: 0 on success, error number otherwise
 */
static ssize_t wcnss_wowenable_write(struct file *file,
				 const char __user *buf,
				 size_t count, loff_t *ppos)
{
	ssize_t ret;

	cds_ssr_protect(__func__);
	ret = __wcnss_wowenable_write(file, buf, count, ppos);
	cds_ssr_unprotect(__func__);

	return ret;
}

/**
 * __wcnss_wowpattern_write() - wow_pattern debugfs handler
 * @file: debugfs file handle
 * @buf: text being written to the debugfs
 * @count: size of @buf
 * @ppos: (unused) offset into the virtual file system
 *
 * Return: number of bytes processed
 */
static ssize_t __wcnss_wowpattern_write(struct file *file,
				      const char __user *buf, size_t count,
				      loff_t *ppos)
{
	hdd_adapter_t *pAdapter = (hdd_adapter_t *) file->private_data;
	hdd_context_t *hdd_ctx;
	char cmd[MAX_USER_COMMAND_SIZE_WOWL_PATTERN + 1];
	char *sptr, *token;
	uint8_t pattern_idx = 0;
	uint8_t pattern_offset = 0;
	char *pattern_buf;
	char *pattern_mask;
	int ret;

	ENTER();

	if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
		hdd_err("Invalid adapter or adapter has invalid magic");
		return -EINVAL;
	}

	hdd_ctx = WLAN_HDD_GET_CTX(pAdapter);
	ret = wlan_hdd_validate_context(hdd_ctx);
	if (0 != ret)
		return ret;

	if (!wlan_hdd_validate_modules_state(hdd_ctx))
		return -EINVAL;

	if (!sme_is_feature_supported_by_fw(WOW)) {
		hdd_err("Wake-on-Wireless feature is not supported in firmware!");
		return -EINVAL;
	}

	if (count > MAX_USER_COMMAND_SIZE_WOWL_PATTERN) {
		hdd_err("Command length is larger than %d bytes",
			MAX_USER_COMMAND_SIZE_WOWL_PATTERN);
		return -EINVAL;
	}

	/* Get command from user */
	if (copy_from_user(cmd, buf, count))
		return -EFAULT;
	cmd[count] = '\0';
	sptr = cmd;

	/* Get pattern idx */
	token = strsep(&sptr, " ");
	if (!token)
		return -EINVAL;

	if (kstrtou8(token, 0, &pattern_idx))
		return -EINVAL;

	/* Get pattern offset */
	token = strsep(&sptr, " ");

	/* Delete pattern if no further argument */
	if (!token) {
		hdd_del_wowl_ptrn_debugfs(pAdapter, pattern_idx);

		return count;
	}

	if (kstrtou8(token, 0, &pattern_offset))
		return -EINVAL;

	/* Get pattern */
	token = strsep(&sptr, " ");
	if (!token)
		return -EINVAL;

	pattern_buf = token;

	/* Get pattern mask */
	token = strsep(&sptr, " ");
	if (!token)
		return -EINVAL;

	pattern_mask = token;
	pattern_mask[strlen(pattern_mask) - 1] = '\0';

	hdd_add_wowl_ptrn_debugfs(pAdapter, pattern_idx, pattern_offset,
				  pattern_buf, pattern_mask);
	EXIT();
	return count;
}

/**
 * wcnss_wowpattern_write() - SSR wrapper for __wcnss_wowpattern_write
 * @file: file pointer
 * @buf: buffer
 * @count: count
 * @ppos: position pointer
 *
 * Return: 0 on success, error number otherwise
 */
static ssize_t wcnss_wowpattern_write(struct file *file,
				      const char __user *buf,
				      size_t count, loff_t *ppos)
{
	ssize_t ret;

	cds_ssr_protect(__func__);
	ret = __wcnss_wowpattern_write(file, buf, count, ppos);
	cds_ssr_unprotect(__func__);

	return ret;
}

/**
 * wcnss_patterngen_write() - pattern_gen debugfs handler
 * @file: debugfs file handle
 * @buf: text being written to the debugfs
 * @count: size of @buf
 * @ppos: (unused) offset into the virtual file system
 *
 * Return: number of bytes processed
 */
static ssize_t __wcnss_patterngen_write(struct file *file,
				      const char __user *buf, size_t count,
				      loff_t *ppos)
{
	hdd_adapter_t *pAdapter;
	hdd_context_t *pHddCtx;
	tSirAddPeriodicTxPtrn *addPeriodicTxPtrnParams;
	tSirDelPeriodicTxPtrn *delPeriodicTxPtrnParams;

	char *cmd, *sptr, *token;
	uint8_t pattern_idx = 0;
	uint8_t pattern_duration = 0;
	char *pattern_buf;
	uint16_t pattern_len = 0;
	uint16_t i = 0;
	QDF_STATUS status;
	int ret;

	ENTER();

	pAdapter = (hdd_adapter_t *)file->private_data;
	if ((NULL == pAdapter) || (WLAN_HDD_ADAPTER_MAGIC != pAdapter->magic)) {
		hdd_err("Invalid adapter or adapter has invalid magic");
		return -EINVAL;
	}

	pHddCtx = WLAN_HDD_GET_CTX(pAdapter);
	ret = wlan_hdd_validate_context(pHddCtx);
	if (0 != ret)
		return ret;

	if (!wlan_hdd_validate_modules_state(pHddCtx))
		return -EINVAL;

	if (!sme_is_feature_supported_by_fw(WLAN_PERIODIC_TX_PTRN)) {
		hdd_err("Periodic Tx Pattern Offload feature is not supported in firmware!");
		return -EINVAL;
	}

	/* Get command from user */
	if (count <= MAX_USER_COMMAND_SIZE_FRAME)
		cmd = qdf_mem_malloc(count + 1);
	else {
		hdd_err("Command length is larger than %d bytes",
			MAX_USER_COMMAND_SIZE_FRAME);

		return -EINVAL;
	}

	if (!cmd) {
		hdd_err("Memory allocation for cmd failed!");
		return -ENOMEM;
	}

	if (copy_from_user(cmd, buf, count)) {
		qdf_mem_free(cmd);
		return -EFAULT;
	}
	cmd[count] = '\0';
	sptr = cmd;

	/* Get pattern idx */
	token = strsep(&sptr, " ");
	if (!token)
		goto failure;
	if (kstrtou8(token, 0, &pattern_idx))
		goto failure;

	if (pattern_idx > (MAXNUM_PERIODIC_TX_PTRNS - 1)) {
		hdd_err("Pattern index: %d is not in the range (0 ~ %d)",
			pattern_idx, MAXNUM_PERIODIC_TX_PTRNS - 1);

		goto failure;
	}

	/* Get pattern duration */
	token = strsep(&sptr, " ");
	if (!token)
		goto failure;
	if (kstrtou8(token, 0, &pattern_duration))
		goto failure;

	/* Delete pattern using index if duration is 0 */
	if (!pattern_duration) {
		delPeriodicTxPtrnParams =
			qdf_mem_malloc(sizeof(tSirDelPeriodicTxPtrn));
		if (!delPeriodicTxPtrnParams) {
			hdd_err("Memory allocation failed!");
			qdf_mem_free(cmd);
			return -ENOMEM;
		}
		delPeriodicTxPtrnParams->ucPtrnId = pattern_idx;
		delPeriodicTxPtrnParams->ucPatternIdBitmap = 1 << pattern_idx;
		qdf_copy_macaddr(&delPeriodicTxPtrnParams->mac_address,
				 &pAdapter->macAddressCurrent);

		/* Delete pattern */
		status = sme_del_periodic_tx_ptrn(pHddCtx->hHal,
						  delPeriodicTxPtrnParams);
		if (QDF_STATUS_SUCCESS != status) {
			hdd_err("sme_del_periodic_tx_ptrn() failed!");

			qdf_mem_free(delPeriodicTxPtrnParams);
			goto failure;
		}
		qdf_mem_free(cmd);
		qdf_mem_free(delPeriodicTxPtrnParams);
		return count;
	}

	/*
	 * In SAP mode allow configuration without any connection check
	 * In STA mode check if it's in connected state before adding
	 * patterns
	 */
	hdd_debug("device mode %d", pAdapter->device_mode);
	if ((QDF_STA_MODE == pAdapter->device_mode) &&
	    (!hdd_conn_is_connected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter)))) {
		hdd_err("Not in Connected state!");
		goto failure;
	}

	/* Get pattern */
	token = strsep(&sptr, " ");
	if (!token)
		goto failure;

	pattern_buf = token;
	pattern_buf[strlen(pattern_buf) - 1] = '\0';
	pattern_len = strlen(pattern_buf);

	/* Since the pattern is a hex string, 2 characters represent 1 byte. */
	if (pattern_len % 2) {
		hdd_err("Malformed pattern!");

		goto failure;
	} else
		pattern_len >>= 1;

	if (pattern_len < 14 || pattern_len > PERIODIC_TX_PTRN_MAX_SIZE) {
		hdd_err("Not an 802.3 frame!");

		goto failure;
	}

	addPeriodicTxPtrnParams = qdf_mem_malloc(sizeof(tSirAddPeriodicTxPtrn));
	if (!addPeriodicTxPtrnParams) {
		hdd_err("Memory allocation failed!");
		qdf_mem_free(cmd);
		return -ENOMEM;
	}

	addPeriodicTxPtrnParams->ucPtrnId = pattern_idx;
	addPeriodicTxPtrnParams->usPtrnIntervalMs = pattern_duration * 500;
	addPeriodicTxPtrnParams->ucPtrnSize = pattern_len;
	qdf_copy_macaddr(&addPeriodicTxPtrnParams->mac_address,
			 &pAdapter->macAddressCurrent);

	/* Extract the pattern */
	for (i = 0; i < addPeriodicTxPtrnParams->ucPtrnSize; i++) {
		addPeriodicTxPtrnParams->ucPattern[i] =
			(hex_to_bin(pattern_buf[0]) << 4) +
			hex_to_bin(pattern_buf[1]);

		/* Skip to next byte */
		pattern_buf += 2;
	}

	/* Add pattern */
	status = sme_add_periodic_tx_ptrn(pHddCtx->hHal,
					  addPeriodicTxPtrnParams);
	if (QDF_STATUS_SUCCESS != status) {
		hdd_err("sme_add_periodic_tx_ptrn() failed!");

		qdf_mem_free(addPeriodicTxPtrnParams);
		goto failure;
	}
	qdf_mem_free(cmd);
	qdf_mem_free(addPeriodicTxPtrnParams);
	EXIT();
	return count;

failure:
	hdd_err("Invalid input. Input format is: ptrn_idx duration pattern");
	qdf_mem_free(cmd);
	return -EINVAL;
}

/**
 * wcnss_patterngen_write() - SSR wrapper for __wcnss_patterngen_write
 * @file: file pointer
 * @buf: buffer
 * @count: count
 * @ppos: position pointer
 *
 * Return: 0 on success, error number otherwise
 */
static ssize_t wcnss_patterngen_write(struct file *file,
				      const char __user *buf,
				      size_t count, loff_t *ppos)
{
	ssize_t ret;

	cds_ssr_protect(__func__);
	ret = __wcnss_patterngen_write(file, buf, count, ppos);
	cds_ssr_unprotect(__func__);

	return ret;
}

#ifdef WLAN_POWER_DEBUGFS
struct power_stats_priv {
	struct power_stats_response power_stats;
};

static void hdd_power_debugstats_dealloc(void *priv)
{
	struct power_stats_priv *stats = priv;
	qdf_mem_free(stats->power_stats.debug_registers);
}

static void hdd_power_debugstats_cb(struct power_stats_response *response,
				    void *context)
{
	struct hdd_request *request;
	struct power_stats_priv *priv;
	uint32_t *debug_registers;
	uint32_t debug_registers_len;

	ENTER();

	request = hdd_request_get(context);
	if (!request) {
		hdd_err("Obsolete request");
		return;
	}

	priv = hdd_request_priv(request);

	/* copy fixed-sized data */
	priv->power_stats = *response;

	/* copy variable-size data */
	if (response->num_debug_register) {
		debug_registers_len = (sizeof(response->debug_registers[0]) *
				       response->num_debug_register);
		debug_registers = qdf_mem_malloc(debug_registers_len);
		priv->power_stats.debug_registers = debug_registers;
		if (debug_registers) {
			qdf_mem_copy(debug_registers,
				     response->debug_registers,
				     debug_registers_len);
		} else {
			hdd_err("Power stats memory alloc fails!");
			priv->power_stats.num_debug_register = 0;
		}
	}
	hdd_request_complete(request);
	hdd_request_put(request);
	EXIT();
}

/**
 * __wlan_hdd_read_power_debugfs() - API to collect Chip power stats from FW
 * @file: file pointer
 * @buf: buffer
 * @count: count
 * @pos: position pointer
 *
 * Return: Number of bytes read on success, error number otherwise
 */
static ssize_t __wlan_hdd_read_power_debugfs(struct file *file,
		char __user *buf,
		size_t count, loff_t *pos)
{
	hdd_adapter_t *adapter;
	hdd_context_t *hdd_ctx;
	QDF_STATUS status;
	struct power_stats_response *chip_power_stats;
	ssize_t ret_cnt = 0;
	int j;
	unsigned int len = 0;
	char *power_debugfs_buf = NULL;
	void *cookie;
	struct hdd_request *request;
	struct power_stats_priv	*priv;
	static const struct hdd_request_params params = {
		.priv_size = sizeof(*priv),
		.timeout_ms = WLAN_WAIT_TIME_STATS,
		.dealloc = hdd_power_debugstats_dealloc,
	};

	ENTER();
	adapter = (hdd_adapter_t *)file->private_data;
	if ((!adapter) || (WLAN_HDD_ADAPTER_MAGIC != adapter->magic)) {
		hdd_err("Invalid adapter or adapter has invalid magic");
		return -EINVAL;
	}

	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
	ret_cnt = wlan_hdd_validate_context(hdd_ctx);
	if (0 != ret_cnt)
		return ret_cnt;

	if (!wlan_hdd_validate_modules_state(hdd_ctx))
		return -EINVAL;

	request = hdd_request_alloc(&params);
	if (!request) {
		hdd_err("Request allocation failure");
		return -ENOMEM;
	}
	cookie = hdd_request_cookie(request);

	status = sme_power_debug_stats_req(hdd_ctx->hHal,
					   hdd_power_debugstats_cb,
					   cookie);
	if (QDF_IS_STATUS_ERROR(status)) {
		hdd_err("chip power stats request failed");
		ret_cnt = -EINVAL;
		goto cleanup;
	}

	ret_cnt = hdd_request_wait_for_response(request);
	if (ret_cnt) {
		hdd_err("Target response timed out Power stats");
		ret_cnt = -ETIMEDOUT;
		goto cleanup;
	}

	priv = hdd_request_priv(request);
	chip_power_stats = &priv->power_stats;

	power_debugfs_buf = qdf_mem_malloc(POWER_DEBUGFS_BUFFER_MAX_LEN);
	if (!power_debugfs_buf) {
		hdd_err("Power stats buffer alloc fails!");
		ret_cnt = -EINVAL;
		goto cleanup;
	}

	len += scnprintf(power_debugfs_buf, POWER_DEBUGFS_BUFFER_MAX_LEN,
			"POWER DEBUG STATS\n=================\n"
			"cumulative_sleep_time_ms: %d\n"
			"cumulative_total_on_time_ms: %d\n"
			"deep_sleep_enter_counter: %d\n"
			"last_deep_sleep_enter_tstamp_ms: %d\n"
			"debug_register_fmt: %d\n"
			"num_debug_register: %d\n",
			chip_power_stats->cumulative_sleep_time_ms,
			chip_power_stats->cumulative_total_on_time_ms,
			chip_power_stats->deep_sleep_enter_counter,
			chip_power_stats->last_deep_sleep_enter_tstamp_ms,
			chip_power_stats->debug_register_fmt,
			chip_power_stats->num_debug_register);

	for (j = 0; j < chip_power_stats->num_debug_register; j++) {
		if ((POWER_DEBUGFS_BUFFER_MAX_LEN - len) > 0)
			len += scnprintf(power_debugfs_buf + len,
					POWER_DEBUGFS_BUFFER_MAX_LEN - len,
					"debug_registers[%d]: 0x%x\n", j,
					chip_power_stats->debug_registers[j]);
		else
			j = chip_power_stats->num_debug_register;
	}

	ret_cnt = simple_read_from_buffer(buf, count, pos,
					  power_debugfs_buf, len);

 cleanup:
	qdf_mem_free(power_debugfs_buf);
	hdd_request_put(request);

	return ret_cnt;
}

/**
 * wlan_hdd_read_power_debugfs() - SSR wrapper function to read power debugfs
 * @file: file pointer
 * @buf: buffer
 * @count: count
 * @pos: position pointer
 *
 * Return: Number of bytes read on success, error number otherwise
 */
static ssize_t wlan_hdd_read_power_debugfs(struct file *file,
		char __user *buf,
		size_t count, loff_t *pos)
{
	int ret;

	cds_ssr_protect(__func__);
	ret = __wlan_hdd_read_power_debugfs(file, buf, count, pos);
	cds_ssr_unprotect(__func__);

	return ret;
}

/**
 * __wlan_hdd_open_power_debugfs() - Function to save private on open
 * @inode: Pointer to inode structure
 * @file: file pointer
 *
 * Return: zero
 */
static int __wlan_hdd_open_power_debugfs(struct inode *inode, struct file *file)
{
	file->private_data = inode->i_private;
	return 0;
}


/**
 * wlan_hdd_open_power_debugfs() - SSR wrapper function to save private on open
 * @inode: Pointer to inode structure
 * @file: file pointer
 *
 * Return: zero
 */
static int wlan_hdd_open_power_debugfs(struct inode *inode, struct file *file)
{
	int ret;

	cds_ssr_protect(__func__);
	ret = __wlan_hdd_open_power_debugfs(inode, file);
	cds_ssr_unprotect(__func__);

	return ret;
}
#endif

/**
 * __wcnss_debugfs_open() - Generic debugfs open() handler
 * @inode: inode of the debugfs file
 * @file: file handle of the debugfs file
 *
 * Return: 0
 */
static int __wcnss_debugfs_open(struct inode *inode, struct file *file)
{
	hdd_adapter_t *adapter;
	hdd_context_t *hdd_ctx;
	int ret;

	ENTER();

	if (inode->i_private)
		file->private_data = inode->i_private;

	adapter = (hdd_adapter_t *)file->private_data;
	if ((NULL == adapter) || (WLAN_HDD_ADAPTER_MAGIC != 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 (0 != ret)
		return ret;
	EXIT();
	return 0;
}

/**
 * wcnss_debugfs_open() - SSR wrapper for __wcnss_debugfs_open
 * @inode: inode pointer
 * @file: file pointer
 *
 * Return: 0 on success, error number otherwise
 */
static int wcnss_debugfs_open(struct inode *inode, struct file *file)
{
	int ret;

	cds_ssr_protect(__func__);
	ret = __wcnss_debugfs_open(inode, file);
	cds_ssr_unprotect(__func__);

	return ret;
}

static const struct file_operations fops_wowenable = {
	.write = wcnss_wowenable_write,
	.open = wcnss_debugfs_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static const struct file_operations fops_wowpattern = {
	.write = wcnss_wowpattern_write,
	.open = wcnss_debugfs_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

static const struct file_operations fops_patterngen = {
	.write = wcnss_patterngen_write,
	.open = wcnss_debugfs_open,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

#ifdef WLAN_POWER_DEBUGFS
static const struct file_operations fops_powerdebugs = {
	.read = wlan_hdd_read_power_debugfs,
	.open = wlan_hdd_open_power_debugfs,
	.owner = THIS_MODULE,
	.llseek = default_llseek,
};

/**
 * wlan_hdd_create_power_stats_file() - API to create power stats file
 * @adapter: interface adapter pointer
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS wlan_hdd_create_power_stats_file(hdd_adapter_t *adapter)
{
	if (!debugfs_create_file("power_stats", 00400 | 00040 | 00004,
				adapter->debugfs_phy, adapter,
				&fops_powerdebugs))
		return QDF_STATUS_E_FAILURE;

	return QDF_STATUS_SUCCESS;
}

#else
static QDF_STATUS wlan_hdd_create_power_stats_file(hdd_adapter_t *adapter)
{
	return QDF_STATUS_SUCCESS;
}
#endif

/**
 * hdd_debugfs_init() - Initialize debugfs interface
 * @adapter: interface adapter pointer
 *
 * Register support for the debugfs files supported by the driver.
 *
 * NB: The current implementation only supports debugfs operations
 * on the primary interface, i.e. wlan0
 *
 * Return: QDF_STATUS_SUCCESS if all files registered,
 *	   QDF_STATUS_E_FAILURE on failure
 */
QDF_STATUS hdd_debugfs_init(hdd_adapter_t *adapter)
{
	struct net_device *dev = adapter->dev;
	adapter->debugfs_phy = debugfs_create_dir(dev->name, 0);

	if (NULL == adapter->debugfs_phy)
		return QDF_STATUS_E_FAILURE;

	if (NULL == debugfs_create_file("wow_enable", 00400 | 00200,
					adapter->debugfs_phy, adapter,
					&fops_wowenable))
		return QDF_STATUS_E_FAILURE;

	if (NULL == debugfs_create_file("wow_pattern", 00400 | 00200,
					adapter->debugfs_phy, adapter,
					&fops_wowpattern))
		return QDF_STATUS_E_FAILURE;

	if (NULL == debugfs_create_file("pattern_gen", 00400 | 00200,
					adapter->debugfs_phy, adapter,
					&fops_patterngen))
		return QDF_STATUS_E_FAILURE;

	if (QDF_STATUS_SUCCESS != wlan_hdd_create_power_stats_file(adapter))
		return QDF_STATUS_E_FAILURE;

	if (0 != wlan_hdd_create_ll_stats_file(adapter))
		return QDF_STATUS_E_FAILURE;

	return QDF_STATUS_SUCCESS;
}

/**
 * hdd_debugfs_exit() - Shutdown debugfs interface
 * @adapter: interface adapter pointer
 *
 * Unregister support for the debugfs files supported by the driver.
 *
 * Return: None
 */
void hdd_debugfs_exit(hdd_adapter_t *adapter)
{
	debugfs_remove_recursive(adapter->debugfs_phy);
}
#endif /* #ifdef WLAN_OPEN_SOURCE */
