mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 13:14:07 +08:00
wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware
This is a mac80211 driver for Qualcomm WCN3660/WCN3680 devices. So far WCN3660/WCN3680 is available only on MSM platform. Firmware can be found here: https://www.codeaurora.org/cgit/external/hisense/platform/vendor/qcom-opensource/wlan/prima/tree/firmware_bin?h=8130_CS Wiki page is available here: http://wireless.kernel.org/en/users/Drivers/wcn36xx A lot people made a contribution to this driver. Here is the list in alphabetical order: Eugene Krasnikov <k.eugene.e@gmail.com> Kalle Valo <kvalo@qca.qualcomm.com> Olof Johansson <dev@skyshaper.net> Pontus Fuchs <pontus.fuchs@gmail.com> Yanbo Li <yanbol@qti.qualcomm.com> Signed-off-by: Eugene Krasnikov <k.eugene.e@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
c856197d6e
commit
8e84c25821
@ -6816,6 +6816,14 @@ L: linux-hexagon@vger.kernel.org
|
||||
S: Supported
|
||||
F: arch/hexagon/
|
||||
|
||||
QUALCOMM WCN36XX WIRELESS DRIVER
|
||||
M: Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
L: wcn36xx@lists.infradead.org
|
||||
W: http://wireless.kernel.org/en/users/Drivers/wcn36xx
|
||||
T: git git://github.com/KrasnikovEugene/wcn36xx.git
|
||||
S: Supported
|
||||
F: drivers/net/wireless/ath/wcn36xx/
|
||||
|
||||
QUICKCAM PARALLEL PORT WEBCAMS
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -32,5 +32,6 @@ source "drivers/net/wireless/ath/ath6kl/Kconfig"
|
||||
source "drivers/net/wireless/ath/ar5523/Kconfig"
|
||||
source "drivers/net/wireless/ath/wil6210/Kconfig"
|
||||
source "drivers/net/wireless/ath/ath10k/Kconfig"
|
||||
source "drivers/net/wireless/ath/wcn36xx/Kconfig"
|
||||
|
||||
endif
|
||||
|
@ -5,6 +5,7 @@ obj-$(CONFIG_ATH6KL) += ath6kl/
|
||||
obj-$(CONFIG_AR5523) += ar5523/
|
||||
obj-$(CONFIG_WIL6210) += wil6210/
|
||||
obj-$(CONFIG_ATH10K) += ath10k/
|
||||
obj-$(CONFIG_WCN36XX) += wcn36xx/
|
||||
|
||||
obj-$(CONFIG_ATH_COMMON) += ath.o
|
||||
|
||||
|
16
drivers/net/wireless/ath/wcn36xx/Kconfig
Normal file
16
drivers/net/wireless/ath/wcn36xx/Kconfig
Normal file
@ -0,0 +1,16 @@
|
||||
config WCN36XX
|
||||
tristate "Qualcomm Atheros WCN3660/3680 support"
|
||||
depends on MAC80211 && HAS_DMA
|
||||
---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.
|
7
drivers/net/wireless/ath/wcn36xx/Makefile
Normal file
7
drivers/net/wireless/ath/wcn36xx/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
obj-$(CONFIG_WCN36XX) := wcn36xx.o
|
||||
wcn36xx-y += main.o \
|
||||
dxe.o \
|
||||
txrx.o \
|
||||
smd.o \
|
||||
pmc.o \
|
||||
debug.o
|
188
drivers/net/wireless/ath/wcn36xx/debug.c
Normal file
188
drivers/net/wireless/ath/wcn36xx/debug.c
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#ifdef CONFIG_WCN36XX_DEBUGFS
|
||||
|
||||
static int wcn36xx_debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
file->private_data = inode->i_private;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_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 = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_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 = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_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 = wcn36xx_debugfs_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 (kstrtoul(begin, 0, (unsigned long *)(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 = wcn36xx_debugfs_open,
|
||||
.write = write_file_dump,
|
||||
};
|
||||
|
||||
#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, S_IRUSR | S_IWUSR,
|
||||
&fops_wcn36xx_bmps, wcn);
|
||||
ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn);
|
||||
}
|
||||
|
||||
void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
|
||||
{
|
||||
struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
|
||||
debugfs_remove_recursive(dfs->rootdir);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_WCN36XX_DEBUGFS */
|
49
drivers/net/wireless/ath/wcn36xx/debug.h
Normal file
49
drivers/net/wireless/ath/wcn36xx/debug.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
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_ */
|
805
drivers/net/wireless/ath/wcn36xx/dxe.c
Normal file
805
drivers/net/wireless/ath/wcn36xx/dxe.c
Normal file
@ -0,0 +1,805 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* DXE - DMA transfer engine
|
||||
* we have 2 channels(High prio and Low prio) for TX and 2 channels for RX.
|
||||
* through low channels data packets are transfered
|
||||
* through high channels managment packets are transfered
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include "wcn36xx.h"
|
||||
#include "txrx.h"
|
||||
|
||||
void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low)
|
||||
{
|
||||
struct wcn36xx_dxe_ch *ch = is_low ?
|
||||
&wcn->dxe_tx_l_ch :
|
||||
&wcn->dxe_tx_h_ch;
|
||||
|
||||
return ch->head_blk_ctl->bd_cpu_addr;
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
|
||||
{
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE,
|
||||
"wcn36xx_dxe_write_register: addr=%x, data=%x\n",
|
||||
addr, data);
|
||||
|
||||
writel(data, wcn->mmio + addr);
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
|
||||
{
|
||||
*data = readl(wcn->mmio + addr);
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE,
|
||||
"wcn36xx_dxe_read_register: addr=%x, data=%x\n",
|
||||
addr, *data);
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ch->desc_num && ctl; i++) {
|
||||
next = ctl->next;
|
||||
kfree(ctl);
|
||||
ctl = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *prev_ctl = NULL;
|
||||
struct wcn36xx_dxe_ctl *cur_ctl = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ch->desc_num; i++) {
|
||||
cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL);
|
||||
if (!cur_ctl)
|
||||
goto out_fail;
|
||||
|
||||
cur_ctl->ctl_blk_order = i;
|
||||
if (i == 0) {
|
||||
ch->head_blk_ctl = cur_ctl;
|
||||
ch->tail_blk_ctl = cur_ctl;
|
||||
} else if (ch->desc_num - 1 == i) {
|
||||
prev_ctl->next = cur_ctl;
|
||||
cur_ctl->next = ch->head_blk_ctl;
|
||||
} else {
|
||||
prev_ctl->next = cur_ctl;
|
||||
}
|
||||
prev_ctl = cur_ctl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
wcn36xx_dxe_free_ctl_block(ch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L;
|
||||
wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H;
|
||||
wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L;
|
||||
wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L;
|
||||
wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H;
|
||||
wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L;
|
||||
wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L;
|
||||
wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD;
|
||||
wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD;
|
||||
|
||||
wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB;
|
||||
wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB;
|
||||
|
||||
wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L;
|
||||
wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L;
|
||||
wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H;
|
||||
|
||||
/* DXE control block allocation */
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
/* Initialize SMSM state Clear TX Enable RING EMPTY STATE */
|
||||
ret = wcn->ctrl_ops->smsm_change_state(
|
||||
WCN36XX_SMSM_WLAN_TX_ENABLE,
|
||||
WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
wcn36xx_err("Failed to allocate DXE control blocks\n");
|
||||
wcn36xx_dxe_free_ctl_blks(wcn);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn)
|
||||
{
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch);
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch);
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch);
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch);
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch)
|
||||
{
|
||||
struct wcn36xx_dxe_desc *cur_dxe = NULL;
|
||||
struct wcn36xx_dxe_desc *prev_dxe = NULL;
|
||||
struct wcn36xx_dxe_ctl *cur_ctl = NULL;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc);
|
||||
wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr,
|
||||
GFP_KERNEL);
|
||||
if (!wcn_ch->cpu_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(wcn_ch->cpu_addr, 0, size);
|
||||
|
||||
cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr;
|
||||
cur_ctl = wcn_ch->head_blk_ctl;
|
||||
|
||||
for (i = 0; i < wcn_ch->desc_num; i++) {
|
||||
cur_ctl->desc = cur_dxe;
|
||||
cur_ctl->desc_phy_addr = wcn_ch->dma_addr +
|
||||
i * sizeof(struct wcn36xx_dxe_desc);
|
||||
|
||||
switch (wcn_ch->ch_type) {
|
||||
case WCN36XX_DXE_CH_TX_L:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L;
|
||||
cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L;
|
||||
break;
|
||||
case WCN36XX_DXE_CH_TX_H:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H;
|
||||
cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H;
|
||||
break;
|
||||
case WCN36XX_DXE_CH_RX_L:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
|
||||
cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L;
|
||||
break;
|
||||
case WCN36XX_DXE_CH_RX_H:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
|
||||
cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H;
|
||||
break;
|
||||
}
|
||||
if (0 == i) {
|
||||
cur_dxe->phy_next_l = 0;
|
||||
} else if ((0 < i) && (i < wcn_ch->desc_num - 1)) {
|
||||
prev_dxe->phy_next_l =
|
||||
cur_ctl->desc_phy_addr;
|
||||
} else if (i == (wcn_ch->desc_num - 1)) {
|
||||
prev_dxe->phy_next_l =
|
||||
cur_ctl->desc_phy_addr;
|
||||
cur_dxe->phy_next_l =
|
||||
wcn_ch->head_blk_ctl->desc_phy_addr;
|
||||
}
|
||||
cur_ctl = cur_ctl->next;
|
||||
prev_dxe = cur_dxe;
|
||||
cur_dxe++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch,
|
||||
struct wcn36xx_dxe_mem_pool *pool)
|
||||
{
|
||||
int i, chunk_size = pool->chunk_size;
|
||||
dma_addr_t bd_phy_addr = pool->phy_addr;
|
||||
void *bd_cpu_addr = pool->virt_addr;
|
||||
struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl;
|
||||
|
||||
for (i = 0; i < ch->desc_num; i++) {
|
||||
/* Only every second dxe needs a bd pointer,
|
||||
the other will point to the skb data */
|
||||
if (!(i & 1)) {
|
||||
cur->bd_phy_addr = bd_phy_addr;
|
||||
cur->bd_cpu_addr = bd_cpu_addr;
|
||||
bd_phy_addr += chunk_size;
|
||||
bd_cpu_addr += chunk_size;
|
||||
} else {
|
||||
cur->bd_phy_addr = 0;
|
||||
cur->bd_cpu_addr = NULL;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
|
||||
{
|
||||
int reg_data = 0;
|
||||
|
||||
wcn36xx_dxe_read_register(wcn,
|
||||
WCN36XX_DXE_INT_MASK_REG,
|
||||
®_data);
|
||||
|
||||
reg_data |= wcn_ch;
|
||||
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_INT_MASK_REG,
|
||||
(int)reg_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl)
|
||||
{
|
||||
struct wcn36xx_dxe_desc *dxe = ctl->desc;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dxe->dst_addr_l = dma_map_single(NULL,
|
||||
skb_tail_pointer(skb),
|
||||
WCN36XX_PKT_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
ctl->skb = skb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn,
|
||||
struct wcn36xx_dxe_ch *wcn_ch)
|
||||
{
|
||||
int i;
|
||||
struct wcn36xx_dxe_ctl *cur_ctl = NULL;
|
||||
|
||||
cur_ctl = wcn_ch->head_blk_ctl;
|
||||
|
||||
for (i = 0; i < wcn_ch->desc_num; i++) {
|
||||
wcn36xx_dxe_fill_skb(cur_ctl);
|
||||
cur_ctl = cur_ctl->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn,
|
||||
struct wcn36xx_dxe_ch *wcn_ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < wcn_ch->desc_num; i++) {
|
||||
kfree_skb(cur->skb);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
|
||||
{
|
||||
struct ieee80211_tx_info *info;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wcn->dxe_lock, flags);
|
||||
skb = wcn->tx_ack_skb;
|
||||
wcn->tx_ack_skb = NULL;
|
||||
spin_unlock_irqrestore(&wcn->dxe_lock, flags);
|
||||
|
||||
if (!skb) {
|
||||
wcn36xx_warn("Spurious TX complete indication\n");
|
||||
return;
|
||||
}
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (status == 1)
|
||||
info->flags |= IEEE80211_TX_STAT_ACK;
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status);
|
||||
|
||||
ieee80211_tx_status_irqsafe(wcn->hw, skb);
|
||||
ieee80211_wake_queues(wcn->hw);
|
||||
}
|
||||
|
||||
static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl;
|
||||
struct ieee80211_tx_info *info;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Make at least one loop of do-while because in case ring is
|
||||
* completely full head and tail are pointing to the same element
|
||||
* and while-do will not make any cycles.
|
||||
*/
|
||||
do {
|
||||
if (ctl->skb) {
|
||||
dma_unmap_single(NULL, ctl->desc->src_addr_l,
|
||||
ctl->skb->len, DMA_TO_DEVICE);
|
||||
info = IEEE80211_SKB_CB(ctl->skb);
|
||||
if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) {
|
||||
/* Keep frame until TX status comes */
|
||||
ieee80211_free_txskb(wcn->hw, ctl->skb);
|
||||
}
|
||||
spin_lock_irqsave(&ctl->skb_lock, flags);
|
||||
if (wcn->queues_stopped) {
|
||||
wcn->queues_stopped = false;
|
||||
ieee80211_wake_queues(wcn->hw);
|
||||
}
|
||||
spin_unlock_irqrestore(&ctl->skb_lock, flags);
|
||||
|
||||
ctl->skb = NULL;
|
||||
}
|
||||
ctl = ctl->next;
|
||||
} while (ctl != ch->head_blk_ctl &&
|
||||
!(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK));
|
||||
|
||||
ch->tail_blk_ctl = ctl;
|
||||
}
|
||||
|
||||
static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
|
||||
{
|
||||
struct wcn36xx *wcn = (struct wcn36xx *)dev;
|
||||
int int_src, int_reason;
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
|
||||
|
||||
if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) {
|
||||
wcn36xx_dxe_read_register(wcn,
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H,
|
||||
&int_reason);
|
||||
|
||||
/* TODO: Check int_reason */
|
||||
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_H);
|
||||
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_H);
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n");
|
||||
reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
|
||||
}
|
||||
|
||||
if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
|
||||
wcn36xx_dxe_read_register(wcn,
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L,
|
||||
&int_reason);
|
||||
/* TODO: Check int_reason */
|
||||
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_L);
|
||||
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_L);
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n");
|
||||
reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev)
|
||||
{
|
||||
struct wcn36xx *wcn = (struct wcn36xx *)dev;
|
||||
|
||||
disable_irq_nosync(wcn->rx_irq);
|
||||
wcn36xx_dxe_rx_frame(wcn);
|
||||
enable_irq(wcn->rx_irq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete,
|
||||
IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn);
|
||||
if (ret) {
|
||||
wcn36xx_err("failed to alloc tx irq\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH,
|
||||
"wcn36xx_rx", wcn);
|
||||
if (ret) {
|
||||
wcn36xx_err("failed to alloc rx irq\n");
|
||||
goto out_txirq;
|
||||
}
|
||||
|
||||
enable_irq_wake(wcn->rx_irq);
|
||||
|
||||
return 0;
|
||||
|
||||
out_txirq:
|
||||
free_irq(wcn->tx_irq, wcn);
|
||||
out_err:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
|
||||
struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl;
|
||||
struct wcn36xx_dxe_desc *dxe = ctl->desc;
|
||||
dma_addr_t dma_addr;
|
||||
struct sk_buff *skb;
|
||||
|
||||
while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
|
||||
skb = ctl->skb;
|
||||
dma_addr = dxe->dst_addr_l;
|
||||
wcn36xx_dxe_fill_skb(ctl);
|
||||
|
||||
switch (ch->ch_type) {
|
||||
case WCN36XX_DXE_CH_RX_L:
|
||||
dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
|
||||
WCN36XX_DXE_INT_CH1_MASK);
|
||||
break;
|
||||
case WCN36XX_DXE_CH_RX_H:
|
||||
dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
|
||||
WCN36XX_DXE_INT_CH3_MASK);
|
||||
break;
|
||||
default:
|
||||
wcn36xx_warn("Unknown channel\n");
|
||||
}
|
||||
|
||||
dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
wcn36xx_rx_skb(wcn, skb);
|
||||
ctl = ctl->next;
|
||||
dxe = ctl->desc;
|
||||
}
|
||||
|
||||
ch->head_blk_ctl = ctl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn)
|
||||
{
|
||||
int int_src;
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
|
||||
|
||||
/* RX_LOW_PRI */
|
||||
if (int_src & WCN36XX_DXE_INT_CH1_MASK) {
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_DXE_INT_CH1_MASK);
|
||||
wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch));
|
||||
}
|
||||
|
||||
/* RX_HIGH_PRI */
|
||||
if (int_src & WCN36XX_DXE_INT_CH3_MASK) {
|
||||
/* Clean up all the INT within this channel */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_DXE_INT_CH3_MASK);
|
||||
wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch));
|
||||
}
|
||||
|
||||
if (!int_src)
|
||||
wcn36xx_warn("No DXE interrupt pending\n");
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn)
|
||||
{
|
||||
size_t s;
|
||||
void *cpu_addr;
|
||||
|
||||
/* Allocate BD headers for MGMT frames */
|
||||
|
||||
/* Where this come from ask QC */
|
||||
wcn->mgmt_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE +
|
||||
16 - (WCN36XX_BD_CHUNK_SIZE % 8);
|
||||
|
||||
s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H;
|
||||
cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr,
|
||||
GFP_KERNEL);
|
||||
if (!cpu_addr)
|
||||
goto out_err;
|
||||
|
||||
wcn->mgmt_mem_pool.virt_addr = cpu_addr;
|
||||
memset(cpu_addr, 0, s);
|
||||
|
||||
/* Allocate BD headers for DATA frames */
|
||||
|
||||
/* Where this come from ask QC */
|
||||
wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE +
|
||||
16 - (WCN36XX_BD_CHUNK_SIZE % 8);
|
||||
|
||||
s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L;
|
||||
cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr,
|
||||
GFP_KERNEL);
|
||||
if (!cpu_addr)
|
||||
goto out_err;
|
||||
|
||||
wcn->data_mem_pool.virt_addr = cpu_addr;
|
||||
memset(cpu_addr, 0, s);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
wcn36xx_dxe_free_mem_pools(wcn);
|
||||
wcn36xx_err("Failed to allocate BD mempool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn)
|
||||
{
|
||||
if (wcn->mgmt_mem_pool.virt_addr)
|
||||
dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size *
|
||||
WCN36XX_DXE_CH_DESC_NUMB_TX_H,
|
||||
wcn->mgmt_mem_pool.virt_addr,
|
||||
wcn->mgmt_mem_pool.phy_addr);
|
||||
|
||||
if (wcn->data_mem_pool.virt_addr) {
|
||||
dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size *
|
||||
WCN36XX_DXE_CH_DESC_NUMB_TX_L,
|
||||
wcn->data_mem_pool.virt_addr,
|
||||
wcn->data_mem_pool.phy_addr);
|
||||
}
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
|
||||
struct wcn36xx_vif *vif_priv,
|
||||
struct sk_buff *skb,
|
||||
bool is_low)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = NULL;
|
||||
struct wcn36xx_dxe_desc *desc = NULL;
|
||||
struct wcn36xx_dxe_ch *ch = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
|
||||
|
||||
ctl = ch->head_blk_ctl;
|
||||
|
||||
spin_lock_irqsave(&ctl->next->skb_lock, flags);
|
||||
|
||||
/*
|
||||
* If skb is not null that means that we reached the tail of the ring
|
||||
* hence ring is full. Stop queues to let mac80211 back off until ring
|
||||
* has an empty slot again.
|
||||
*/
|
||||
if (NULL != ctl->next->skb) {
|
||||
ieee80211_stop_queues(wcn->hw);
|
||||
wcn->queues_stopped = true;
|
||||
spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
|
||||
|
||||
ctl->skb = NULL;
|
||||
desc = ctl->desc;
|
||||
|
||||
/* Set source address of the BD we send */
|
||||
desc->src_addr_l = ctl->bd_phy_addr;
|
||||
|
||||
desc->dst_addr_l = ch->dxe_wq;
|
||||
desc->fr_len = sizeof(struct wcn36xx_tx_bd);
|
||||
desc->ctrl = ch->ctrl_bd;
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");
|
||||
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
|
||||
(char *)desc, sizeof(*desc));
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
|
||||
"BD >>> ", (char *)ctl->bd_cpu_addr,
|
||||
sizeof(struct wcn36xx_tx_bd));
|
||||
|
||||
/* Set source address of the SKB we send */
|
||||
ctl = ctl->next;
|
||||
ctl->skb = skb;
|
||||
desc = ctl->desc;
|
||||
if (ctl->bd_cpu_addr) {
|
||||
wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc->src_addr_l = dma_map_single(NULL,
|
||||
ctl->skb->data,
|
||||
ctl->skb->len,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
desc->dst_addr_l = ch->dxe_wq;
|
||||
desc->fr_len = ctl->skb->len;
|
||||
|
||||
/* set dxe descriptor to VALID */
|
||||
desc->ctrl = ch->ctrl_skb;
|
||||
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
|
||||
(char *)desc, sizeof(*desc));
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB >>> ",
|
||||
(char *)ctl->skb->data, ctl->skb->len);
|
||||
|
||||
/* Move the head of the ring to the next empty descriptor */
|
||||
ch->head_blk_ctl = ctl->next;
|
||||
|
||||
/*
|
||||
* When connected and trying to send data frame chip can be in sleep
|
||||
* mode and writing to the register will not wake up the chip. Instead
|
||||
* notify chip about new frame through SMSM bus.
|
||||
*/
|
||||
if (is_low && vif_priv->pw_state == WCN36XX_BMPS) {
|
||||
wcn->ctrl_ops->smsm_change_state(
|
||||
0,
|
||||
WCN36XX_SMSM_WLAN_TX_ENABLE);
|
||||
} else {
|
||||
/* indicate End Of Packet and generate interrupt on descriptor
|
||||
* done.
|
||||
*/
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
ch->reg_ctrl, ch->def_ctrl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_init(struct wcn36xx *wcn)
|
||||
{
|
||||
int reg_data = 0, ret;
|
||||
|
||||
reg_data = WCN36XX_DXE_REG_RESET;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);
|
||||
|
||||
/* Setting interrupt path */
|
||||
reg_data = WCN36XX_DXE_CCU_INT;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for TX LOW channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch);
|
||||
wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);
|
||||
|
||||
/* Write channel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L,
|
||||
wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Program DMA destination addr for TX LOW */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_TX_L,
|
||||
WCN36XX_DXE_WQ_TX_L);
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data);
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for TX HIGH channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch);
|
||||
wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);
|
||||
|
||||
/* Write channel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H,
|
||||
wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Program DMA destination addr for TX HIGH */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_TX_H,
|
||||
WCN36XX_DXE_WQ_TX_H);
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data);
|
||||
|
||||
/* Enable channel interrupts */
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for RX LOW channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch);
|
||||
|
||||
/* For RX we need to preallocated buffers */
|
||||
wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);
|
||||
|
||||
/* Write channel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L,
|
||||
wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Write DMA source address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_SRC_ADDR_RX_L,
|
||||
WCN36XX_DXE_WQ_RX_L);
|
||||
|
||||
/* Program preallocated destination address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_RX_L,
|
||||
wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l);
|
||||
|
||||
/* Enable default control registers */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_REG_CTL_RX_L,
|
||||
WCN36XX_DXE_CH_DEFAULT_CTL_RX_L);
|
||||
|
||||
/* Enable channel interrupts */
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for RX HIGH channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch);
|
||||
|
||||
/* For RX we need to prealocat buffers */
|
||||
wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);
|
||||
|
||||
/* Write chanel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H,
|
||||
wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Write DMA source address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_SRC_ADDR_RX_H,
|
||||
WCN36XX_DXE_WQ_RX_H);
|
||||
|
||||
/* Program preallocated destination address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_RX_H,
|
||||
wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l);
|
||||
|
||||
/* Enable default control registers */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_REG_CTL_RX_H,
|
||||
WCN36XX_DXE_CH_DEFAULT_CTL_RX_H);
|
||||
|
||||
/* Enable channel interrupts */
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H);
|
||||
|
||||
ret = wcn36xx_dxe_request_irqs(wcn);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
|
||||
{
|
||||
free_irq(wcn->tx_irq, wcn);
|
||||
free_irq(wcn->rx_irq, wcn);
|
||||
|
||||
if (wcn->tx_ack_skb) {
|
||||
ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
|
||||
wcn->tx_ack_skb = NULL;
|
||||
}
|
||||
|
||||
wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch);
|
||||
wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch);
|
||||
}
|
284
drivers/net/wireless/ath/wcn36xx/dxe.h
Normal file
284
drivers/net/wireless/ath/wcn36xx/dxe.h
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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_BASE 0x03000000
|
||||
#define WCN36XX_DXE_MEM_REG 0x202000
|
||||
|
||||
#define WCN36XX_DXE_CCU_INT 0xA0011
|
||||
#define WCN36XX_DXE_REG_CCU_INT 0x200b10
|
||||
|
||||
/* TODO This must calculated properly but not hardcoded */
|
||||
#define WCN36XX_DXE_CTRL_TX_L 0x328a44
|
||||
#define WCN36XX_DXE_CTRL_TX_H 0x32ce44
|
||||
#define WCN36XX_DXE_CTRL_RX_L 0x12ad2f
|
||||
#define WCN36XX_DXE_CTRL_RX_H 0x12d12f
|
||||
#define WCN36XX_DXE_CTRL_TX_H_BD 0x30ce45
|
||||
#define WCN36XX_DXE_CTRL_TX_H_SKB 0x32ce4d
|
||||
#define WCN36XX_DXE_CTRL_TX_L_BD 0x308a45
|
||||
#define WCN36XX_DXE_CTRL_TX_L_SKB 0x328a4d
|
||||
|
||||
/* TODO This must calculated properly but not hardcoded */
|
||||
#define WCN36XX_DXE_WQ_TX_L 0x17
|
||||
#define WCN36XX_DXE_WQ_TX_H 0x17
|
||||
#define WCN36XX_DXE_WQ_RX_L 0xB
|
||||
#define WCN36XX_DXE_WQ_RX_H 0x4
|
||||
|
||||
/* DXE descriptor control filed */
|
||||
#define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001)
|
||||
|
||||
/* TODO This must calculated properly but not hardcoded */
|
||||
/* DXE default control register values */
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L 0x847EAD2F
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H 0x84FED12F
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H 0x853ECF4D
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L 0x843e8b4d
|
||||
|
||||
/* 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_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;
|
||||
spinlock_t skb_lock;
|
||||
void *bd_cpu_addr;
|
||||
dma_addr_t bd_phy_addr;
|
||||
};
|
||||
|
||||
struct wcn36xx_dxe_ch {
|
||||
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_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 sk_buff *skb,
|
||||
bool is_low);
|
||||
void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
|
||||
void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low);
|
||||
#endif /* _DXE_H_ */
|
4657
drivers/net/wireless/ath/wcn36xx/hal.h
Normal file
4657
drivers/net/wireless/ath/wcn36xx/hal.h
Normal file
File diff suppressed because it is too large
Load Diff
1036
drivers/net/wireless/ath/wcn36xx/main.c
Normal file
1036
drivers/net/wireless/ath/wcn36xx/main.c
Normal file
File diff suppressed because it is too large
Load Diff
62
drivers/net/wireless/ath/wcn36xx/pmc.c
Normal file
62
drivers/net/wireless/ath/wcn36xx/pmc.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
int ret = 0;
|
||||
struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
|
||||
/* 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;
|
||||
} 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");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
|
||||
|
||||
if (WCN36XX_BMPS != vif_priv->pw_state) {
|
||||
wcn36xx_err("Not in BMPS mode, no need to exit from BMPS mode!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
wcn36xx_smd_exit_bmps(wcn, vif);
|
||||
vif_priv->pw_state = WCN36XX_FULL_POWER;
|
||||
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);
|
||||
}
|
33
drivers/net/wireless/ath/wcn36xx/pmc.h
Normal file
33
drivers/net/wireless/ath/wcn36xx/pmc.h
Normal file
@ -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_ */
|
2126
drivers/net/wireless/ath/wcn36xx/smd.c
Normal file
2126
drivers/net/wireless/ath/wcn36xx/smd.c
Normal file
File diff suppressed because it is too large
Load Diff
127
drivers/net/wireless/ath/wcn36xx/smd.h
Normal file
127
drivers/net/wireless/ath/wcn36xx/smd.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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 200
|
||||
#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;
|
||||
u8 *msg;
|
||||
size_t msg_len;
|
||||
};
|
||||
|
||||
struct wcn36xx;
|
||||
|
||||
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_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode);
|
||||
int wcn36xx_smd_start_scan(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_end_scan(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
|
||||
enum wcn36xx_hal_sys_mode mode);
|
||||
int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn);
|
||||
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_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 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 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);
|
||||
int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index);
|
||||
int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index);
|
||||
|
||||
int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value);
|
||||
#endif /* _SMD_H_ */
|
284
drivers/net/wireless/ath/wcn36xx/txrx.c
Normal file
284
drivers/net/wireless/ath/wcn36xx/txrx.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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 "txrx.h"
|
||||
|
||||
static inline int get_rssi0(struct wcn36xx_rx_bd *bd)
|
||||
{
|
||||
return 100 - ((bd->phy_stat0 >> 24) & 0xff);
|
||||
}
|
||||
|
||||
int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_rx_status status;
|
||||
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((u32 *)bd, sizeof(*bd)/sizeof(u32));
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP,
|
||||
"BD <<< ", (char *)bd,
|
||||
sizeof(struct wcn36xx_rx_bd));
|
||||
|
||||
skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len);
|
||||
skb_pull(skb, bd->pdu.mpdu_header_off);
|
||||
|
||||
status.mactime = 10;
|
||||
status.freq = WCN36XX_CENTER_FREQ(wcn);
|
||||
status.band = WCN36XX_BAND(wcn);
|
||||
status.signal = -get_rssi0(bd);
|
||||
status.antenna = 1;
|
||||
status.rate_idx = 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 status->vendor_radiotap_len=%x\n",
|
||||
status.flag, status.vendor_radiotap_len);
|
||||
|
||||
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
fc = __le16_to_cpu(hdr->frame_control);
|
||||
sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
ieee80211_rx_irqsafe(wcn->hw, skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_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_set_tx_data(struct wcn36xx_tx_bd *bd,
|
||||
struct wcn36xx *wcn,
|
||||
struct wcn36xx_vif **vif_priv,
|
||||
struct wcn36xx_sta *sta_priv,
|
||||
struct ieee80211_hdr *hdr,
|
||||
bool bcast)
|
||||
{
|
||||
struct ieee80211_vif *vif = NULL;
|
||||
struct wcn36xx_vif *__vif_priv = NULL;
|
||||
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 = container_of((void *)__vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_priv);
|
||||
|
||||
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->ucast_dpu_signature;
|
||||
|
||||
if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
(sta_priv && !sta_priv->is_data_encrypted))
|
||||
bd->dpu_ne = 1;
|
||||
|
||||
if (bcast) {
|
||||
bd->ub = 1;
|
||||
bd->ack_policy = 1;
|
||||
}
|
||||
*vif_priv = __vif_priv;
|
||||
}
|
||||
|
||||
static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd,
|
||||
struct wcn36xx *wcn,
|
||||
struct wcn36xx_vif **vif_priv,
|
||||
struct ieee80211_hdr *hdr,
|
||||
bool bcast)
|
||||
{
|
||||
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) == IEEE80211_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;
|
||||
}
|
||||
|
||||
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);
|
||||
unsigned long flags;
|
||||
bool is_low = ieee80211_is_data(hdr->frame_control);
|
||||
bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
|
||||
is_multicast_ether_addr(hdr->addr1);
|
||||
struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low);
|
||||
|
||||
if (!bd) {
|
||||
/*
|
||||
* TX DXE are used in pairs. One for the BD and one for the
|
||||
* actual frame. The BD DXE's has a preallocated buffer while
|
||||
* the skb ones does not. If this isn't true something is really
|
||||
* wierd. TODO: Recover from this situation
|
||||
*/
|
||||
|
||||
wcn36xx_err("bd address may not be NULL for BD DXE\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||
if (bd->tx_comp) {
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
|
||||
spin_lock_irqsave(&wcn->dxe_lock, flags);
|
||||
if (wcn->tx_ack_skb) {
|
||||
spin_unlock_irqrestore(&wcn->dxe_lock, flags);
|
||||
wcn36xx_warn("tx_ack_skb already set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wcn->tx_ack_skb = skb;
|
||||
spin_unlock_irqrestore(&wcn->dxe_lock, flags);
|
||||
|
||||
/* Only one at a time is supported by fw. Stop the TX queues
|
||||
* until the ack status gets back.
|
||||
*
|
||||
* TODO: Add watchdog in case FW does not answer
|
||||
*/
|
||||
ieee80211_stop_queues(wcn->hw);
|
||||
}
|
||||
|
||||
/* Data frames served first*/
|
||||
if (is_low) {
|
||||
wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast);
|
||||
wcn36xx_set_tx_pdu(bd,
|
||||
ieee80211_is_data_qos(hdr->frame_control) ?
|
||||
sizeof(struct ieee80211_qos_hdr) :
|
||||
sizeof(struct ieee80211_hdr_3addr),
|
||||
skb->len, sta_priv ? sta_priv->tid : 0);
|
||||
} else {
|
||||
/* MGMT and CTRL frames are handeld here*/
|
||||
wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast);
|
||||
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);
|
||||
}
|
||||
|
||||
buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
|
||||
bd->tx_bd_sign = 0xbdbdbdbd;
|
||||
|
||||
return wcn36xx_dxe_tx_frame(wcn, vif_priv, skb, is_low);
|
||||
}
|
160
drivers/net/wireless/ath/wcn36xx/txrx.h
Normal file
160
drivers/net/wireless/ath/wcn36xx/txrx.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
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 reserved3:4;
|
||||
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 fr_type_subtype:8;
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif /* _TXRX_H_ */
|
238
drivers/net/wireless/ath/wcn36xx/wcn36xx.h
Normal file
238
drivers/net/wireless/ath/wcn36xx/wcn36xx.h
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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/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_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)
|
||||
|
||||
#define WCN36XX_HW_CHANNEL(__wcn) (__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)
|
||||
|
||||
static inline void buff_to_be(u32 *buf, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = cpu_to_be32(buf[i]);
|
||||
}
|
||||
|
||||
struct nv_data {
|
||||
int is_valid;
|
||||
u8 table;
|
||||
};
|
||||
|
||||
/* Interface for platform control path
|
||||
*
|
||||
* @open: hook must be called when wcn36xx wants to open control channel.
|
||||
* @tx: sends a buffer.
|
||||
*/
|
||||
struct wcn36xx_platform_ctrl_ops {
|
||||
int (*open)(void *drv_priv, void *rsp_cb);
|
||||
void (*close)(void);
|
||||
int (*tx)(char *buf, size_t len);
|
||||
int (*get_hw_mac)(u8 *addr);
|
||||
int (*smsm_change_state)(u32 clear_mask, u32 set_mask);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
struct wcn36xx_sta *sta;
|
||||
u8 dtim_period;
|
||||
enum ani_ed_type encrypt_type;
|
||||
bool is_joining;
|
||||
struct wcn36xx_hal_mac_ssid ssid;
|
||||
|
||||
/* Power management */
|
||||
enum wcn36xx_power_state pw_state;
|
||||
|
||||
u8 bss_index;
|
||||
u8 ucast_dpu_signature;
|
||||
/* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */
|
||||
u8 self_sta_index;
|
||||
u8 self_dpu_desc_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 bellow 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 wcn36xx_vif *vif;
|
||||
u16 aid;
|
||||
u16 tid;
|
||||
u8 sta_index;
|
||||
u8 dpu_desc_index;
|
||||
u8 bss_sta_index;
|
||||
u8 bss_dpu_desc_index;
|
||||
bool is_data_encrypted;
|
||||
/* Rates */
|
||||
struct wcn36xx_hal_supported_rates supported_rates;
|
||||
};
|
||||
struct wcn36xx_dxe_ch;
|
||||
struct wcn36xx {
|
||||
struct ieee80211_hw *hw;
|
||||
struct device *dev;
|
||||
struct list_head vif_list;
|
||||
|
||||
u8 fw_revision;
|
||||
u8 fw_version;
|
||||
u8 fw_minor;
|
||||
u8 fw_major;
|
||||
|
||||
/* extra byte for the NULL termination */
|
||||
u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
|
||||
u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1];
|
||||
|
||||
/* IRQs */
|
||||
int tx_irq;
|
||||
int rx_irq;
|
||||
void __iomem *mmio;
|
||||
|
||||
struct wcn36xx_platform_ctrl_ops *ctrl_ops;
|
||||
/*
|
||||
* 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;
|
||||
struct mutex hal_ind_mutex;
|
||||
struct list_head hal_ind_queue;
|
||||
|
||||
/* 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;
|
||||
|
||||
#ifdef CONFIG_WCN36XX_DEBUGFS
|
||||
/* Debug file system entry */
|
||||
struct wcn36xx_dfs_entry dfs;
|
||||
#endif /* CONFIG_WCN36XX_DEBUGFS */
|
||||
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
#endif /* _WCN36XX_H_ */
|
Loading…
Reference in New Issue
Block a user