Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: ISC
config WCN36XX
tristate "Qualcomm Atheros WCN3660/3680 support"
depends on MAC80211 && HAS_DMA
depends on QCOM_WCNSS_CTRL || QCOM_WCNSS_CTRL=n
depends on RPMSG || RPMSG=n
help
This module adds support for wireless adapters based on
Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets.
If you choose to build a module, it'll be called wcn36xx.
config WCN36XX_DEBUGFS
bool "WCN36XX debugfs support"
depends on WCN36XX
help
Enabled debugfs support
If unsure, say Y to make it easier to debug problems.
@@ -0,0 +1,11 @@
# SPDX-License-Identifier: ISC
obj-$(CONFIG_WCN36XX) := wcn36xx.o
wcn36xx-y += main.o \
dxe.o \
txrx.o \
smd.o \
pmc.o \
debug.o \
firmware.o
wcn36xx-$(CONFIG_NL80211_TESTMODE) += testmode.o
@@ -0,0 +1,213 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include "wcn36xx.h"
#include "debug.h"
#include "pmc.h"
#include "firmware.h"
#ifdef CONFIG_WCN36XX_DEBUGFS
static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wcn36xx *wcn = file->private_data;
struct wcn36xx_vif *vif_priv = NULL;
struct ieee80211_vif *vif = NULL;
char buf[3];
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
vif = wcn36xx_priv_to_vif(vif_priv);
if (NL80211_IFTYPE_STATION == vif->type) {
if (vif_priv->pw_state == WCN36XX_BMPS)
buf[0] = '1';
else
buf[0] = '0';
break;
}
}
buf[1] = '\n';
buf[2] = 0x00;
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
}
static ssize_t write_file_bool_bmps(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wcn36xx *wcn = file->private_data;
struct wcn36xx_vif *vif_priv = NULL;
struct ieee80211_vif *vif = NULL;
char buf[32];
int buf_size;
buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
switch (buf[0]) {
case 'y':
case 'Y':
case '1':
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
vif = wcn36xx_priv_to_vif(vif_priv);
if (NL80211_IFTYPE_STATION == vif->type) {
wcn36xx_enable_keep_alive_null_packet(wcn, vif);
wcn36xx_pmc_enter_bmps_state(wcn, vif);
}
}
break;
case 'n':
case 'N':
case '0':
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
vif = wcn36xx_priv_to_vif(vif_priv);
if (NL80211_IFTYPE_STATION == vif->type)
wcn36xx_pmc_exit_bmps_state(wcn, vif);
}
break;
}
return count;
}
static const struct file_operations fops_wcn36xx_bmps = {
.open = simple_open,
.read = read_file_bool_bmps,
.write = write_file_bool_bmps,
};
static ssize_t write_file_dump(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wcn36xx *wcn = file->private_data;
char buf[255], *tmp;
int buf_size;
u32 arg[WCN36xx_MAX_DUMP_ARGS];
int i;
memset(buf, 0, sizeof(buf));
memset(arg, 0, sizeof(arg));
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
tmp = buf;
for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) {
char *begin;
begin = strsep(&tmp, " ");
if (begin == NULL)
break;
if (kstrtos32(begin, 0, &arg[i]) != 0)
break;
}
wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2],
arg[3], arg[4]);
wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]);
return count;
}
static const struct file_operations fops_wcn36xx_dump = {
.open = simple_open,
.write = write_file_dump,
};
static ssize_t read_file_firmware_feature_caps(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct wcn36xx *wcn = file->private_data;
size_t len = 0, buf_len = 2048;
char *buf;
int i;
int ret;
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
mutex_lock(&wcn->hal_mutex);
for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
if (wcn36xx_firmware_get_feat_caps(wcn->fw_feat_caps, i)) {
len += scnprintf(buf + len, buf_len - len, "%s\n",
wcn36xx_firmware_get_cap_name(i));
}
if (len >= buf_len)
break;
}
mutex_unlock(&wcn->hal_mutex);
ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
kfree(buf);
return ret;
}
static const struct file_operations fops_wcn36xx_firmware_feat_caps = {
.open = simple_open,
.read = read_file_firmware_feature_caps,
};
#define ADD_FILE(name, mode, fop, priv_data) \
do { \
struct dentry *d; \
d = debugfs_create_file(__stringify(name), \
mode, dfs->rootdir, \
priv_data, fop); \
dfs->file_##name.dentry = d; \
if (IS_ERR(d)) { \
wcn36xx_warn("Create the debugfs entry failed");\
dfs->file_##name.dentry = NULL; \
} \
} while (0)
void wcn36xx_debugfs_init(struct wcn36xx *wcn)
{
struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME,
wcn->hw->wiphy->debugfsdir);
if (IS_ERR(dfs->rootdir)) {
wcn36xx_warn("Create the debugfs failed\n");
dfs->rootdir = NULL;
}
ADD_FILE(bmps_switcher, 0600, &fops_wcn36xx_bmps, wcn);
ADD_FILE(dump, 0200, &fops_wcn36xx_dump, wcn);
ADD_FILE(firmware_feat_caps, 0200,
&fops_wcn36xx_firmware_feat_caps, wcn);
}
void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
{
struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
debugfs_remove_recursive(dfs->rootdir);
}
#endif /* CONFIG_WCN36XX_DEBUGFS */
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#ifndef _WCN36XX_DEBUG_H_
#define _WCN36XX_DEBUG_H_
#include <linux/kernel.h>
#define WCN36xx_MAX_DUMP_ARGS 5
#ifdef CONFIG_WCN36XX_DEBUGFS
struct wcn36xx_dfs_file {
struct dentry *dentry;
u32 value;
};
struct wcn36xx_dfs_entry {
struct dentry *rootdir;
struct wcn36xx_dfs_file file_bmps_switcher;
struct wcn36xx_dfs_file file_dump;
struct wcn36xx_dfs_file file_firmware_feat_caps;
};
void wcn36xx_debugfs_init(struct wcn36xx *wcn);
void wcn36xx_debugfs_exit(struct wcn36xx *wcn);
#else
static inline void wcn36xx_debugfs_init(struct wcn36xx *wcn)
{
}
static inline void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
{
}
#endif /* CONFIG_WCN36XX_DEBUGFS */
#endif /* _WCN36XX_DEBUG_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,471 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#ifndef _DXE_H_
#define _DXE_H_
#include "wcn36xx.h"
/*
TX_LOW = DMA0
TX_HIGH = DMA4
RX_LOW = DMA1
RX_HIGH = DMA3
H2H_TEST_RX_TX = DMA2
*/
/* DXE registers */
#define WCN36XX_DXE_MEM_REG 0
#define WCN36XX_CCU_DXE_INT_SELECT_RIVA 0x310
#define WCN36XX_CCU_DXE_INT_SELECT_PRONTO 0x10dc
/* Descriptor valid */
#define WCN36xx_DXE_CTRL_VLD BIT(0)
/* End of packet */
#define WCN36xx_DXE_CTRL_EOP BIT(3)
/* BD handling bit */
#define WCN36xx_DXE_CTRL_BDH BIT(4)
/* Source is a queue */
#define WCN36xx_DXE_CTRL_SIQ BIT(5)
/* Destination is a queue */
#define WCN36xx_DXE_CTRL_DIQ BIT(6)
/* Pointer address is a queue */
#define WCN36xx_DXE_CTRL_PIQ BIT(7)
/* Release PDU when done */
#define WCN36xx_DXE_CTRL_PDU_REL BIT(8)
/* STOP channel processing */
#define WCN36xx_DXE_CTRL_STOP BIT(16)
/* INT on descriptor done */
#define WCN36xx_DXE_CTRL_INT BIT(17)
/* Endian byte swap enable */
#define WCN36xx_DXE_CTRL_SWAP BIT(20)
/* Master endianness */
#define WCN36xx_DXE_CTRL_ENDIANNESS BIT(21)
/* Transfer type */
#define WCN36xx_DXE_CTRL_XTYPE_SHIFT 1
#define WCN36xx_DXE_CTRL_XTYPE_MASK GENMASK(2, WCN36xx_DXE_CTRL_XTYPE_SHIFT)
#define WCN36xx_DXE_CTRL_XTYPE_SET(x) ((x) << WCN36xx_DXE_CTRL_XTYPE_SHIFT)
/* BMU Threshold select */
#define WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT 9
#define WCN36xx_DXE_CTRL_BTHLD_SEL_MASK GENMASK(12, WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT)
#define WCN36xx_DXE_CTRL_BTHLD_SEL_SET(x) ((x) << WCN36xx_DXE_CTRL_BTHLD_SEL_SHIFT)
/* Priority */
#define WCN36xx_DXE_CTRL_PRIO_SHIFT 13
#define WCN36xx_DXE_CTRL_PRIO_MASK GENMASK(15, WCN36xx_DXE_CTRL_PRIO_SHIFT)
#define WCN36xx_DXE_CTRL_PRIO_SET(x) ((x) << WCN36xx_DXE_CTRL_PRIO_SHIFT)
/* BD Template index */
#define WCN36xx_DXE_CTRL_BDT_IDX_SHIFT 18
#define WCN36xx_DXE_CTRL_BDT_IDX_MASK GENMASK(19, WCN36xx_DXE_CTRL_BDT_IDX_SHIFT)
#define WCN36xx_DXE_CTRL_BDT_IDX_SET(x) ((x) << WCN36xx_DXE_CTRL_BDT_IDX_SHIFT)
/* Transfer types: */
/* Host to host */
#define WCN36xx_DXE_XTYPE_H2H (0)
/* Host to BMU */
#define WCN36xx_DXE_XTYPE_H2B (2)
/* BMU to host */
#define WCN36xx_DXE_XTYPE_B2H (3)
#define WCN36XX_DXE_CTRL_TX_L (WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | \
WCN36xx_DXE_CTRL_PRIO_SET(4) | WCN36xx_DXE_CTRL_INT | \
WCN36xx_DXE_CTRL_SWAP | WCN36xx_DXE_CTRL_ENDIANNESS)
#define WCN36XX_DXE_CTRL_TX_H (WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | \
WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_INT | \
WCN36xx_DXE_CTRL_SWAP | WCN36xx_DXE_CTRL_ENDIANNESS)
#define WCN36XX_DXE_CTRL_RX_L (WCN36xx_DXE_CTRL_VLD | \
WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_SIQ | \
WCN36xx_DXE_CTRL_PDU_REL | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(6) | \
WCN36xx_DXE_CTRL_PRIO_SET(5) | WCN36xx_DXE_CTRL_INT | \
WCN36xx_DXE_CTRL_SWAP)
#define WCN36XX_DXE_CTRL_RX_H (WCN36xx_DXE_CTRL_VLD | \
WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_SIQ | \
WCN36xx_DXE_CTRL_PDU_REL | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(8) | \
WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_INT | \
WCN36xx_DXE_CTRL_SWAP)
#define WCN36XX_DXE_CTRL_TX_H_BD (WCN36xx_DXE_CTRL_VLD | \
WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | \
WCN36xx_DXE_CTRL_PRIO_SET(6) | WCN36xx_DXE_CTRL_SWAP | \
WCN36xx_DXE_CTRL_ENDIANNESS)
#define WCN36XX_DXE_CTRL_TX_H_SKB (WCN36xx_DXE_CTRL_VLD | \
WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_DIQ | \
WCN36xx_DXE_CTRL_BTHLD_SEL_SET(7) | WCN36xx_DXE_CTRL_PRIO_SET(6) | \
WCN36xx_DXE_CTRL_INT | WCN36xx_DXE_CTRL_SWAP | \
WCN36xx_DXE_CTRL_ENDIANNESS)
#define WCN36XX_DXE_CTRL_TX_L_BD (WCN36xx_DXE_CTRL_VLD | \
WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CTRL_DIQ | WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | \
WCN36xx_DXE_CTRL_PRIO_SET(4) | WCN36xx_DXE_CTRL_SWAP | \
WCN36xx_DXE_CTRL_ENDIANNESS)
#define WCN36XX_DXE_CTRL_TX_L_SKB (WCN36xx_DXE_CTRL_VLD | \
WCN36xx_DXE_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CTRL_EOP | WCN36xx_DXE_CTRL_DIQ | \
WCN36xx_DXE_CTRL_BTHLD_SEL_SET(5) | WCN36xx_DXE_CTRL_PRIO_SET(4) | \
WCN36xx_DXE_CTRL_INT | WCN36xx_DXE_CTRL_SWAP | \
WCN36xx_DXE_CTRL_ENDIANNESS)
/* TODO This must calculated properly but not hardcoded */
#define WCN36XX_DXE_WQ_TX_L(wcn) ((wcn)->is_pronto_v3 ? 0x6 : 0x17)
#define WCN36XX_DXE_WQ_TX_H(wcn) ((wcn)->is_pronto_v3 ? 0x6 : 0x17)
#define WCN36XX_DXE_WQ_RX_L 0xB
#define WCN36XX_DXE_WQ_RX_H 0x4
/* Channel enable or restart */
#define WCN36xx_DXE_CH_CTRL_EN BIT(0)
/* End of packet bit */
#define WCN36xx_DXE_CH_CTRL_EOP BIT(3)
/* BD Handling bit */
#define WCN36xx_DXE_CH_CTRL_BDH BIT(4)
/* Source is queue */
#define WCN36xx_DXE_CH_CTRL_SIQ BIT(5)
/* Destination is queue */
#define WCN36xx_DXE_CH_CTRL_DIQ BIT(6)
/* Pointer descriptor is queue */
#define WCN36xx_DXE_CH_CTRL_PIQ BIT(7)
/* Relase PDU when done */
#define WCN36xx_DXE_CH_CTRL_PDU_REL BIT(8)
/* Stop channel processing */
#define WCN36xx_DXE_CH_CTRL_STOP BIT(16)
/* Enable external descriptor interrupt */
#define WCN36xx_DXE_CH_CTRL_INE_ED BIT(17)
/* Enable channel interrupt on errors */
#define WCN36xx_DXE_CH_CTRL_INE_ERR BIT(18)
/* Enable Channel interrupt when done */
#define WCN36xx_DXE_CH_CTRL_INE_DONE BIT(19)
/* External descriptor enable */
#define WCN36xx_DXE_CH_CTRL_EDEN BIT(20)
/* Wait for valid bit */
#define WCN36xx_DXE_CH_CTRL_EDVEN BIT(21)
/* Endianness is little endian*/
#define WCN36xx_DXE_CH_CTRL_ENDIANNESS BIT(26)
/* Abort transfer */
#define WCN36xx_DXE_CH_CTRL_ABORT BIT(27)
/* Long descriptor format */
#define WCN36xx_DXE_CH_CTRL_DFMT BIT(28)
/* Endian byte swap enable */
#define WCN36xx_DXE_CH_CTRL_SWAP BIT(31)
/* Transfer type */
#define WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT 1
#define WCN36xx_DXE_CH_CTRL_XTYPE_MASK GENMASK(2, WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT)
#define WCN36xx_DXE_CH_CTRL_XTYPE_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_XTYPE_SHIFT)
/* Channel BMU Threshold select */
#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT 9
#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_MASK GENMASK(12, WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT)
#define WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SHIFT)
/* Channel Priority */
#define WCN36xx_DXE_CH_CTRL_PRIO_SHIFT 13
#define WCN36xx_DXE_CH_CTRL_PRIO_MASK GENMASK(15, WCN36xx_DXE_CH_CTRL_PRIO_SHIFT)
#define WCN36xx_DXE_CH_CTRL_PRIO_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_PRIO_SHIFT)
/* Counter select */
#define WCN36xx_DXE_CH_CTRL_SEL_SHIFT 22
#define WCN36xx_DXE_CH_CTRL_SEL_MASK GENMASK(25, WCN36xx_DXE_CH_CTRL_SEL_SHIFT)
#define WCN36xx_DXE_CH_CTRL_SEL_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_SEL_SHIFT)
/* Channel BD template index */
#define WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT 29
#define WCN36xx_DXE_CH_CTRL_BDT_IDX_MASK GENMASK(30, WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT)
#define WCN36xx_DXE_CH_CTRL_BDT_IDX_SET(x) ((x) << WCN36xx_DXE_CH_CTRL_BDT_IDX_SHIFT)
/* DXE default control register values */
#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L (WCN36xx_DXE_CH_CTRL_EN | \
WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_SIQ | \
WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(6) | \
WCN36xx_DXE_CH_CTRL_PRIO_SET(5) | WCN36xx_DXE_CH_CTRL_INE_ED | \
WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
WCN36xx_DXE_CH_CTRL_SEL_SET(1) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
WCN36xx_DXE_CH_CTRL_SWAP)
#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H (WCN36xx_DXE_CH_CTRL_EN | \
WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_B2H) | \
WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_SIQ | \
WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(8) | \
WCN36xx_DXE_CH_CTRL_PRIO_SET(6) | WCN36xx_DXE_CH_CTRL_INE_ED | \
WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
WCN36xx_DXE_CH_CTRL_SEL_SET(3) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
WCN36xx_DXE_CH_CTRL_SWAP)
#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H (WCN36xx_DXE_CH_CTRL_EN | \
WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_DIQ | \
WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(7) | \
WCN36xx_DXE_CH_CTRL_PRIO_SET(6) | WCN36xx_DXE_CH_CTRL_INE_ED | \
WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
WCN36xx_DXE_CH_CTRL_SEL_SET(4) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
WCN36xx_DXE_CH_CTRL_SWAP)
#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L (WCN36xx_DXE_CH_CTRL_EN | \
WCN36xx_DXE_CH_CTRL_XTYPE_SET(WCN36xx_DXE_XTYPE_H2B) | \
WCN36xx_DXE_CH_CTRL_EOP | WCN36xx_DXE_CH_CTRL_DIQ | \
WCN36xx_DXE_CH_CTRL_PDU_REL | WCN36xx_DXE_CH_CTRL_BTHLD_SEL_SET(5) | \
WCN36xx_DXE_CH_CTRL_PRIO_SET(4) | WCN36xx_DXE_CH_CTRL_INE_ED | \
WCN36xx_DXE_CH_CTRL_INE_ERR | WCN36xx_DXE_CH_CTRL_INE_DONE | \
WCN36xx_DXE_CH_CTRL_EDEN | WCN36xx_DXE_CH_CTRL_EDVEN | \
WCN36xx_DXE_CH_CTRL_SEL_SET(0) | WCN36xx_DXE_CH_CTRL_ENDIANNESS | \
WCN36xx_DXE_CH_CTRL_SWAP)
/* Common DXE registers */
#define WCN36XX_DXE_MEM_CSR (WCN36XX_DXE_MEM_REG + 0x00)
#define WCN36XX_DXE_REG_CSR_RESET (WCN36XX_DXE_MEM_REG + 0x00)
#define WCN36XX_DXE_ENCH_ADDR (WCN36XX_DXE_MEM_REG + 0x04)
#define WCN36XX_DXE_REG_CH_EN (WCN36XX_DXE_MEM_REG + 0x08)
#define WCN36XX_DXE_REG_CH_DONE (WCN36XX_DXE_MEM_REG + 0x0C)
#define WCN36XX_DXE_REG_CH_ERR (WCN36XX_DXE_MEM_REG + 0x10)
#define WCN36XX_DXE_INT_MASK_REG (WCN36XX_DXE_MEM_REG + 0x18)
#define WCN36XX_DXE_INT_SRC_RAW_REG (WCN36XX_DXE_MEM_REG + 0x20)
/* #define WCN36XX_DXE_INT_CH6_MASK 0x00000040 */
/* #define WCN36XX_DXE_INT_CH5_MASK 0x00000020 */
#define WCN36XX_DXE_INT_CH4_MASK 0x00000010
#define WCN36XX_DXE_INT_CH3_MASK 0x00000008
/* #define WCN36XX_DXE_INT_CH2_MASK 0x00000004 */
#define WCN36XX_DXE_INT_CH1_MASK 0x00000002
#define WCN36XX_DXE_INT_CH0_MASK 0x00000001
#define WCN36XX_DXE_0_INT_CLR (WCN36XX_DXE_MEM_REG + 0x30)
#define WCN36XX_DXE_0_INT_ED_CLR (WCN36XX_DXE_MEM_REG + 0x34)
#define WCN36XX_DXE_0_INT_DONE_CLR (WCN36XX_DXE_MEM_REG + 0x38)
#define WCN36XX_DXE_0_INT_ERR_CLR (WCN36XX_DXE_MEM_REG + 0x3C)
#define WCN36XX_CH_STAT_INT_DONE_MASK 0x00008000
#define WCN36XX_CH_STAT_INT_ERR_MASK 0x00004000
#define WCN36XX_CH_STAT_INT_ED_MASK 0x00002000
#define WCN36XX_DXE_0_CH0_STATUS (WCN36XX_DXE_MEM_REG + 0x404)
#define WCN36XX_DXE_0_CH1_STATUS (WCN36XX_DXE_MEM_REG + 0x444)
#define WCN36XX_DXE_0_CH2_STATUS (WCN36XX_DXE_MEM_REG + 0x484)
#define WCN36XX_DXE_0_CH3_STATUS (WCN36XX_DXE_MEM_REG + 0x4C4)
#define WCN36XX_DXE_0_CH4_STATUS (WCN36XX_DXE_MEM_REG + 0x504)
#define WCN36XX_DXE_REG_RESET 0x5c89
/* Temporary BMU Workqueue 4 */
#define WCN36XX_DXE_BMU_WQ_RX_LOW 0xB
#define WCN36XX_DXE_BMU_WQ_RX_HIGH 0x4
/* DMA channel offset */
#define WCN36XX_DXE_TX_LOW_OFFSET 0x400
#define WCN36XX_DXE_TX_HIGH_OFFSET 0x500
#define WCN36XX_DXE_RX_LOW_OFFSET 0x440
#define WCN36XX_DXE_RX_HIGH_OFFSET 0x4C0
/* Address of the next DXE descriptor */
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR 0x001C
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_LOW_OFFSET + \
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_HIGH_OFFSET + \
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_LOW_OFFSET + \
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_HIGH_OFFSET + \
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
/* DXE Descriptor source address */
#define WCN36XX_DXE_CH_SRC_ADDR 0x000C
#define WCN36XX_DXE_CH_SRC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_LOW_OFFSET + \
WCN36XX_DXE_CH_SRC_ADDR)
#define WCN36XX_DXE_CH_SRC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_HIGH_OFFSET + \
WCN36XX_DXE_CH_SRC_ADDR)
/* DXE Descriptor address destination address */
#define WCN36XX_DXE_CH_DEST_ADDR 0x0014
#define WCN36XX_DXE_CH_DEST_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_LOW_OFFSET + \
WCN36XX_DXE_CH_DEST_ADDR)
#define WCN36XX_DXE_CH_DEST_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_HIGH_OFFSET + \
WCN36XX_DXE_CH_DEST_ADDR)
#define WCN36XX_DXE_CH_DEST_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_LOW_OFFSET + \
WCN36XX_DXE_CH_DEST_ADDR)
#define WCN36XX_DXE_CH_DEST_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_HIGH_OFFSET + \
WCN36XX_DXE_CH_DEST_ADDR)
/* Interrupt status */
#define WCN36XX_DXE_CH_STATUS_REG_ADDR 0x0004
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_LOW_OFFSET + \
WCN36XX_DXE_CH_STATUS_REG_ADDR)
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_HIGH_OFFSET + \
WCN36XX_DXE_CH_STATUS_REG_ADDR)
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_LOW_OFFSET + \
WCN36XX_DXE_CH_STATUS_REG_ADDR)
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_HIGH_OFFSET + \
WCN36XX_DXE_CH_STATUS_REG_ADDR)
/* DXE default control register */
#define WCN36XX_DXE_REG_CTL_RX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_LOW_OFFSET)
#define WCN36XX_DXE_REG_CTL_RX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_RX_HIGH_OFFSET)
#define WCN36XX_DXE_REG_CTL_TX_H (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_HIGH_OFFSET)
#define WCN36XX_DXE_REG_CTL_TX_L (WCN36XX_DXE_MEM_REG + \
WCN36XX_DXE_TX_LOW_OFFSET)
#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400
#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200
/* Interrupt control channel mask */
#define WCN36XX_INT_MASK_CHAN_TX_L 0x00000001
#define WCN36XX_INT_MASK_CHAN_RX_L 0x00000002
#define WCN36XX_INT_MASK_CHAN_RX_H 0x00000008
#define WCN36XX_INT_MASK_CHAN_TX_H 0x00000010
#define WCN36XX_BD_CHUNK_SIZE 128
#define WCN36XX_PKT_SIZE 0xF20
enum wcn36xx_dxe_ch_type {
WCN36XX_DXE_CH_TX_L,
WCN36XX_DXE_CH_TX_H,
WCN36XX_DXE_CH_RX_L,
WCN36XX_DXE_CH_RX_H
};
/* amount of descriptors per channel */
enum wcn36xx_dxe_ch_desc_num {
WCN36XX_DXE_CH_DESC_NUMB_TX_L = 128,
WCN36XX_DXE_CH_DESC_NUMB_TX_H = 10,
WCN36XX_DXE_CH_DESC_NUMB_RX_L = 512,
WCN36XX_DXE_CH_DESC_NUMB_RX_H = 40
};
/**
* struct wcn36xx_dxe_desc - describes descriptor of one DXE buffer
*
* @ctrl: is a union that consists of following bits:
* union {
* u32 valid :1; //0 = DMA stop, 1 = DMA continue with this
* //descriptor
* u32 transfer_type :2; //0 = Host to Host space
* u32 eop :1; //End of Packet
* u32 bd_handling :1; //if transferType = Host to BMU, then 0
* // means first 128 bytes contain BD, and 1
* // means create new empty BD
* u32 siq :1; // SIQ
* u32 diq :1; // DIQ
* u32 pdu_rel :1; //0 = don't release BD and PDUs when done,
* // 1 = release them
* u32 bthld_sel :4; //BMU Threshold Select
* u32 prio :3; //Specifies the priority level to use for
* // the transfer
* u32 stop_channel :1; //1 = DMA stops processing further, channel
* //requires re-enabling after this
* u32 intr :1; //Interrupt on Descriptor Done
* u32 rsvd :1; //reserved
* u32 size :14;//14 bits used - ignored for BMU transfers,
* //only used for host to host transfers?
* } ctrl;
*/
struct wcn36xx_dxe_desc {
u32 ctrl;
u32 fr_len;
u32 src_addr_l;
u32 dst_addr_l;
u32 phy_next_l;
u32 src_addr_h;
u32 dst_addr_h;
u32 phy_next_h;
} __packed;
/* DXE Control block */
struct wcn36xx_dxe_ctl {
struct wcn36xx_dxe_ctl *next;
struct wcn36xx_dxe_desc *desc;
unsigned int desc_phy_addr;
int ctl_blk_order;
struct sk_buff *skb;
void *bd_cpu_addr;
dma_addr_t bd_phy_addr;
};
struct wcn36xx_dxe_ch {
spinlock_t lock; /* protects head/tail ptrs */
enum wcn36xx_dxe_ch_type ch_type;
void *cpu_addr;
dma_addr_t dma_addr;
enum wcn36xx_dxe_ch_desc_num desc_num;
/* DXE control block ring */
struct wcn36xx_dxe_ctl *head_blk_ctl;
struct wcn36xx_dxe_ctl *tail_blk_ctl;
/* DXE channel specific configs */
u32 dxe_wq;
u32 ctrl_bd;
u32 ctrl_skb;
u32 reg_ctrl;
u32 def_ctrl;
};
/* Memory Pool for BD headers */
struct wcn36xx_dxe_mem_pool {
int chunk_size;
void *virt_addr;
dma_addr_t phy_addr;
};
struct wcn36xx_tx_bd;
struct wcn36xx_vif;
int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn);
void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn);
void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn);
int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn);
void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn);
int wcn36xx_dxe_init(struct wcn36xx *wcn);
void wcn36xx_dxe_deinit(struct wcn36xx *wcn);
int wcn36xx_dxe_init_channels(struct wcn36xx *wcn);
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
struct wcn36xx_vif *vif_priv,
struct wcn36xx_tx_bd *bd,
struct sk_buff *skb,
bool is_low);
int wcn36xx_dxe_tx_flush(struct wcn36xx *wcn);
void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
#endif /* _DXE_H_ */
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "wcn36xx.h"
#include "firmware.h"
#define DEFINE(s)[s] = #s
static const char * const wcn36xx_firmware_caps_names[] = {
DEFINE(MCC),
DEFINE(P2P),
DEFINE(DOT11AC),
DEFINE(SLM_SESSIONIZATION),
DEFINE(DOT11AC_OPMODE),
DEFINE(SAP32STA),
DEFINE(TDLS),
DEFINE(P2P_GO_NOA_DECOUPLE_INIT_SCAN),
DEFINE(WLANACTIVE_OFFLOAD),
DEFINE(BEACON_OFFLOAD),
DEFINE(SCAN_OFFLOAD),
DEFINE(ROAM_OFFLOAD),
DEFINE(BCN_MISS_OFFLOAD),
DEFINE(STA_POWERSAVE),
DEFINE(STA_ADVANCED_PWRSAVE),
DEFINE(AP_UAPSD),
DEFINE(AP_DFS),
DEFINE(BLOCKACK),
DEFINE(PHY_ERR),
DEFINE(BCN_FILTER),
DEFINE(RTT),
DEFINE(RATECTRL),
DEFINE(WOW),
DEFINE(WLAN_ROAM_SCAN_OFFLOAD),
DEFINE(SPECULATIVE_PS_POLL),
DEFINE(SCAN_SCH),
DEFINE(IBSS_HEARTBEAT_OFFLOAD),
DEFINE(WLAN_SCAN_OFFLOAD),
DEFINE(WLAN_PERIODIC_TX_PTRN),
DEFINE(ADVANCE_TDLS),
DEFINE(BATCH_SCAN),
DEFINE(FW_IN_TX_PATH),
DEFINE(EXTENDED_NSOFFLOAD_SLOT),
DEFINE(CH_SWITCH_V1),
DEFINE(HT40_OBSS_SCAN),
DEFINE(UPDATE_CHANNEL_LIST),
DEFINE(WLAN_MCADDR_FLT),
DEFINE(WLAN_CH144),
DEFINE(NAN),
DEFINE(TDLS_SCAN_COEXISTENCE),
DEFINE(LINK_LAYER_STATS_MEAS),
DEFINE(MU_MIMO),
DEFINE(EXTENDED_SCAN),
DEFINE(DYNAMIC_WMM_PS),
DEFINE(MAC_SPOOFED_SCAN),
DEFINE(BMU_ERROR_GENERIC_RECOVERY),
DEFINE(DISA),
DEFINE(FW_STATS),
DEFINE(WPS_PRBRSP_TMPL),
DEFINE(BCN_IE_FLT_DELTA),
DEFINE(TDLS_OFF_CHANNEL),
DEFINE(RTT3),
DEFINE(MGMT_FRAME_LOGGING),
DEFINE(ENHANCED_TXBD_COMPLETION),
DEFINE(LOGGING_ENHANCEMENT),
DEFINE(EXT_SCAN_ENHANCED),
DEFINE(MEMORY_DUMP_SUPPORTED),
DEFINE(PER_PKT_STATS_SUPPORTED),
DEFINE(EXT_LL_STAT),
DEFINE(WIFI_CONFIG),
DEFINE(ANTENNA_DIVERSITY_SELECTION),
};
#undef DEFINE
const char *wcn36xx_firmware_get_cap_name(enum wcn36xx_firmware_feat_caps x)
{
if (x >= ARRAY_SIZE(wcn36xx_firmware_caps_names))
return "UNKNOWN";
return wcn36xx_firmware_caps_names[x];
}
void wcn36xx_firmware_set_feat_caps(u32 *bitmap,
enum wcn36xx_firmware_feat_caps cap)
{
int arr_idx, bit_idx;
if (cap < 0 || cap > 127) {
wcn36xx_warn("error cap idx %d\n", cap);
return;
}
arr_idx = cap / 32;
bit_idx = cap % 32;
bitmap[arr_idx] |= (1 << bit_idx);
}
int wcn36xx_firmware_get_feat_caps(u32 *bitmap,
enum wcn36xx_firmware_feat_caps cap)
{
int arr_idx, bit_idx;
if (cap < 0 || cap > 127) {
wcn36xx_warn("error cap idx %d\n", cap);
return -EINVAL;
}
arr_idx = cap / 32;
bit_idx = cap % 32;
return (bitmap[arr_idx] & (1 << bit_idx)) ? 1 : 0;
}
void wcn36xx_firmware_clear_feat_caps(u32 *bitmap,
enum wcn36xx_firmware_feat_caps cap)
{
int arr_idx, bit_idx;
if (cap < 0 || cap > 127) {
wcn36xx_warn("error cap idx %d\n", cap);
return;
}
arr_idx = cap / 32;
bit_idx = cap % 32;
bitmap[arr_idx] &= ~(1 << bit_idx);
}
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _FIRMWARE_H_
#define _FIRMWARE_H_
/* Capability bitmap exchange definitions and macros starts */
enum wcn36xx_firmware_feat_caps {
MCC = 0,
P2P = 1,
DOT11AC = 2,
SLM_SESSIONIZATION = 3,
DOT11AC_OPMODE = 4,
SAP32STA = 5,
TDLS = 6,
P2P_GO_NOA_DECOUPLE_INIT_SCAN = 7,
WLANACTIVE_OFFLOAD = 8,
BEACON_OFFLOAD = 9,
SCAN_OFFLOAD = 10,
ROAM_OFFLOAD = 11,
BCN_MISS_OFFLOAD = 12,
STA_POWERSAVE = 13,
STA_ADVANCED_PWRSAVE = 14,
AP_UAPSD = 15,
AP_DFS = 16,
BLOCKACK = 17,
PHY_ERR = 18,
BCN_FILTER = 19,
RTT = 20,
RATECTRL = 21,
WOW = 22,
WLAN_ROAM_SCAN_OFFLOAD = 23,
SPECULATIVE_PS_POLL = 24,
SCAN_SCH = 25,
IBSS_HEARTBEAT_OFFLOAD = 26,
WLAN_SCAN_OFFLOAD = 27,
WLAN_PERIODIC_TX_PTRN = 28,
ADVANCE_TDLS = 29,
BATCH_SCAN = 30,
FW_IN_TX_PATH = 31,
EXTENDED_NSOFFLOAD_SLOT = 32,
CH_SWITCH_V1 = 33,
HT40_OBSS_SCAN = 34,
UPDATE_CHANNEL_LIST = 35,
WLAN_MCADDR_FLT = 36,
WLAN_CH144 = 37,
NAN = 38,
TDLS_SCAN_COEXISTENCE = 39,
LINK_LAYER_STATS_MEAS = 40,
MU_MIMO = 41,
EXTENDED_SCAN = 42,
DYNAMIC_WMM_PS = 43,
MAC_SPOOFED_SCAN = 44,
BMU_ERROR_GENERIC_RECOVERY = 45,
DISA = 46,
FW_STATS = 47,
WPS_PRBRSP_TMPL = 48,
BCN_IE_FLT_DELTA = 49,
TDLS_OFF_CHANNEL = 51,
RTT3 = 52,
MGMT_FRAME_LOGGING = 53,
ENHANCED_TXBD_COMPLETION = 54,
LOGGING_ENHANCEMENT = 55,
EXT_SCAN_ENHANCED = 56,
MEMORY_DUMP_SUPPORTED = 57,
PER_PKT_STATS_SUPPORTED = 58,
EXT_LL_STAT = 60,
WIFI_CONFIG = 61,
ANTENNA_DIVERSITY_SELECTION = 62,
MAX_FEATURE_SUPPORTED = 128,
};
void wcn36xx_firmware_set_feat_caps(u32 *bitmap,
enum wcn36xx_firmware_feat_caps cap);
int wcn36xx_firmware_get_feat_caps(u32 *bitmap,
enum wcn36xx_firmware_feat_caps cap);
void wcn36xx_firmware_clear_feat_caps(u32 *bitmap,
enum wcn36xx_firmware_feat_caps cap);
const char *wcn36xx_firmware_get_cap_name(enum wcn36xx_firmware_feat_caps x);
#endif /* _FIRMWARE_H_ */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "wcn36xx.h"
#define WCN36XX_BMPS_FAIL_THREHOLD 3
int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn,
struct ieee80211_vif *vif)
{
int ret = 0;
struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
/* TODO: Make sure the TX chain clean */
ret = wcn36xx_smd_enter_bmps(wcn, vif);
if (!ret) {
wcn36xx_dbg(WCN36XX_DBG_PMC, "Entered BMPS\n");
vif_priv->pw_state = WCN36XX_BMPS;
vif_priv->bmps_fail_ct = 0;
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
} else {
/*
* One of the reasons why HW will not enter BMPS is because
* driver is trying to enter bmps before first beacon was
* received just after auth complete
*/
wcn36xx_err("Can not enter BMPS!\n");
if (vif_priv->bmps_fail_ct++ == WCN36XX_BMPS_FAIL_THREHOLD) {
ieee80211_connection_loss(vif);
vif_priv->bmps_fail_ct = 0;
}
}
return ret;
}
int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn,
struct ieee80211_vif *vif)
{
struct wcn36xx_vif *vif_priv = wcn36xx_vif_to_priv(vif);
if (WCN36XX_BMPS != vif_priv->pw_state) {
/* Unbalanced call or last BMPS enter failed */
wcn36xx_dbg(WCN36XX_DBG_PMC,
"Not in BMPS mode, no need to exit\n");
return -EALREADY;
}
wcn36xx_smd_exit_bmps(wcn, vif);
vif_priv->pw_state = WCN36XX_FULL_POWER;
vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
return 0;
}
int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn,
struct ieee80211_vif *vif)
{
wcn36xx_dbg(WCN36XX_DBG_PMC, "%s\n", __func__);
return wcn36xx_smd_keep_alive_req(wcn, vif,
WCN36XX_HAL_KEEP_ALIVE_NULL_PKT);
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#ifndef _WCN36XX_PMC_H_
#define _WCN36XX_PMC_H_
struct wcn36xx;
enum wcn36xx_power_state {
WCN36XX_FULL_POWER,
WCN36XX_BMPS
};
int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn,
struct ieee80211_vif *vif);
int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn,
struct ieee80211_vif *vif);
int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn,
struct ieee80211_vif *vif);
#endif /* _WCN36XX_PMC_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#ifndef _SMD_H_
#define _SMD_H_
#include "wcn36xx.h"
/* Max shared size is 4k but we take less.*/
#define WCN36XX_NV_FRAGMENT_SIZE 3072
#define WCN36XX_HAL_BUF_SIZE 4096
#define HAL_MSG_TIMEOUT 10000
#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400
#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200
/* The PNO version info be contained in the rsp msg */
#define WCN36XX_FW_MSG_PNO_VERSION_MASK 0x8000
enum wcn36xx_fw_msg_result {
WCN36XX_FW_MSG_RESULT_SUCCESS = 0,
WCN36XX_FW_MSG_RESULT_SUCCESS_SYNC = 1,
WCN36XX_FW_MSG_RESULT_MEM_FAIL = 5,
};
/******************************/
/* SMD requests and responses */
/******************************/
struct wcn36xx_fw_msg_status_rsp {
u32 status;
} __packed;
struct wcn36xx_hal_ind_msg {
struct list_head list;
size_t msg_len;
u8 msg[] __counted_by(msg_len);
};
struct wcn36xx;
struct rpmsg_device;
int wcn36xx_smd_open(struct wcn36xx *wcn);
void wcn36xx_smd_close(struct wcn36xx *wcn);
int wcn36xx_smd_load_nv(struct wcn36xx *wcn);
int wcn36xx_smd_start(struct wcn36xx *wcn);
int wcn36xx_smd_stop(struct wcn36xx *wcn);
int wcn36xx_smd_start_scan(struct wcn36xx *wcn, u8 scan_channel);
int wcn36xx_smd_end_scan(struct wcn36xx *wcn, u8 scan_channel);
int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode,
struct ieee80211_vif *vif);
int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode,
struct ieee80211_vif *vif);
int wcn36xx_smd_start_hw_scan(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
int wcn36xx_smd_stop_hw_scan(struct wcn36xx *wcn);
int wcn36xx_smd_update_channel_list(struct wcn36xx *wcn, struct cfg80211_scan_request *req);
int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif);
int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr);
int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index);
int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch);
int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid,
const u8 *sta_mac,
enum wcn36xx_hal_link_state state);
int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, const u8 *bssid,
bool update);
int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif);
int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct ieee80211_sta *sta);
int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct sk_buff *skb_beacon, u16 tim_off,
u16 p2p_off);
int wcn36xx_smd_switch_channel(struct wcn36xx *wcn,
struct ieee80211_vif *vif, int ch);
int wcn36xx_smd_process_ptt_msg(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
void *ptt_msg, size_t len,
void **ptt_rsp_msg);
int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
struct sk_buff *skb);
int wcn36xx_smd_set_stakey(struct wcn36xx *wcn,
enum ani_ed_type enc_type,
u8 keyidx,
u8 keylen,
u8 *key,
u8 sta_index);
int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn,
enum ani_ed_type enc_type,
u8 bssidx,
u8 keyidx,
u8 keylen,
u8 *key);
int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn,
enum ani_ed_type enc_type,
u8 keyidx,
u8 sta_index);
int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
enum ani_ed_type enc_type,
u8 bssidx,
u8 keyidx);
int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim);
int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
int packet_type);
int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2,
u32 arg3, u32 arg4, u32 arg5);
int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn);
int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
struct ieee80211_sta *sta,
u16 tid,
u16 *ssn,
u8 direction,
u8 sta_index);
int wcn36xx_smd_add_ba(struct wcn36xx *wcn, u8 session_id);
int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 direction, u8 sta_index);
int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index, u16 tid, u16 *ssn);
int wcn36xx_smd_get_stats(struct wcn36xx *wcn, u8 sta_index, u32 stats_mask,
struct station_info *sinfo);
int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value);
int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
void *buf, int len, void *priv, u32 addr);
int wcn36xx_smd_set_mc_list(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
struct wcn36xx_hal_rcv_flt_mc_addr_list_type *fp);
int wcn36xx_smd_arp_offload(struct wcn36xx *wcn, struct ieee80211_vif *vif,
bool enable);
int wcn36xx_smd_ipv6_ns_offload(struct wcn36xx *wcn, struct ieee80211_vif *vif,
bool enable);
int wcn36xx_smd_gtk_offload(struct wcn36xx *wcn, struct ieee80211_vif *vif,
bool enable);
int wcn36xx_smd_gtk_offload_get_info(struct wcn36xx *wcn,
struct ieee80211_vif *vif);
int wcn36xx_smd_wlan_host_suspend_ind(struct wcn36xx *wcn);
int wcn36xx_smd_host_resume(struct wcn36xx *wcn);
int wcn36xx_smd_enter_imps(struct wcn36xx *wcn);
int wcn36xx_smd_exit_imps(struct wcn36xx *wcn);
int wcn36xx_smd_add_beacon_filter(struct wcn36xx *wcn,
struct ieee80211_vif *vif);
#endif /* _SMD_H_ */
@@ -0,0 +1,149 @@
/*
* Copyright (c) 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 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.
*/
#include <net/netlink.h>
#include <linux/firmware.h>
#include <net/cfg80211.h>
#include "wcn36xx.h"
#include "testmode.h"
#include "testmode_i.h"
#include "hal.h"
#include "smd.h"
static const struct nla_policy wcn36xx_tm_policy[WCN36XX_TM_ATTR_MAX + 1] = {
[WCN36XX_TM_ATTR_CMD] = { .type = NLA_U16 },
[WCN36XX_TM_ATTR_DATA] = { .type = NLA_BINARY,
.len = WCN36XX_TM_DATA_MAX_LEN },
};
struct build_release_number {
u16 drv_major;
u16 drv_minor;
u16 drv_patch;
u16 drv_build;
u16 ptt_max;
u16 ptt_min;
u16 fw_ver;
} __packed;
static int wcn36xx_tm_cmd_ptt(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct nlattr *tb[])
{
int ret = 0, buf_len;
void *buf;
struct ftm_rsp_msg *msg, *rsp = NULL;
struct sk_buff *skb;
if (!tb[WCN36XX_TM_ATTR_DATA])
return -EINVAL;
buf = nla_data(tb[WCN36XX_TM_ATTR_DATA]);
buf_len = nla_len(tb[WCN36XX_TM_ATTR_DATA]);
msg = buf;
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"testmode cmd wmi msg_id 0x%04X msg_len %d buf %p buf_len %d\n",
msg->msg_id, msg->msg_body_length,
buf, buf_len);
wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "REQ ", buf, buf_len);
if (msg->msg_id == MSG_GET_BUILD_RELEASE_NUMBER) {
struct build_release_number *body =
(struct build_release_number *)
msg->msg_response;
body->drv_major = wcn->fw_major;
body->drv_minor = wcn->fw_minor;
body->drv_patch = wcn->fw_version;
body->drv_build = wcn->fw_revision;
body->ptt_max = 10;
body->ptt_min = 0;
rsp = msg;
rsp->resp_status = 0;
} else {
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"PPT Request >> HAL size %d\n",
msg->msg_body_length);
msg->resp_status = wcn36xx_smd_process_ptt_msg(wcn, vif, msg,
msg->msg_body_length, (void *)(&rsp));
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"Response status = %d\n",
msg->resp_status);
if (rsp)
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"PPT Response << HAL size %d\n",
rsp->msg_body_length);
}
if (!rsp) {
rsp = msg;
wcn36xx_warn("No response! Echoing request with response status %d\n",
rsp->resp_status);
}
wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "RSP ",
rsp, rsp->msg_body_length);
skb = cfg80211_testmode_alloc_reply_skb(wcn->hw->wiphy,
nla_total_size(msg->msg_body_length));
if (!skb) {
ret = -ENOMEM;
goto out;
}
ret = nla_put(skb, WCN36XX_TM_ATTR_DATA, rsp->msg_body_length, rsp);
if (ret) {
kfree_skb(skb);
goto out;
}
ret = cfg80211_testmode_reply(skb);
out:
if (rsp != msg)
kfree(rsp);
return ret;
}
int wcn36xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
struct wcn36xx *wcn = hw->priv;
struct nlattr *tb[WCN36XX_TM_ATTR_MAX + 1];
int ret = 0;
unsigned short attr;
wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "Data:", data, len);
ret = nla_parse_deprecated(tb, WCN36XX_TM_ATTR_MAX, data, len,
wcn36xx_tm_policy, NULL);
if (ret)
return ret;
if (!tb[WCN36XX_TM_ATTR_CMD])
return -EINVAL;
attr = nla_get_u16(tb[WCN36XX_TM_ATTR_CMD]);
if (attr != WCN36XX_TM_CMD_PTT)
return -EOPNOTSUPP;
return wcn36xx_tm_cmd_ptt(wcn, vif, tb);
}
@@ -0,0 +1,46 @@
/*
* Copyright (c) 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 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.
*/
#include "wcn36xx.h"
struct ftm_rsp_msg {
u16 msg_id;
u16 msg_body_length;
u32 resp_status;
u8 msg_response[];
} __packed;
/* The request buffer of FTM which contains a byte of command and the request */
struct ftm_payload {
u16 ftm_cmd_type;
struct ftm_rsp_msg ftm_cmd_msg;
} __packed;
#define MSG_GET_BUILD_RELEASE_NUMBER 0x32A2
#ifdef CONFIG_NL80211_TESTMODE
int wcn36xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
#else
static inline int wcn36xx_tm_cmd(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
void *data, int len)
{
return 0;
}
#endif
@@ -0,0 +1,29 @@
/*
* Copyright (c) 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 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.
*/
#define WCN36XX_TM_DATA_MAX_LEN 5000
enum wcn36xx_tm_attr {
__WCN36XX_TM_ATTR_INVALID = 0,
WCN36XX_TM_ATTR_CMD = 1,
WCN36XX_TM_ATTR_DATA = 2,
/* keep last */
__WCN36XX_TM_ATTR_AFTER_LAST,
WCN36XX_TM_ATTR_MAX = __WCN36XX_TM_ATTR_AFTER_LAST - 1,
};
#define WCN36XX_TM_CMD_PTT 3
@@ -0,0 +1,734 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/random.h>
#include "txrx.h"
static inline int get_rssi0(struct wcn36xx_rx_bd *bd)
{
return 100 - ((bd->phy_stat0 >> 24) & 0xff);
}
static inline int get_snr(struct wcn36xx_rx_bd *bd)
{
return ((bd->phy_stat1 >> 24) & 0xff);
}
struct wcn36xx_rate {
u16 bitrate;
u16 mcs_or_legacy_index;
enum mac80211_rx_encoding encoding;
enum mac80211_rx_encoding_flags encoding_flags;
enum rate_info_bw bw;
};
/* Buffer descriptor rx_ch field is limited to 5-bit (4+1), a mapping is used
* for 11A Channels.
*/
static const u8 ab_rx_ch_map[] = { 36, 40, 44, 48, 52, 56, 60, 64, 100, 104,
108, 112, 116, 120, 124, 128, 132, 136, 140,
149, 153, 157, 161, 165, 144 };
static const struct wcn36xx_rate wcn36xx_rate_table[] = {
/* 11b rates */
{ 10, 0, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 20, 1, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 55, 2, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 110, 3, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
/* 11b SP (short preamble) */
{ 10, 0, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 },
{ 20, 1, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 },
{ 55, 2, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 },
{ 110, 3, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 },
/* 11ag */
{ 60, 4, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 90, 5, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 120, 6, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 180, 7, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 240, 8, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 360, 9, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 480, 10, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
{ 540, 11, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 },
/* 11n */
{ 65, 0, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 130, 1, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 195, 2, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 260, 3, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 390, 4, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 520, 5, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 585, 6, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 650, 7, RX_ENC_HT, 0, RATE_INFO_BW_20 },
/* 11n SGI */
{ 72, 0, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 144, 1, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 217, 2, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 289, 3, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 434, 4, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 578, 5, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 650, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 722, 7, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
/* 11n GF (greenfield) */
{ 65, 0, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
{ 130, 1, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
{ 195, 2, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
{ 260, 3, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
{ 390, 4, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
{ 520, 5, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
{ 585, 6, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
{ 650, 7, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 },
/* 11n CB (channel bonding) */
{ 135, 0, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 270, 1, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 405, 2, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 540, 3, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 810, 4, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1080, 5, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1215, 6, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1350, 7, RX_ENC_HT, 0, RATE_INFO_BW_40 },
/* 11n CB + SGI */
{ 150, 0, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 300, 1, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 450, 2, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 600, 3, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 900, 4, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1200, 5, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1500, 7, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11n GF + CB */
{ 135, 0, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
{ 270, 1, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
{ 405, 2, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
{ 540, 3, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
{ 810, 4, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
{ 1080, 5, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
{ 1215, 6, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
{ 1350, 7, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 },
/* 11ac reserved indices */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 20 MHz 800ns GI MCS 0-8 */
{ 65, 0, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 130, 1, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 195, 2, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 260, 3, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 390, 4, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 520, 5, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 585, 6, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 650, 7, RX_ENC_HT, 0, RATE_INFO_BW_20 },
{ 780, 8, RX_ENC_HT, 0, RATE_INFO_BW_20 },
/* 11ac reserved indices */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 20 MHz 400ns SGI MCS 6-8 */
{ 655, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 722, 7, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
{ 866, 8, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 },
/* 11ac reserved indices */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 40 MHz 800ns GI MCS 0-9 */
{ 135, 0, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 270, 1, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 405, 2, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 540, 3, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 810, 4, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1080, 5, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1215, 6, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1350, 7, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1350, 7, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1620, 8, RX_ENC_HT, 0, RATE_INFO_BW_40 },
{ 1800, 9, RX_ENC_HT, 0, RATE_INFO_BW_40 },
/* 11ac reserved indices */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 40 MHz 400ns SGI MCS 5-7 */
{ 1200, 5, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1500, 7, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac reserved index */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 40 MHz 400ns SGI MCS 5-7 */
{ 1800, 8, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 2000, 9, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac reserved index */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 80 MHz 800ns GI MCS 0-7 */
{ 292, 0, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 585, 1, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 877, 2, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 1170, 3, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 1755, 4, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 2340, 5, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 2632, 6, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 2925, 7, RX_ENC_HT, 0, RATE_INFO_BW_80},
/* 11 ac reserved index */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 80 MHz 800 ns GI MCS 8-9 */
{ 3510, 8, RX_ENC_HT, 0, RATE_INFO_BW_80},
{ 3900, 9, RX_ENC_HT, 0, RATE_INFO_BW_80},
/* 11 ac reserved indices */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 80 MHz 400 ns SGI MCS 6-7 */
{ 2925, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_80 },
{ 3250, 7, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_80 },
/* 11ac reserved index */
{ 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 },
/* 11ac 80 MHz 400ns SGI MCS 8-9 */
{ 3900, 8, RX_ENC_VHT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_80 },
{ 4333, 9, RX_ENC_VHT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_80 },
};
static struct sk_buff *wcn36xx_unchain_msdu(struct sk_buff_head *amsdu)
{
struct sk_buff *skb, *first;
int total_len = 0;
int space;
first = __skb_dequeue(amsdu);
skb_queue_walk(amsdu, skb)
total_len += skb->len;
space = total_len - skb_tailroom(first);
if (space > 0 && pskb_expand_head(first, 0, space, GFP_ATOMIC) < 0) {
__skb_queue_head(amsdu, first);
return NULL;
}
/* Walk list again, copying contents into msdu_head */
while ((skb = __skb_dequeue(amsdu))) {
skb_copy_from_linear_data(skb, skb_put(first, skb->len),
skb->len);
dev_kfree_skb_irq(skb);
}
return first;
}
static void __skb_queue_purge_irq(struct sk_buff_head *list)
{
struct sk_buff *skb;
while ((skb = __skb_dequeue(list)) != NULL)
dev_kfree_skb_irq(skb);
}
static void wcn36xx_update_survey(struct wcn36xx *wcn, int rssi, int snr,
int band, int freq)
{
static struct ieee80211_channel *channel;
struct ieee80211_supported_band *sband;
int idx;
int i;
u8 snr_sample = snr & 0xff;
idx = 0;
if (band == NL80211_BAND_5GHZ)
idx = wcn->hw->wiphy->bands[NL80211_BAND_2GHZ]->n_channels;
sband = wcn->hw->wiphy->bands[band];
channel = sband->channels;
for (i = 0; i < sband->n_channels; i++, channel++) {
if (channel->center_freq == freq) {
idx += i;
break;
}
}
spin_lock(&wcn->survey_lock);
wcn->chan_survey[idx].rssi = rssi;
wcn->chan_survey[idx].snr = snr;
spin_unlock(&wcn->survey_lock);
add_device_randomness(&snr_sample, sizeof(snr_sample));
}
int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
{
struct ieee80211_rx_status status;
const struct wcn36xx_rate *rate;
struct ieee80211_hdr *hdr;
struct wcn36xx_rx_bd *bd;
u16 fc, sn;
/*
* All fields must be 0, otherwise it can lead to
* unexpected consequences.
*/
memset(&status, 0, sizeof(status));
bd = (struct wcn36xx_rx_bd *)skb->data;
buff_to_be(bd, sizeof(*bd)/sizeof(u32));
wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP,
"BD <<< ", (char *)bd,
sizeof(struct wcn36xx_rx_bd));
if (bd->pdu.mpdu_data_off <= bd->pdu.mpdu_header_off ||
bd->pdu.mpdu_len < bd->pdu.mpdu_header_len)
goto drop;
if (bd->asf && !bd->esf) { /* chained A-MSDU chunks */
/* Sanity check */
if (bd->pdu.mpdu_data_off + bd->pdu.mpdu_len > WCN36XX_PKT_SIZE)
goto drop;
skb_put(skb, bd->pdu.mpdu_data_off + bd->pdu.mpdu_len);
skb_pull(skb, bd->pdu.mpdu_data_off);
/* Only set status for first chained BD (with mac header) */
goto done;
}
if (bd->pdu.mpdu_header_off < sizeof(*bd) ||
bd->pdu.mpdu_header_off + bd->pdu.mpdu_len > WCN36XX_PKT_SIZE)
goto drop;
skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len);
skb_pull(skb, bd->pdu.mpdu_header_off);
hdr = (struct ieee80211_hdr *) skb->data;
fc = __le16_to_cpu(hdr->frame_control);
sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
status.mactime = 10;
status.signal = -get_rssi0(bd);
status.antenna = 1;
status.flag = 0;
status.rx_flags = 0;
status.flag |= RX_FLAG_IV_STRIPPED |
RX_FLAG_MMIC_STRIPPED |
RX_FLAG_DECRYPTED;
wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag);
if (bd->scan_learn) {
/* If packet originate from hardware scanning, extract the
* band/channel from bd descriptor.
*/
u8 hwch = (bd->reserved0 << 4) + bd->rx_ch;
if (bd->rf_band != 1 && hwch <= sizeof(ab_rx_ch_map) && hwch >= 1) {
status.band = NL80211_BAND_5GHZ;
status.freq = ieee80211_channel_to_frequency(ab_rx_ch_map[hwch - 1],
status.band);
} else {
status.band = NL80211_BAND_2GHZ;
status.freq = ieee80211_channel_to_frequency(hwch, status.band);
}
} else {
status.band = WCN36XX_BAND(wcn);
status.freq = WCN36XX_CENTER_FREQ(wcn);
}
wcn36xx_update_survey(wcn, status.signal, get_snr(bd),
status.band, status.freq);
if (bd->rate_id < ARRAY_SIZE(wcn36xx_rate_table)) {
rate = &wcn36xx_rate_table[bd->rate_id];
status.encoding = rate->encoding;
status.enc_flags = rate->encoding_flags;
status.bw = rate->bw;
status.rate_idx = rate->mcs_or_legacy_index;
status.nss = 1;
if (status.band == NL80211_BAND_5GHZ &&
status.encoding == RX_ENC_LEGACY &&
status.rate_idx >= 4) {
/* no dsss rates in 5Ghz rates table */
status.rate_idx -= 4;
}
} else {
status.encoding = 0;
status.bw = 0;
status.enc_flags = 0;
status.rate_idx = 0;
}
if (ieee80211_is_beacon(hdr->frame_control) ||
ieee80211_is_probe_resp(hdr->frame_control))
status.boottime_ns = ktime_get_boottime_ns();
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
if (ieee80211_is_beacon(hdr->frame_control)) {
wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n",
skb, skb->len, fc, sn);
wcn36xx_dbg_dump(WCN36XX_DBG_BEACON_DUMP, "SKB <<< ",
(char *)skb->data, skb->len);
} else {
wcn36xx_dbg(WCN36XX_DBG_RX, "rx skb %p len %d fc %04x sn %d\n",
skb, skb->len, fc, sn);
wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, "SKB <<< ",
(char *)skb->data, skb->len);
}
done:
/* Chained AMSDU ? slow path */
if (unlikely(bd->asf && !(bd->lsf && bd->esf))) {
if (bd->esf && !skb_queue_empty(&wcn->amsdu)) {
wcn36xx_err("Discarding non complete chain");
__skb_queue_purge_irq(&wcn->amsdu);
}
__skb_queue_tail(&wcn->amsdu, skb);
if (!bd->lsf)
return 0; /* Not the last AMSDU, wait for more */
skb = wcn36xx_unchain_msdu(&wcn->amsdu);
if (!skb)
goto drop;
}
ieee80211_rx_irqsafe(wcn->hw, skb);
return 0;
drop: /* drop everything */
wcn36xx_err("Drop frame! skb:%p len:%u hoff:%u doff:%u asf=%u esf=%u lsf=%u\n",
skb, bd->pdu.mpdu_len, bd->pdu.mpdu_header_off,
bd->pdu.mpdu_data_off, bd->asf, bd->esf, bd->lsf);
dev_kfree_skb_irq(skb);
__skb_queue_purge_irq(&wcn->amsdu);
return -EINVAL;
}
static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd,
u32 mpdu_header_len,
u32 len,
u16 tid)
{
bd->pdu.mpdu_header_len = mpdu_header_len;
bd->pdu.mpdu_header_off = sizeof(*bd);
bd->pdu.mpdu_data_off = bd->pdu.mpdu_header_len +
bd->pdu.mpdu_header_off;
bd->pdu.mpdu_len = len;
bd->pdu.tid = tid;
}
static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn,
u8 *addr)
{
struct wcn36xx_vif *vif_priv = NULL;
struct ieee80211_vif *vif = NULL;
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
vif = wcn36xx_priv_to_vif(vif_priv);
if (memcmp(vif->addr, addr, ETH_ALEN) == 0)
return vif_priv;
}
wcn36xx_warn("vif %pM not found\n", addr);
return NULL;
}
static void wcn36xx_tx_start_ampdu(struct wcn36xx *wcn,
struct wcn36xx_sta *sta_priv,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_sta *sta;
u8 *qc, tid;
if (!conf_is_ht(&wcn->hw->conf))
return;
sta = wcn36xx_priv_to_sta(sta_priv);
if (WARN_ON(!ieee80211_is_data_qos(hdr->frame_control)))
return;
if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
return;
qc = ieee80211_get_qos_ctl(hdr);
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
spin_lock(&sta_priv->ampdu_lock);
if (sta_priv->ampdu_state[tid] != WCN36XX_AMPDU_NONE)
goto out_unlock;
if (sta_priv->non_agg_frame_ct++ >= WCN36XX_AMPDU_START_THRESH) {
sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START;
sta_priv->non_agg_frame_ct = 0;
ieee80211_start_tx_ba_session(sta, tid, 0);
}
out_unlock:
spin_unlock(&sta_priv->ampdu_lock);
}
static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
struct wcn36xx *wcn,
struct wcn36xx_vif **vif_priv,
struct wcn36xx_sta *sta_priv,
struct sk_buff *skb,
bool bcast)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = NULL;
struct wcn36xx_vif *__vif_priv = NULL;
bool is_data_qos = ieee80211_is_data_qos(hdr->frame_control);
u16 tid = 0;
bd->bd_rate = WCN36XX_BD_RATE_DATA;
/*
* For not unicast frames mac80211 will not set sta pointer so use
* self_sta_index instead.
*/
if (sta_priv) {
__vif_priv = sta_priv->vif;
vif = wcn36xx_priv_to_vif(__vif_priv);
bd->dpu_sign = sta_priv->ucast_dpu_sign;
if (vif->type == NL80211_IFTYPE_STATION) {
bd->sta_index = sta_priv->bss_sta_index;
bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index;
} else if (vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_ADHOC ||
vif->type == NL80211_IFTYPE_MESH_POINT) {
bd->sta_index = sta_priv->sta_index;
bd->dpu_desc_idx = sta_priv->dpu_desc_index;
}
} else {
__vif_priv = get_vif_by_addr(wcn, hdr->addr2);
bd->sta_index = __vif_priv->self_sta_index;
bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
bd->dpu_sign = __vif_priv->self_ucast_dpu_sign;
}
if (is_data_qos) {
tid = ieee80211_get_tid(hdr);
/* TID->QID is one-to-one mapping */
bd->queue_id = tid;
bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_DPU_QOS;
} else {
bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_DPU_NON_QOS;
}
if (info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT ||
(sta_priv && !sta_priv->is_data_encrypted)) {
bd->dpu_ne = 1;
}
if (ieee80211_is_any_nullfunc(hdr->frame_control)) {
/* Don't use a regular queue for null packet (no ampdu) */
bd->queue_id = WCN36XX_TX_U_WQ_ID;
bd->bd_rate = WCN36XX_BD_RATE_CTRL;
if (ieee80211_is_qos_nullfunc(hdr->frame_control))
bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_HOST;
}
if (bcast) {
bd->ub = 1;
bd->ack_policy = 1;
}
*vif_priv = __vif_priv;
wcn36xx_set_tx_pdu(bd,
is_data_qos ?
sizeof(struct ieee80211_qos_hdr) :
sizeof(struct ieee80211_hdr_3addr),
skb->len, tid);
if (sta_priv && is_data_qos)
wcn36xx_tx_start_ampdu(wcn, sta_priv, skb);
}
static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd,
struct wcn36xx *wcn,
struct wcn36xx_vif **vif_priv,
struct sk_buff *skb,
bool bcast)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct wcn36xx_vif *__vif_priv =
get_vif_by_addr(wcn, hdr->addr2);
bd->sta_index = __vif_priv->self_sta_index;
bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
bd->dpu_ne = 1;
/* default rate for unicast */
if (ieee80211_is_mgmt(hdr->frame_control))
bd->bd_rate = (WCN36XX_BAND(wcn) == NL80211_BAND_5GHZ) ?
WCN36XX_BD_RATE_CTRL :
WCN36XX_BD_RATE_MGMT;
else if (ieee80211_is_ctl(hdr->frame_control))
bd->bd_rate = WCN36XX_BD_RATE_CTRL;
else
wcn36xx_warn("frame control type unknown\n");
/*
* In joining state trick hardware that probe is sent as
* unicast even if address is broadcast.
*/
if (__vif_priv->is_joining &&
ieee80211_is_probe_req(hdr->frame_control))
bcast = false;
if (bcast) {
/* broadcast */
bd->ub = 1;
/* No ack needed not unicast */
bd->ack_policy = 1;
bd->queue_id = WCN36XX_TX_B_WQ_ID;
} else
bd->queue_id = WCN36XX_TX_U_WQ_ID;
*vif_priv = __vif_priv;
bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_DPU_NON_QOS;
wcn36xx_set_tx_pdu(bd,
ieee80211_is_data_qos(hdr->frame_control) ?
sizeof(struct ieee80211_qos_hdr) :
sizeof(struct ieee80211_hdr_3addr),
skb->len, WCN36XX_TID);
}
int wcn36xx_start_tx(struct wcn36xx *wcn,
struct wcn36xx_sta *sta_priv,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct wcn36xx_vif *vif_priv = NULL;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
bool is_low = ieee80211_is_data(hdr->frame_control);
bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
is_multicast_ether_addr(hdr->addr1);
bool ack_ind = (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) &&
!(info->flags & IEEE80211_TX_CTL_NO_ACK);
struct wcn36xx_tx_bd bd;
int ret;
memset(&bd, 0, sizeof(bd));
wcn36xx_dbg(WCN36XX_DBG_TX,
"tx skb %p len %d fc %04x sn %d %s %s\n",
skb, skb->len, __le16_to_cpu(hdr->frame_control),
IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)),
is_low ? "low" : "high", bcast ? "bcast" : "ucast");
wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len);
bd.dpu_rf = WCN36XX_BMU_WQ_TX;
if (unlikely(ack_ind)) {
wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
/* Only one at a time is supported by fw. Stop the TX queues
* until the ack status gets back.
*/
ieee80211_stop_queues(wcn->hw);
/* Request ack indication from the firmware */
bd.tx_comp = 1;
}
/* Data frames served first*/
if (is_low)
wcn36xx_set_tx_data(&bd, wcn, &vif_priv, sta_priv, skb, bcast);
else
/* MGMT and CTRL frames are handeld here*/
wcn36xx_set_tx_mgmt(&bd, wcn, &vif_priv, skb, bcast);
buff_to_be(&bd, sizeof(bd)/sizeof(u32));
bd.tx_bd_sign = 0xbdbdbdbd;
ret = wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low);
if (unlikely(ret && ack_ind)) {
/* If the skb has not been transmitted, resume TX queue */
ieee80211_wake_queues(wcn->hw);
}
return ret;
}
void wcn36xx_process_tx_rate(struct ani_global_class_a_stats_info *stats, struct rate_info *info)
{
/* tx_rate is in units of 500kbps; mac80211 wants them in 100kbps */
if (stats->tx_rate_flags & HAL_TX_RATE_LEGACY)
info->legacy = stats->tx_rate * 5;
info->flags = 0;
info->mcs = stats->mcs_index;
info->nss = 1;
if (stats->tx_rate_flags & (HAL_TX_RATE_HT20 | HAL_TX_RATE_HT40))
info->flags |= RATE_INFO_FLAGS_MCS;
if (stats->tx_rate_flags & (HAL_TX_RATE_VHT20 | HAL_TX_RATE_VHT40 | HAL_TX_RATE_VHT80))
info->flags |= RATE_INFO_FLAGS_VHT_MCS;
if (stats->tx_rate_flags & HAL_TX_RATE_SGI)
info->flags |= RATE_INFO_FLAGS_SHORT_GI;
if (stats->tx_rate_flags & (HAL_TX_RATE_HT20 | HAL_TX_RATE_VHT20))
info->bw = RATE_INFO_BW_20;
if (stats->tx_rate_flags & (HAL_TX_RATE_HT40 | HAL_TX_RATE_VHT40))
info->bw = RATE_INFO_BW_40;
if (stats->tx_rate_flags & HAL_TX_RATE_VHT80)
info->bw = RATE_INFO_BW_80;
}
@@ -0,0 +1,169 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#ifndef _TXRX_H_
#define _TXRX_H_
#include <linux/etherdevice.h>
#include "wcn36xx.h"
/* TODO describe all properties */
#define WCN36XX_802_11_HEADER_LEN 24
#define WCN36XX_BMU_WQ_TX 25
#define WCN36XX_TID 7
/* broadcast wq ID */
#define WCN36XX_TX_B_WQ_ID 0xA
#define WCN36XX_TX_U_WQ_ID 0x9
/* bd_rate */
#define WCN36XX_BD_RATE_DATA 0
#define WCN36XX_BD_RATE_MGMT 2
#define WCN36XX_BD_RATE_CTRL 3
enum wcn36xx_txbd_ssn_type {
WCN36XX_TXBD_SSN_FILL_HOST = 0,
WCN36XX_TXBD_SSN_FILL_DPU_NON_QOS = 1,
WCN36XX_TXBD_SSN_FILL_DPU_QOS = 2,
};
struct wcn36xx_pdu {
u32 dpu_fb:8;
u32 adu_fb:8;
u32 pdu_id:16;
/* 0x04*/
u32 tail_pdu_idx:16;
u32 head_pdu_idx:16;
/* 0x08*/
u32 pdu_count:7;
u32 mpdu_data_off:9;
u32 mpdu_header_off:8;
u32 mpdu_header_len:8;
/* 0x0c*/
u32 reserved4:8;
u32 tid:4;
u32 bd_ssn:2;
u32 reserved3:2;
u32 mpdu_len:16;
};
struct wcn36xx_rx_bd {
u32 bdt:2;
u32 ft:1;
u32 dpu_ne:1;
u32 rx_key_id:3;
u32 ub:1;
u32 rmf:1;
u32 uma_bypass:1;
u32 csr11:1;
u32 reserved0:1;
u32 scan_learn:1;
u32 rx_ch:4;
u32 rtsf:1;
u32 bsf:1;
u32 a2hf:1;
u32 st_auf:1;
u32 dpu_sign:3;
u32 dpu_rf:8;
struct wcn36xx_pdu pdu;
/* 0x14*/
u32 addr3:8;
u32 addr2:8;
u32 addr1:8;
u32 dpu_desc_idx:8;
/* 0x18*/
u32 rxp_flags:23;
u32 rate_id:9;
u32 phy_stat0;
u32 phy_stat1;
/* 0x24 */
u32 rx_times;
u32 pmi_cmd[6];
/* 0x40 */
u32 reserved7:4;
u32 reorder_slot_id:6;
u32 reorder_fwd_id:6;
u32 reserved6:12;
u32 reorder_code:4;
/* 0x44 */
u32 exp_seq_num:12;
u32 cur_seq_num:12;
u32 rf_band:2;
u32 fr_type_subtype:6;
/* 0x48 */
u32 msdu_size:16;
u32 sub_fr_id:4;
u32 proc_order:4;
u32 reserved9:4;
u32 aef:1;
u32 lsf:1;
u32 esf:1;
u32 asf:1;
};
struct wcn36xx_tx_bd {
u32 bdt:2;
u32 ft:1;
u32 dpu_ne:1;
u32 fw_tx_comp:1;
u32 tx_comp:1;
u32 reserved1:1;
u32 ub:1;
u32 rmf:1;
u32 reserved0:12;
u32 dpu_sign:3;
u32 dpu_rf:8;
struct wcn36xx_pdu pdu;
/* 0x14*/
u32 reserved5:7;
u32 queue_id:5;
u32 bd_rate:2;
u32 ack_policy:2;
u32 sta_index:8;
u32 dpu_desc_idx:8;
u32 tx_bd_sign;
u32 reserved6;
u32 dxe_start_time;
u32 dxe_end_time;
/*u32 tcp_udp_start_off:10;
u32 header_cks:16;
u32 reserved7:6;*/
};
struct wcn36xx_sta;
struct wcn36xx;
int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb);
int wcn36xx_start_tx(struct wcn36xx *wcn,
struct wcn36xx_sta *sta_priv,
struct sk_buff *skb);
void wcn36xx_process_tx_rate(struct ani_global_class_a_stats_info *stats, struct rate_info *info);
#endif /* _TXRX_H_ */
@@ -0,0 +1,342 @@
/*
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
*
* 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.
*/
#ifndef _WCN36XX_H_
#define _WCN36XX_H_
#include <linux/completion.h>
#include <linux/in6.h>
#include <linux/printk.h>
#include <linux/spinlock.h>
#include <net/mac80211.h>
#include "hal.h"
#include "smd.h"
#include "txrx.h"
#include "dxe.h"
#include "pmc.h"
#include "debug.h"
#define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
#define WCN36XX_AGGR_BUFFER_SIZE 64
extern unsigned int wcn36xx_dbg_mask;
enum wcn36xx_debug_mask {
WCN36XX_DBG_DXE = 0x00000001,
WCN36XX_DBG_DXE_DUMP = 0x00000002,
WCN36XX_DBG_SMD = 0x00000004,
WCN36XX_DBG_SMD_DUMP = 0x00000008,
WCN36XX_DBG_RX = 0x00000010,
WCN36XX_DBG_RX_DUMP = 0x00000020,
WCN36XX_DBG_TX = 0x00000040,
WCN36XX_DBG_TX_DUMP = 0x00000080,
WCN36XX_DBG_HAL = 0x00000100,
WCN36XX_DBG_HAL_DUMP = 0x00000200,
WCN36XX_DBG_MAC = 0x00000400,
WCN36XX_DBG_BEACON = 0x00000800,
WCN36XX_DBG_BEACON_DUMP = 0x00001000,
WCN36XX_DBG_PMC = 0x00002000,
WCN36XX_DBG_PMC_DUMP = 0x00004000,
WCN36XX_DBG_TESTMODE = 0x00008000,
WCN36XX_DBG_TESTMODE_DUMP = 0x00010000,
WCN36XX_DBG_ANY = 0xffffffff,
};
#define wcn36xx_err(fmt, arg...) \
printk(KERN_ERR pr_fmt("ERROR " fmt), ##arg)
#define wcn36xx_warn(fmt, arg...) \
printk(KERN_WARNING pr_fmt("WARNING " fmt), ##arg)
#define wcn36xx_info(fmt, arg...) \
printk(KERN_INFO pr_fmt(fmt), ##arg)
#define wcn36xx_dbg(mask, fmt, arg...) do { \
if (wcn36xx_dbg_mask & mask) \
printk(KERN_DEBUG pr_fmt(fmt), ##arg); \
} while (0)
#define wcn36xx_dbg_dump(mask, prefix_str, buf, len) do { \
if (wcn36xx_dbg_mask & mask) \
print_hex_dump(KERN_DEBUG, pr_fmt(prefix_str), \
DUMP_PREFIX_OFFSET, 32, 1, \
buf, len, false); \
} while (0)
enum wcn36xx_ampdu_state {
WCN36XX_AMPDU_NONE,
WCN36XX_AMPDU_INIT,
WCN36XX_AMPDU_START,
WCN36XX_AMPDU_OPERATIONAL,
};
#define HW_VALUE_PHY_SHIFT 8
#define HW_VALUE_PHY(hw_value) ((hw_value) >> HW_VALUE_PHY_SHIFT)
#define HW_VALUE_CHANNEL(hw_value) ((hw_value) & 0xFF)
#define WCN36XX_HW_CHANNEL(__wcn)\
HW_VALUE_CHANNEL(__wcn->hw->conf.chandef.chan->hw_value)
#define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band)
#define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq)
#define WCN36XX_LISTEN_INTERVAL(__wcn) (__wcn->hw->conf.listen_interval)
#define WCN36XX_FLAGS(__wcn) (__wcn->hw->flags)
#define WCN36XX_MAX_POWER(__wcn) (__wcn->hw->conf.chandef.chan->max_power)
#define RF_UNKNOWN 0x0000
#define RF_IRIS_WCN3620 0x3620
#define RF_IRIS_WCN3660 0x3660
#define RF_IRIS_WCN3680 0x3680
static inline void buff_to_be(void *buf, size_t len)
{
__be32 *to = buf;
u32 *from = buf;
int i;
for (i = 0; i < len; i++)
to[i] = cpu_to_be32(from[i]);
}
struct nv_data {
int is_valid;
u8 table;
};
/**
* struct wcn36xx_vif - holds VIF related fields
*
* @bss_index: bss_index is initially set to 0xFF. bss_index is received from
* HW after first config_bss call and must be used in delete_bss and
* enter/exit_bmps.
*/
struct wcn36xx_vif {
struct list_head list;
u8 dtim_period;
enum ani_ed_type encrypt_type;
bool is_joining;
bool sta_assoc;
struct wcn36xx_hal_mac_ssid ssid;
enum wcn36xx_hal_bss_type bss_type;
/* Power management */
enum wcn36xx_power_state pw_state;
u8 bss_index;
/* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */
u8 self_sta_index;
u8 self_dpu_desc_index;
u8 self_ucast_dpu_sign;
#if IS_ENABLED(CONFIG_IPV6)
/* IPv6 addresses for WoWLAN */
struct in6_addr target_ipv6_addrs[WCN36XX_HAL_IPV6_OFFLOAD_ADDR_MAX];
unsigned long tentative_addrs[BITS_TO_LONGS(WCN36XX_HAL_IPV6_OFFLOAD_ADDR_MAX)];
int num_target_ipv6_addrs;
#endif
/* WoWLAN GTK rekey data */
struct {
u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
__le64 replay_ctr;
bool valid;
} rekey_data;
struct list_head sta_list;
int bmps_fail_ct;
};
/**
* struct wcn36xx_sta - holds STA related fields
*
* @tid: traffic ID that is used during AMPDU and in TX BD.
* @sta_index: STA index is returned from HW after config_sta call and is
* used in both SMD channel and TX BD.
* @dpu_desc_index: DPU descriptor index is returned from HW after config_sta
* call and is used in TX BD.
* @bss_sta_index: STA index is returned from HW after config_bss call and is
* used in both SMD channel and TX BD. See table below when it is used.
* @bss_dpu_desc_index: DPU descriptor index is returned from HW after
* config_bss call and is used in TX BD.
* ______________________________________________
* | | STA | AP |
* |______________|_____________|_______________|
* | TX BD |bss_sta_index| sta_index |
* |______________|_____________|_______________|
* |all SMD calls |bss_sta_index| sta_index |
* |______________|_____________|_______________|
* |smd_delete_sta| sta_index | sta_index |
* |______________|_____________|_______________|
*/
struct wcn36xx_sta {
struct list_head list;
struct wcn36xx_vif *vif;
u16 aid;
u16 tid;
u8 sta_index;
u8 dpu_desc_index;
u8 ucast_dpu_sign;
u8 bss_sta_index;
u8 bss_dpu_desc_index;
bool is_data_encrypted;
/* Rates */
struct wcn36xx_hal_supported_rates_v1 supported_rates;
spinlock_t ampdu_lock; /* protects next two fields */
enum wcn36xx_ampdu_state ampdu_state[16];
int non_agg_frame_ct;
};
struct wcn36xx_dxe_ch;
struct wcn36xx_chan_survey {
s8 rssi;
u8 snr;
};
struct wcn36xx {
struct ieee80211_hw *hw;
struct device *dev;
struct list_head vif_list;
const char *nv_file;
const struct firmware *nv;
u8 fw_revision;
u8 fw_version;
u8 fw_minor;
u8 fw_major;
u32 fw_feat_caps[WCN36XX_HAL_CAPS_SIZE];
bool is_pronto;
bool is_pronto_v3;
/* extra byte for the NULL termination */
u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1];
bool first_boot;
/* IRQs */
int tx_irq;
int rx_irq;
void __iomem *ccu_base;
void __iomem *dxe_base;
struct rpmsg_endpoint *smd_channel;
struct qcom_smem_state *tx_enable_state;
unsigned tx_enable_state_bit;
struct qcom_smem_state *tx_rings_empty_state;
unsigned tx_rings_empty_state_bit;
/* prevents concurrent FW reconfiguration */
struct mutex conf_mutex;
/*
* smd_buf must be protected with smd_mutex to garantee
* that all messages are sent one after another
*/
u8 *hal_buf;
size_t hal_rsp_len;
struct mutex hal_mutex;
struct completion hal_rsp_compl;
struct workqueue_struct *hal_ind_wq;
struct work_struct hal_ind_work;
spinlock_t hal_ind_lock;
struct list_head hal_ind_queue;
struct cfg80211_scan_request *scan_req;
bool sw_scan;
u8 sw_scan_opchannel;
bool sw_scan_init;
u8 sw_scan_channel;
struct ieee80211_vif *sw_scan_vif;
struct mutex scan_lock;
bool scan_aborted;
/* DXE channels */
struct wcn36xx_dxe_ch dxe_tx_l_ch; /* TX low */
struct wcn36xx_dxe_ch dxe_tx_h_ch; /* TX high */
struct wcn36xx_dxe_ch dxe_rx_l_ch; /* RX low */
struct wcn36xx_dxe_ch dxe_rx_h_ch; /* RX high */
/* For synchronization of DXE resources from BH, IRQ and WQ contexts */
spinlock_t dxe_lock;
bool queues_stopped;
/* Memory pools */
struct wcn36xx_dxe_mem_pool mgmt_mem_pool;
struct wcn36xx_dxe_mem_pool data_mem_pool;
struct sk_buff *tx_ack_skb;
struct timer_list tx_ack_timer;
/* For A-MSDU re-aggregation */
struct sk_buff_head amsdu;
/* RF module */
unsigned rf_id;
#ifdef CONFIG_WCN36XX_DEBUGFS
/* Debug file system entry */
struct wcn36xx_dfs_entry dfs;
#endif /* CONFIG_WCN36XX_DEBUGFS */
struct ieee80211_supported_band *band;
struct ieee80211_channel *channel;
spinlock_t survey_lock; /* protects chan_survey */
struct wcn36xx_chan_survey chan_survey[];
};
static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
u8 major,
u8 minor,
u8 version,
u8 revision)
{
return (wcn->fw_major == major &&
wcn->fw_minor == minor &&
wcn->fw_version == version &&
wcn->fw_revision == revision);
}
void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates);
void wcn36xx_set_default_rates_v1(struct wcn36xx_hal_supported_rates_v1 *rates);
static inline
struct ieee80211_sta *wcn36xx_priv_to_sta(struct wcn36xx_sta *sta_priv)
{
return container_of((void *)sta_priv, struct ieee80211_sta, drv_priv);
}
static inline
struct wcn36xx_vif *wcn36xx_vif_to_priv(struct ieee80211_vif *vif)
{
return (struct wcn36xx_vif *) vif->drv_priv;
}
static inline
struct ieee80211_vif *wcn36xx_priv_to_vif(struct wcn36xx_vif *vif_priv)
{
return container_of((void *) vif_priv, struct ieee80211_vif, drv_priv);
}
static inline
struct wcn36xx_sta *wcn36xx_sta_to_priv(struct ieee80211_sta *sta)
{
return (struct wcn36xx_sta *)sta->drv_priv;
}
#endif /* _WCN36XX_H_ */