/*
|
* Copyright (c) 2015 South Silicon Valley Microelectronics Inc.
|
* Copyright (c) 2015 iComm Corporation
|
*
|
* This program is free software: you can redistribute it and/or modify
|
* it under the terms of the GNU General Public License as published by
|
* the Free Software Foundation, either version 3 of the License, or
|
* (at your option) any later version.
|
* This program is distributed in the hope that it will be useful, but
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
* See the GNU General Public License for more details.
|
* You should have received a copy of the GNU General Public License
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
*/
|
|
#include <linux/nl80211.h>
|
#include <linux/etherdevice.h>
|
#include <linux/delay.h>
|
#include <linux/version.h>
|
#include <linux/time.h>
|
#include <linux/kthread.h>
|
#include <linux/ktime.h>
|
|
#include <net/mac80211.h>
|
#include <ssv6200.h>
|
#include <hci/hctrl.h>
|
#include "linux_80211.h"
|
#include "lib.h"
|
#include "ssv_rc.h"
|
#include "ssv_ht_rc.h"
|
#include "dev.h"
|
#include "ap.h"
|
#include "init.h"
|
#include "p2p.h"
|
#ifdef CONFIG_SSV_SUPPORT_ANDROID
|
#include "ssv_pm.h"
|
#endif
|
#ifdef MULTI_THREAD_ENCRYPT
|
#include <linux/freezer.h>
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0) && LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
|
#include "linux_3_0_0.h"
|
#endif
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
#include "ssv6xxx_debugfs.h"
|
#endif
|
#ifdef CONFIG_SSV_RSSI
|
struct rssi_res_st rssi_res, *p_rssi_res;
|
#endif
|
#define NO_USE_RXQ_LOCK
|
#ifndef WLAN_CIPHER_SUITE_SMS4
|
#define WLAN_CIPHER_SUITE_SMS4 0x00147201
|
#endif
|
#ifdef ENABLE_TX_Q_FLOW_CONTROL
|
#ifdef MULTI_THREAD_ENCRYPT
|
#define MAX_CRYPTO_Q_LEN (64)
|
#define LOW_CRYPTO_Q_LEN (MAX_CRYPTO_Q_LEN/2)
|
#endif
|
#define MAX_TX_Q_LEN (64)
|
#define LOW_TX_Q_LEN (MAX_TX_Q_LEN/2)
|
#endif
|
static u16 bits_per_symbol[][2] =
|
{
|
{ 26, 54 },
|
{ 52, 108 },
|
{ 78, 162 },
|
{ 104, 216 },
|
{ 156, 324 },
|
{ 208, 432 },
|
{ 234, 486 },
|
{ 260, 540 },
|
};
|
#ifdef CONFIG_DEBUG_SKB_TIMESTAMP
|
extern struct ssv6xxx_hci_ctrl *ssv_dbg_ctrl_hci;
|
extern unsigned int cal_duration_of_ampdu(struct sk_buff *ampdu_skb, int stage);
|
#endif
|
struct ssv6xxx_calib_table {
|
u16 channel_id;
|
u32 rf_ctrl_N;
|
u32 rf_ctrl_F;
|
u16 rf_precision_default;
|
};
|
static void _process_rx_q (struct ssv_softc *sc, struct sk_buff_head *rx_q, spinlock_t *rx_q_lock);
|
static u32 _process_tx_done (struct ssv_softc *sc);
|
#ifdef MULTI_THREAD_ENCRYPT
|
static u32 _remove_sta_skb_from_q (struct ssv_softc *sc, struct sk_buff_head *q,
|
u32 addr0_3, u32 addr4_5);
|
#ifdef CONFIG_DEBUG_SKB_TIMESTAMP
|
unsigned int cal_duration_of_mpdu(struct sk_buff *mpdu_skb)
|
{
|
unsigned int timeout;
|
SKB_info *mpdu_skb_info = (SKB_info *)mpdu_skb->head;
|
timeout = (unsigned int)ktime_to_ms(ktime_sub(ktime_get(), mpdu_skb_info->timestamp));
|
if (timeout > SKB_DURATION_TIMEOUT_MS)
|
printk("*mpdu_duration: %ums\n", timeout);
|
return timeout;
|
}
|
#endif
|
unsigned int skb_queue_len_safe(struct sk_buff_head *list)
|
{
|
unsigned long flags;
|
unsigned int ret = 0;
|
spin_lock_irqsave(&list->lock, flags);
|
ret = skb_queue_len(list);
|
spin_unlock_irqrestore(&list->lock, flags);
|
return ret;
|
}
|
#endif
|
#if 1
|
void _ssv6xxx_hexdump(const char *title, const u8 *buf,
|
size_t len)
|
{
|
size_t i;
|
printk("%s - hexdump(len=%lu):\n", title, (unsigned long) len);
|
if (buf == NULL) {
|
printk(" [NULL]");
|
}else{
|
for (i = 0; i < len; i++){
|
printk(" %02x", buf[i]);
|
if((i+1)%16 ==0)
|
printk("\n");
|
}
|
}
|
printk("\n-----------------------------\n");
|
}
|
#endif
|
void ssv6xxx_txbuf_free_skb(struct sk_buff *skb, void *args)
|
{
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
|
struct ssv_softc *sc = (struct ssv_softc *)args;
|
#endif
|
if (!skb)
|
return;
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
|
ieee80211_free_txskb(sc->hw, skb);
|
#else
|
dev_kfree_skb_any(skb);
|
#endif
|
}
|
#define ADDRESS_OFFSET 16
|
#define HW_ID_OFFSET 7
|
#define CH0_FULL_MASK CH0_FULL_MSK
|
#define MAX_FAIL_COUNT 100
|
#define MAX_RETRY_COUNT 20
|
inline bool ssv6xxx_mcu_input_full(struct ssv_softc *sc)
|
{
|
u32 regval=0;
|
SMAC_REG_READ(sc->sh, ADR_MCU_STATUS, ®val);
|
return CH0_FULL_MASK®val;
|
}
|
u32 ssv6xxx_pbuf_alloc(struct ssv_softc *sc, int size, int type)
|
{
|
u32 regval, pad;
|
int cnt = MAX_RETRY_COUNT;
|
int page_cnt = (size + ((1 << HW_MMU_PAGE_SHIFT) - 1)) >> HW_MMU_PAGE_SHIFT;
|
regval = 0;
|
mutex_lock(&sc->mem_mutex);
|
pad = size%4;
|
size += pad;
|
do{
|
SMAC_REG_WRITE(sc->sh, ADR_WR_ALC, (size | (type << 16)));
|
SMAC_REG_READ(sc->sh, ADR_WR_ALC, ®val);
|
if (regval == 0) {
|
cnt--;
|
msleep(1);
|
}
|
else
|
break;
|
} while (cnt);
|
if (type == TX_BUF)
|
{
|
sc->sh->tx_page_available -= page_cnt;
|
sc->sh->page_count[PACKET_ADDR_2_ID(regval)] = page_cnt;
|
}
|
mutex_unlock(&sc->mem_mutex);
|
if (regval == 0)
|
dev_err(sc->dev, "Failed to allocate packet buffer of %d bytes in %d type.",
|
size, type);
|
else
|
{
|
dev_info(sc->dev, "Allocated %d type packet buffer of size %d (%d) at address %x.\n",
|
type, size, page_cnt, regval);
|
}
|
return regval;
|
}
|
bool ssv6xxx_pbuf_free(struct ssv_softc *sc, u32 pbuf_addr)
|
{
|
u32 regval=0;
|
u16 failCount=0;
|
u8 *p_tx_page_cnt = &sc->sh->page_count[PACKET_ADDR_2_ID(pbuf_addr)];
|
while (ssv6xxx_mcu_input_full(sc))
|
{
|
if (failCount++ < 1000) continue;
|
printk("=============>ERROR!!MAILBOX Block[%d]\n", failCount);
|
return false;
|
}
|
mutex_lock(&sc->mem_mutex);
|
regval = ((M_ENG_TRASH_CAN << HW_ID_OFFSET) |(pbuf_addr >> ADDRESS_OFFSET));
|
printk("[A] ssv6xxx_pbuf_free addr[%08x][%x]\n", pbuf_addr, regval);
|
SMAC_REG_WRITE(sc->sh, ADR_CH0_TRIG_1, regval);
|
if (*p_tx_page_cnt)
|
{
|
sc->sh->tx_page_available += *p_tx_page_cnt;
|
*p_tx_page_cnt = 0;
|
}
|
mutex_unlock(&sc->mem_mutex);
|
return true;
|
}
|
#ifdef CONFIG_SSV_CABRIO_A
|
static const struct ssv6xxx_calib_table vt_tbl[] =
|
{
|
{ 1, 0xf1, 0x333333, 3859},
|
{ 2, 0xf1, 0xB33333, 3867},
|
{ 3, 0xf2, 0x333333, 3875},
|
{ 4, 0xf2, 0xB33333, 3883},
|
{ 5, 0xf3, 0x333333, 3891},
|
{ 6, 0xf3, 0xB33333, 3899},
|
{ 7, 0xf4, 0x333333, 3907},
|
{ 8, 0xf4, 0xB33333, 3915},
|
{ 9, 0xf5, 0x333333, 3923},
|
{ 10, 0xf5, 0xB33333, 3931},
|
{ 11, 0xf6, 0x333333, 3939},
|
{ 12, 0xf6, 0xB33333, 3947},
|
{ 13, 0xf7, 0x333333, 3955},
|
{ 14, 0xf8, 0x666666, 3974},
|
};
|
int ssv6xxx_set_channel(struct ssv_softc *sc, int ch)
|
{
|
int retry_cnt, fail_cnt=0;
|
struct ssv_hw *sh=sc->sh;
|
u32 regval;
|
int ret = 0;
|
int chidx;
|
bool chidx_vld = 0;
|
for(chidx = 0; chidx < (sizeof(vt_tbl)/sizeof(vt_tbl[0])); chidx++) {
|
if (vt_tbl[chidx].channel_id == ch) {
|
chidx_vld = 1;
|
break;
|
}
|
}
|
if (chidx_vld == 0) {
|
printk("%s(): fail! channel_id not found in vt_tbl\n", __FUNCTION__);
|
return -1;
|
}
|
do {
|
if ((ret = SMAC_REG_READ(sh, ADR_SPI_TO_PHY_PARAM1, ®val)) != 0) break;
|
if ((ret = SMAC_REG_WRITE(sh,ADR_SPI_TO_PHY_PARAM1,(regval&~0xffff)|3)) != 0) break;
|
ssv6xxx_rf_disable(sc->sh);
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_CBR_SYN_DIV_SDM_XOSC,
|
(0x01<<13), (0x01<<13))) != 0) break;
|
regval = vt_tbl[chidx].rf_ctrl_F;
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_CBR_SYN_RGISTER_1,
|
(regval << 0), 0x00ffffff)) != 0) break;
|
regval = vt_tbl[chidx].rf_ctrl_N;
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_SYN_RGISTER_2,
|
(regval<<0), 0x000007ff)) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_MANUAL_REGISTER,
|
(64<<1), (0x000007f<<1))) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_MANUAL_REGISTER,
|
(1<<0), 0x00000001)) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_MANUAL_REGISTER,
|
(0<<0), 0x00000001)) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_SX_ENABLE_RGISTER,
|
(1<<11), 0x00000800)) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_SX_ENABLE_RGISTER,
|
(0<<12), 0x00001000)) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_SX_ENABLE_RGISTER,
|
(1<<12), 0x00001000)) != 0) break;
|
for(retry_cnt=20; retry_cnt>0; retry_cnt--)
|
{
|
mdelay(20);
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_READ_ONLY_FLAGS_1, ®val)) != 0) break;
|
if (regval & 0x00000004)
|
{
|
if ((ret = SMAC_REG_SET_BITS(sh, ADR_CBR_SX_ENABLE_RGISTER,
|
(0<<12), 0x00001000)) != 0) break;
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_READ_ONLY_FLAGS_1, ®val)) != 0) break;
|
if ((regval & 0x00001800) == 0)
|
{
|
ssv6xxx_rf_enable(sh);
|
return 0;
|
}
|
else
|
{
|
printk("%s(): Lock channel %d fail!\n", __FUNCTION__, vt_tbl[chidx].channel_id);
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_READ_ONLY_FLAGS_1, ®val)) != 0) break;
|
printk("%s(): dbg: vt-mon read out as %d when rdy\n", __FUNCTION__, ((regval & 0x00001800) >> 11));
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_READ_ONLY_FLAGS_2, ®val)) != 0) break;
|
printk("%s(): dbg: sub-sel read out as %d when rdy\n", __FUNCTION__, ((regval & 0x00000fe0) >> 5));
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_SYN_DIV_SDM_XOSC, ®val)) != 0) break;
|
printk("%s(): dbg: RG_SX_REFBYTWO read out as %d when rdy\n", __FUNCTION__, ((regval & 0x00002000) >> 13));
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_SYN_RGISTER_1, ®val)) != 0) break;
|
printk("%s(): dbg: RG_SX_RFCTRL_F read out as 0x%08x when rdy\n", __FUNCTION__, ((regval & 0x00ffffff) >> 0));
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_SYN_RGISTER_2, ®val)) != 0) break;
|
printk("%s(): dbg: RG_SX_RFCTRL_CH read out as 0x%08x when rdy\n", __FUNCTION__, ((regval & 0x000007ff) >> 0));
|
if ((ret = SMAC_REG_READ(sh, ADR_CBR_SX_ENABLE_RGISTER, ®val)) != 0) break;
|
printk("%s(): dbg: RG_EN_SX_VT_MON_DG read out as %d when rdy\n", __FUNCTION__, ((regval & 0x00001000) >> 12));
|
}
|
}
|
}
|
fail_cnt++;
|
printk("%s(): calibration fail [%d] rounds!!\n",
|
__FUNCTION__, fail_cnt);
|
if(fail_cnt == 100)
|
return -1;
|
} while(ret == 0);
|
return ret;
|
}
|
#endif
|
#ifdef CONFIG_SSV_CABRIO_E
|
static const struct ssv6xxx_calib_table vt_tbl[SSV6XXX_IQK_CFG_XTAL_MAX][14]=
|
{
|
{
|
{ 1, 0xB9, 0x89D89E, 3859},
|
{ 2, 0xB9, 0xEC4EC5, 3867},
|
{ 3, 0xBA, 0x4EC4EC, 3875},
|
{ 4, 0xBA, 0xB13B14, 3883},
|
{ 5, 0xBB, 0x13B13B, 3891},
|
{ 6, 0xBB, 0x762762, 3899},
|
{ 7, 0xBB, 0xD89D8A, 3907},
|
{ 8, 0xBC, 0x3B13B1, 3915},
|
{ 9, 0xBC, 0x9D89D9, 3923},
|
{ 10, 0xBD, 0x000000, 3931},
|
{ 11, 0xBD, 0x627627, 3939},
|
{ 12, 0xBD, 0xC4EC4F, 3947},
|
{ 13, 0xBE, 0x276276, 3955},
|
{ 14, 0xBF, 0x13B13B, 3974},
|
},
|
{
|
{ 1, 0xf1, 0x333333, 3859},
|
{ 2, 0xf1, 0xB33333, 3867},
|
{ 3, 0xf2, 0x333333, 3875},
|
{ 4, 0xf2, 0xB33333, 3883},
|
{ 5, 0xf3, 0x333333, 3891},
|
{ 6, 0xf3, 0xB33333, 3899},
|
{ 7, 0xf4, 0x333333, 3907},
|
{ 8, 0xf4, 0xB33333, 3915},
|
{ 9, 0xf5, 0x333333, 3923},
|
{ 10, 0xf5, 0xB33333, 3931},
|
{ 11, 0xf6, 0x333333, 3939},
|
{ 12, 0xf6, 0xB33333, 3947},
|
{ 13, 0xf7, 0x333333, 3955},
|
{ 14, 0xf8, 0x666666, 3974},
|
},
|
{
|
{ 1, 0xC9, 0x000000, 3859},
|
{ 2, 0xC9, 0x6AAAAB, 3867},
|
{ 3, 0xC9, 0xD55555, 3875},
|
{ 4, 0xCA, 0x400000, 3883},
|
{ 5, 0xCA, 0xAAAAAB, 3891},
|
{ 6, 0xCB, 0x155555, 3899},
|
{ 7, 0xCB, 0x800000, 3907},
|
{ 8, 0xCB, 0xEAAAAB, 3915},
|
{ 9, 0xCC, 0x555555, 3923},
|
{ 10, 0xCC, 0xC00000, 3931},
|
{ 11, 0xCD, 0x2AAAAB, 3939},
|
{ 12, 0xCD, 0x955555, 3947},
|
{ 13, 0xCE, 0x000000, 3955},
|
{ 14, 0xCF, 0x000000, 3974},
|
}
|
};
|
#define FAIL_MAX 100
|
#define RETRY_MAX 20
|
int ssv6xxx_set_channel(struct ssv_softc *sc, int ch)
|
{
|
struct ssv_hw *sh=sc->sh;
|
int retry_cnt, fail_cnt=0;
|
u32 regval;
|
int ret = -1;
|
int chidx;
|
bool chidx_vld = 0;
|
dev_dbg(sc->dev, "Setting channel to %d\n", ch);
|
if((sh->cfg.chip_identity == SSV6051Z) || (sc->sh->cfg.chip_identity == SSV6051P))
|
{
|
if((ch == 13) || (ch == 14))
|
{
|
if(sh->ipd_channel_touch == 0)
|
{
|
for (chidx = 0; chidx < sh->ch_cfg_size; chidx++)
|
{
|
SMAC_REG_WRITE(sh, sh->p_ch_cfg[chidx].reg_addr, sh->p_ch_cfg[chidx].ch13_14_value);
|
}
|
sh->ipd_channel_touch = 1;
|
}
|
}
|
else
|
{
|
if(sh->ipd_channel_touch)
|
{
|
for (chidx = 0; chidx < sh->ch_cfg_size; chidx++)
|
{
|
SMAC_REG_WRITE(sh, sh->p_ch_cfg[chidx].reg_addr, sh->p_ch_cfg[chidx].ch1_12_value);
|
}
|
sh->ipd_channel_touch = 0;
|
}
|
}
|
}
|
for(chidx = 0; chidx < 14; chidx++) {
|
if (vt_tbl[sh->cfg.crystal_type][chidx].channel_id == ch) {
|
chidx_vld = 1;
|
break;
|
}
|
}
|
if (chidx_vld == 0) {
|
dev_dbg(sc->dev, "%s(): fail! channel_id not found in vt_tbl\n", __FUNCTION__);
|
goto exit;
|
}
|
if ((ret = ssv6xxx_rf_disable(sc->sh)) != 0)
|
goto exit;
|
do {
|
if((sh->cfg.crystal_type == SSV6XXX_IQK_CFG_XTAL_26M) || (sh->cfg.crystal_type == SSV6XXX_IQK_CFG_XTAL_24M))
|
{
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_SYN_DIV_SDM_XOSC,
|
(0x00<<13), (0x01<<13))) != 0) break;
|
}
|
else if(sh->cfg.crystal_type == SSV6XXX_IQK_CFG_XTAL_40M)
|
{
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_SYN_DIV_SDM_XOSC,
|
(0x01<<13), (0x01<<13))) != 0) break;
|
}
|
else
|
{
|
printk("Illegal xtal setting -- ssv6xxx_set_channel\n");
|
BUG_ON(1);
|
}
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_SX_LCK_BIN_REGISTERS_I,
|
(0x01<<19), (0x01<<19))) != 0) break;
|
regval = vt_tbl[sh->cfg.crystal_type][chidx].rf_ctrl_F;
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_SYN_REGISTER_1,
|
(regval<<0), (0x00ffffff<<0))) != 0) break;
|
regval = vt_tbl[sh->cfg.crystal_type][chidx].rf_ctrl_N;
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_SYN_REGISTER_2,
|
(regval<<0), (0x07ff<<0))) != 0) break;
|
if ((ret = SMAC_REG_READ(sc->sh, ADR_SX_LCK_BIN_REGISTERS_I, ®val)) != 0) break;
|
regval = vt_tbl[sh->cfg.crystal_type][chidx].rf_precision_default;
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_SX_LCK_BIN_REGISTERS_II,
|
(regval<<0), (0x1fff<<0))) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_MANUAL_ENABLE_REGISTER,
|
(0x00<<14), (0x01<<14))) != 0) break;
|
if ((ret = SMAC_REG_SET_BITS(sc->sh, ADR_MANUAL_ENABLE_REGISTER,
|
(0x01<<14), (0x01<<14))) != 0) break;
|
retry_cnt = 0;
|
do
|
{
|
mdelay(1);
|
if ((ret = SMAC_REG_READ(sc->sh, ADR_READ_ONLY_FLAGS_1, ®val)) != 0) break;
|
if (regval & 0x00000002)
|
{
|
if ((ret = SMAC_REG_READ(sc->sh, ADR_READ_ONLY_FLAGS_2, ®val)) != 0) break;
|
ret = ssv6xxx_rf_enable(sc->sh);
|
#if 0
|
printk("Lock to channel %d ([0xce010098]=%x)!!\n", vt_tbl[sh->cfg.crystal_type][chidx].channel_id, regval);
|
printk("crystal_type [%d]\n",sh->cfg.crystal_type);
|
SMAC_REG_READ(sc->sh, 0xce010040, ®val);
|
printk("0xce010040 [%x]\n",regval);
|
SMAC_REG_READ(sc->sh, 0xce0100a4, ®val);
|
printk("0xce0100a4 [%x]\n",regval);
|
SMAC_REG_READ(sc->sh, ADR_DPLL_DIVIDER_REGISTER, ®val);
|
printk("0xce010060 [%x]\n",regval);
|
SMAC_REG_READ(sc->sh, ADR_SX_ENABLE_REGISTER, ®val);
|
printk("0xce010038 [%x]\n",regval);
|
SMAC_REG_READ(sc->sh, 0xce01003C, ®val);
|
printk("0xce01003C [%x]\n",regval);
|
SMAC_REG_READ(sc->sh, ADR_DPLL_FB_DIVIDER_REGISTERS_I, ®val);
|
printk("0xce01009c [%x]\n",regval);
|
SMAC_REG_READ(sc->sh, ADR_DPLL_FB_DIVIDER_REGISTERS_II, ®val);
|
printk("0xce0100a0 [%x]\n",regval);
|
printk("[%x][%x][%x]\n",vt_tbl[sh->cfg.crystal_type][chidx].rf_ctrl_N,vt_tbl[sh->cfg.crystal_type][chidx].rf_ctrl_F,vt_tbl[sh->cfg.crystal_type][chidx].rf_precision_default);
|
#endif
|
//dev_info(sc->dev, "Lock to channel %d ([0xce010098]=%x)!!\n", vt_tbl[sh->cfg.crystal_type][chidx].channel_id, regval);
|
sc->hw_chan = ch;
|
goto exit;
|
}
|
retry_cnt++;
|
}
|
while(retry_cnt < RETRY_MAX);
|
fail_cnt++;
|
printk("calibation fail:[%d]\n", fail_cnt);
|
}
|
while((fail_cnt < FAIL_MAX) && (ret == 0));
|
exit:
|
if (ch == 14 && regval == 0xff0) {
|
SMAC_IFC_RESET(sc->sh);
|
ssv6xxx_restart_hw(sc);
|
}
|
if(ch <= 7)
|
{
|
if(sh->cfg.tx_power_index_1)
|
{
|
SMAC_REG_READ(sc->sh, ADR_RX_TX_FSM_REGISTER, ®val);
|
regval &= RG_TX_GAIN_OFFSET_I_MSK;
|
regval |= (sh->cfg.tx_power_index_1 << RG_TX_GAIN_OFFSET_SFT);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_TX_FSM_REGISTER, regval);
|
}
|
else if(sh->cfg.tx_power_index_2)
|
{
|
SMAC_REG_READ(sc->sh, ADR_RX_TX_FSM_REGISTER, ®val);
|
regval &= RG_TX_GAIN_OFFSET_I_MSK;
|
SMAC_REG_WRITE(sc->sh, ADR_RX_TX_FSM_REGISTER, regval);
|
}
|
}
|
else
|
{
|
if(sh->cfg.tx_power_index_2)
|
{
|
SMAC_REG_READ(sc->sh, ADR_RX_TX_FSM_REGISTER, ®val);
|
regval &= RG_TX_GAIN_OFFSET_I_MSK;
|
regval |= (sh->cfg.tx_power_index_2 << RG_TX_GAIN_OFFSET_SFT);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_TX_FSM_REGISTER, regval);
|
}
|
else if(sh->cfg.tx_power_index_1)
|
{
|
SMAC_REG_READ(sc->sh, ADR_RX_TX_FSM_REGISTER, ®val);
|
regval &= RG_TX_GAIN_OFFSET_I_MSK;
|
SMAC_REG_WRITE(sc->sh, ADR_RX_TX_FSM_REGISTER, regval);
|
}
|
}
|
return ret;
|
}
|
#ifdef CONFIG_SSV_SMARTLINK
|
int ssv6xxx_get_channel(struct ssv_softc *sc, int *pch)
|
{
|
*pch = sc->hw_chan;
|
return 0;
|
}
|
int ssv6xxx_set_promisc(struct ssv_softc *sc, int accept)
|
{
|
u32 val=0;
|
if (accept)
|
{
|
val = 0x2;
|
}
|
else
|
{
|
val = 0x3;
|
}
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_TB13, val);
|
return 0;
|
}
|
int ssv6xxx_get_promisc(struct ssv_softc *sc, int *paccept)
|
{
|
u32 val=0;
|
SMAC_REG_READ(sc->sh, ADR_MRX_FLT_TB13, &val);
|
if (val == 0x2)
|
{
|
*paccept = 1;
|
}
|
else
|
{
|
*paccept = 0;
|
}
|
return 0;
|
}
|
#endif
|
#endif
|
int ssv6xxx_rf_enable(struct ssv_hw *sh)
|
{
|
return SMAC_REG_SET_BITS(sh,
|
0xce010000,
|
(0x02<<12), (0x03<<12)
|
);
|
}
|
int ssv6xxx_rf_disable(struct ssv_hw *sh)
|
{
|
return SMAC_REG_SET_BITS(sh,
|
0xce010000,
|
(0x01<<12), (0x03<<12)
|
);
|
}
|
int ssv6xxx_update_decision_table(struct ssv_softc *sc)
|
{
|
int i;
|
for(i=0; i<MAC_DECITBL1_SIZE; i++) {
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_TB0+i*4,
|
sc->mac_deci_tbl[i]);
|
SMAC_REG_CONFIRM(sc->sh, ADR_MRX_FLT_TB0+i*4,
|
sc->mac_deci_tbl[i]);
|
}
|
for(i=0; i<MAC_DECITBL2_SIZE; i++) {
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_EN0+i*4,
|
sc->mac_deci_tbl[i+MAC_DECITBL1_SIZE]);
|
SMAC_REG_CONFIRM(sc->sh, ADR_MRX_FLT_EN0+i*4,
|
sc->mac_deci_tbl[i+MAC_DECITBL1_SIZE]);
|
}
|
return 0;
|
}
|
static int ssv6xxx_frame_hdrlen(struct ieee80211_hdr *hdr, bool is_ht)
|
{
|
#define CTRL_FRAME_INDEX(fc) ((hdr->frame_control-IEEE80211_STYPE_BACK_REQ)>>4)
|
u16 fc, CTRL_FLEN[]= { 16, 16, 16, 16, 10, 10, 16, 16 };
|
int hdr_len = 24;
|
fc = hdr->frame_control;
|
if (ieee80211_is_ctl(fc))
|
hdr_len = CTRL_FLEN[CTRL_FRAME_INDEX(fc)];
|
else if (ieee80211_is_mgmt(fc)) {
|
if (ieee80211_has_order(fc))
|
hdr_len += ((is_ht==1)? 4: 0);
|
}
|
else {
|
if (ieee80211_has_a4(fc))
|
hdr_len += 6;
|
if (ieee80211_is_data_qos(fc)) {
|
hdr_len += 2;
|
if (ieee80211_has_order(hdr->frame_control) &&
|
is_ht==true)
|
hdr_len += 4;
|
}
|
}
|
return hdr_len;
|
}
|
#if 0
|
static void ssv6xxx_dump_tx_desc(struct sk_buff *skb)
|
{
|
struct ssv6200_tx_desc *tx_desc;
|
int s;
|
u8 *dat;
|
tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
printk(">> Tx Frame:\n");
|
for(s=0, dat=skb->data; s<tx_desc->hdr_len; s++) {
|
printk("%02x ", dat[sizeof(*tx_desc)+s]);
|
if (((s+1)& 0x0F) == 0)
|
printk("\n");
|
}
|
printk("length: %d, c_type=%d, f80211=%d, qos=%d, ht=%d, use_4addr=%d, sec=%d\n",
|
tx_desc->len, tx_desc->c_type, tx_desc->f80211, tx_desc->qos, tx_desc->ht,
|
tx_desc->use_4addr, tx_desc->security);
|
printk("more_data=%d, sub_type=%x, extra_info=%d\n", tx_desc->more_data,
|
tx_desc->stype_b5b4, tx_desc->extra_info);
|
printk("fcmd=0x%08x, hdr_offset=%d, frag=%d, unicast=%d, hdr_len=%d\n",
|
tx_desc->fCmd, tx_desc->hdr_offset, tx_desc->frag, tx_desc->unicast,
|
tx_desc->hdr_len);
|
printk("tx_burst=%d, ack_policy=%d, do_rts_cts=%d, reason=%d, payload_offset=%d\n",
|
tx_desc->tx_burst, tx_desc->ack_policy, tx_desc->do_rts_cts,
|
tx_desc->reason, tx_desc->payload_offset);
|
printk("fcmdidx=%d, wsid=%d, txq_idx=%d\n",
|
tx_desc->fCmdIdx, tx_desc->wsid, tx_desc->txq_idx);
|
printk("RTS/CTS Nav=%d, frame_time=%d, crate_idx=%d, drate_idx=%d, dl_len=%d\n",
|
tx_desc->rts_cts_nav, tx_desc->frame_consume_time, tx_desc->crate_idx, tx_desc->drate_idx,
|
tx_desc->dl_length);
|
}
|
static void ssv6xxx_dump_rx_desc(struct sk_buff *skb)
|
{
|
struct ssv6200_rx_desc *rx_desc;
|
rx_desc = (struct ssv6200_rx_desc *)skb->data;
|
printk(">> RX Descriptor:\n");
|
printk("len=%d, c_type=%d, f80211=%d, qos=%d, ht=%d, use_4addr=%d, l3cs_err=%d, l4_cs_err=%d\n",
|
rx_desc->len, rx_desc->c_type, rx_desc->f80211, rx_desc->qos, rx_desc->ht, rx_desc->use_4addr,
|
rx_desc->l3cs_err, rx_desc->l4cs_err);
|
printk("align2=%d, psm=%d, stype_b5b4=%d, extra_info=%d\n",
|
rx_desc->align2, rx_desc->psm, rx_desc->stype_b5b4, rx_desc->extra_info);
|
printk("hdr_offset=%d, reason=%d, rx_result=%d\n", rx_desc->hdr_offset,
|
rx_desc->reason, rx_desc->RxResult);
|
}
|
#endif
|
static u32 ssv6xxx_ht_txtime(u8 rix, int pktlen, int width,
|
int half_gi, bool is_gf)
|
{
|
u32 nbits, nsymbits, duration, nsymbols;
|
int streams;
|
streams = 1;
|
nbits = (pktlen << 3) + OFDM_PLCP_BITS;
|
nsymbits = bits_per_symbol[rix % 8][width] * streams;
|
nsymbols = (nbits + nsymbits - 1) / nsymbits;
|
if (!half_gi)
|
duration = SYMBOL_TIME(nsymbols);
|
else
|
{
|
if (!is_gf)
|
duration = DIV_ROUND_UP(SYMBOL_TIME_HALFGI(nsymbols), 4)<<2;
|
else
|
duration = SYMBOL_TIME_HALFGI(nsymbols);
|
}
|
duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams)+HT_SIGNAL_EXT;
|
if (is_gf)
|
duration -=12;
|
duration += HT_SIFS_TIME;
|
return duration;
|
}
|
static u32 ssv6xxx_non_ht_txtime(u8 phy, int kbps,
|
u32 frameLen, bool shortPreamble)
|
{
|
u32 bits_per_symbol, num_bits, num_symbols;
|
u32 phy_time, tx_time;
|
if (kbps == 0)
|
return 0;
|
switch (phy) {
|
case WLAN_RC_PHY_CCK:
|
phy_time = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
|
if (shortPreamble)
|
phy_time >>= 1;
|
num_bits = frameLen << 3;
|
tx_time = CCK_SIFS_TIME + phy_time + ((num_bits * 1000) / kbps);
|
break;
|
case WLAN_RC_PHY_OFDM:
|
bits_per_symbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
|
num_bits = OFDM_PLCP_BITS + (frameLen << 3);
|
num_symbols = DIV_ROUND_UP(num_bits, bits_per_symbol);
|
tx_time = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME
|
+ (num_symbols * OFDM_SYMBOL_TIME);
|
break;
|
default:
|
printk("Unknown phy %u\n", phy);
|
BUG_ON(1);
|
tx_time = 0;
|
break;
|
}
|
return tx_time;
|
}
|
static u32 ssv6xxx_set_frame_duration(struct ieee80211_tx_info *info,
|
struct ssv_rate_info *ssv_rate, u16 len,
|
struct ssv6200_tx_desc *tx_desc, struct fw_rc_retry_params *rc_params,
|
struct ssv_softc *sc)
|
{
|
struct ieee80211_tx_rate *tx_drate;
|
u32 frame_time=0, ack_time=0, rts_cts_nav=0, frame_consume_time=0;
|
u32 l_length=0, drate_kbps=0, crate_kbps=0;
|
bool ctrl_short_preamble=false, is_sgi, is_ht40;
|
bool is_ht, is_gf;
|
int d_phy ,c_phy, nRCParams, mcsidx;
|
struct ssv_rate_ctrl *ssv_rc = NULL;
|
tx_drate = &info->control.rates[0];
|
is_sgi = !!(tx_drate->flags & IEEE80211_TX_RC_SHORT_GI);
|
is_ht40 = !!(tx_drate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH);
|
is_ht = !!(tx_drate->flags & IEEE80211_TX_RC_MCS);
|
is_gf = !!(tx_drate->flags & IEEE80211_TX_RC_GREEN_FIELD);
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
|
if ((info->control.short_preamble) ||
|
(tx_drate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
|
ctrl_short_preamble = true;
|
#else
|
if ((info->control.vif &&
|
info->control.vif->bss_conf.use_short_preamble) ||
|
(tx_drate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
|
ctrl_short_preamble = true;
|
#endif
|
#ifdef FW_RC_RETRY_DEBUG
|
printk("mcs = %d, data rate idx=%d\n",tx_drate->idx, tx_drate[3].count);
|
#endif
|
for (nRCParams = 0; (nRCParams < SSV62XX_TX_MAX_RATES) ; nRCParams++)
|
{
|
if ((rc_params == NULL) || (sc == NULL))
|
{
|
mcsidx = tx_drate->idx;
|
drate_kbps = ssv_rate->drate_kbps;
|
crate_kbps = ssv_rate->crate_kbps;
|
}
|
else
|
{
|
if(rc_params[nRCParams].count == 0)
|
{
|
break;
|
}
|
ssv_rc = sc->rc;
|
mcsidx = (rc_params[nRCParams].drate - SSV62XX_RATE_MCS_INDEX) % MCS_GROUP_RATES;
|
drate_kbps = ssv_rc->rc_table[rc_params[nRCParams].drate].rate_kbps;
|
crate_kbps = ssv_rc->rc_table[rc_params[nRCParams].crate].rate_kbps;
|
}
|
if (tx_drate->flags & IEEE80211_TX_RC_MCS) {
|
frame_time = ssv6xxx_ht_txtime(mcsidx,
|
len, is_ht40, is_sgi, is_gf);
|
d_phy = 0;
|
}
|
else {
|
if ((info->band == INDEX_80211_BAND_2GHZ) &&
|
!(ssv_rate->d_flags & IEEE80211_RATE_ERP_G))
|
d_phy = WLAN_RC_PHY_CCK;
|
else
|
d_phy = WLAN_RC_PHY_OFDM;
|
frame_time = ssv6xxx_non_ht_txtime(d_phy, drate_kbps,
|
len, ctrl_short_preamble);
|
}
|
if ((info->band == INDEX_80211_BAND_2GHZ) &&
|
!(ssv_rate->c_flags & IEEE80211_RATE_ERP_G))
|
c_phy = WLAN_RC_PHY_CCK;
|
else
|
c_phy = WLAN_RC_PHY_OFDM;
|
if (tx_desc->unicast) {
|
if (info->flags & IEEE80211_TX_CTL_AMPDU){
|
ack_time = ssv6xxx_non_ht_txtime(c_phy,
|
crate_kbps, BA_LEN, ctrl_short_preamble);
|
} else {
|
ack_time = ssv6xxx_non_ht_txtime(c_phy,
|
crate_kbps, ACK_LEN, ctrl_short_preamble);
|
}
|
}
|
if (tx_desc->do_rts_cts & IEEE80211_TX_RC_USE_RTS_CTS) {
|
rts_cts_nav = frame_time;
|
rts_cts_nav += ack_time;
|
rts_cts_nav += ssv6xxx_non_ht_txtime(c_phy,
|
crate_kbps, CTS_LEN, ctrl_short_preamble);
|
frame_consume_time = rts_cts_nav;
|
frame_consume_time += ssv6xxx_non_ht_txtime(c_phy,
|
crate_kbps, RTS_LEN, ctrl_short_preamble);
|
}else if (tx_desc->do_rts_cts & IEEE80211_TX_RC_USE_CTS_PROTECT) {
|
rts_cts_nav = frame_time;
|
rts_cts_nav += ack_time;
|
frame_consume_time = rts_cts_nav;
|
frame_consume_time += ssv6xxx_non_ht_txtime(c_phy,
|
crate_kbps, CTS_LEN, ctrl_short_preamble);
|
}
|
else{;}
|
if (tx_drate->flags & IEEE80211_TX_RC_MCS) {
|
l_length = frame_time - HT_SIFS_TIME;
|
l_length = ((l_length-(HT_SIGNAL_EXT+20))+3)>>2;
|
l_length += ((l_length<<1) - 3);
|
}
|
if((rc_params == NULL) || (sc == NULL))
|
{
|
tx_desc->rts_cts_nav = rts_cts_nav;
|
tx_desc->frame_consume_time = (frame_consume_time>>5)+1;;
|
tx_desc->dl_length = l_length;
|
break;
|
}
|
else
|
{
|
rc_params[nRCParams].rts_cts_nav = rts_cts_nav;
|
rc_params[nRCParams].frame_consume_time = (frame_consume_time>>5)+1;
|
rc_params[nRCParams].dl_length = l_length;
|
if(nRCParams == 0)
|
{
|
tx_desc->drate_idx = rc_params[nRCParams].drate;
|
tx_desc->crate_idx = rc_params[nRCParams].crate;
|
tx_desc->rts_cts_nav = rc_params[nRCParams].rts_cts_nav;
|
tx_desc->frame_consume_time = rc_params[nRCParams].frame_consume_time;
|
tx_desc->dl_length = rc_params[nRCParams].dl_length;
|
}
|
}
|
}
|
return ack_time;
|
}
|
static void ssv6200_hw_set_pair_type(struct ssv_hw *sh,u8 type)
|
{
|
u32 temp;
|
SMAC_REG_READ(sh,ADR_SCRT_SET,&temp);
|
temp = (temp & PAIR_SCRT_I_MSK);
|
temp |= (type << PAIR_SCRT_SFT);
|
SMAC_REG_WRITE(sh,ADR_SCRT_SET, temp);
|
printk("==>%s: write cipher type %d into hw\n",__func__,type);
|
}
|
static u32 ssv6200_hw_get_pair_type(struct ssv_hw *sh)
|
{
|
u32 temp;
|
SMAC_REG_READ(sh,ADR_SCRT_SET,&temp);
|
temp &= PAIR_SCRT_MSK;
|
temp = (temp >> PAIR_SCRT_SFT);
|
SMAC_REG_WRITE(sh,ADR_SCRT_SET, temp);
|
printk("==>%s: read cipher type %d from hw\n",__func__, temp);
|
return temp;
|
}
|
static void ssv6200_hw_set_group_type(struct ssv_hw *sh,u8 type)
|
{
|
u32 temp;
|
SMAC_REG_READ(sh,ADR_SCRT_SET,&temp);
|
temp = temp & GRP_SCRT_I_MSK;
|
temp |= (type << GRP_SCRT_SFT);
|
SMAC_REG_WRITE(sh,ADR_SCRT_SET, temp);
|
printk(KERN_ERR "Set group key type %d\n", type);
|
}
|
void ssv6xxx_reset_sec_module(struct ssv_softc *sc)
|
{
|
ssv6200_hw_set_group_type(sc->sh, ME_NONE);
|
ssv6200_hw_set_pair_type(sc->sh, ME_NONE);
|
}
|
#ifdef FW_WSID_WATCH_LIST
|
static int hw_update_watch_wsid(struct ssv_softc *sc, struct ieee80211_sta *sta,
|
struct ssv_sta_info *sta_info, int sta_idx, int rx_hw_sec, int ops)
|
{
|
int ret = 0;
|
int retry_cnt=20;
|
struct sk_buff *skb = NULL;
|
struct cfg_host_cmd *host_cmd;
|
struct ssv6xxx_wsid_params *ptr;
|
printk("cmd=%d for fw wsid list, wsid %d \n", ops, sta_idx);
|
skb = ssv_skb_alloc(HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_wsid_params));
|
if(skb == NULL || sta_info == NULL || sc == NULL)
|
return -1;
|
skb->data_len = HOST_CMD_HDR_LEN + sizeof(struct ssv6xxx_wsid_params);
|
skb->len = skb->data_len;
|
host_cmd = (struct cfg_host_cmd *)skb->data;
|
host_cmd->c_type = HOST_CMD;
|
host_cmd->h_cmd = (u8)SSV6XXX_HOST_CMD_WSID_OP;
|
host_cmd->len = skb->data_len;
|
ptr = (struct ssv6xxx_wsid_params *)host_cmd->dat8;
|
ptr->cmd = ops;
|
ptr->hw_security = rx_hw_sec;
|
if ((ptr->cmd != SSV6XXX_WSID_OPS_HWWSID_PAIRWISE_SET_TYPE)
|
&& (ptr->cmd != SSV6XXX_WSID_OPS_HWWSID_GROUP_SET_TYPE)) {
|
ptr->wsid_idx = (u8)(sta_idx - SSV_NUM_HW_STA);
|
} else {
|
ptr->wsid_idx = (u8)(sta_idx);
|
};
|
memcpy(&ptr->target_wsid, &sta->addr[0], 6);
|
while(((sc->sh->hci.hci_ops->hci_send_cmd(skb)) != 0) && (retry_cnt))
|
{
|
printk(KERN_INFO "WSID cmd=%d retry=%d!!\n", ops, retry_cnt);
|
retry_cnt--;
|
}
|
printk("%s: wsid_idx = %u\n", __FUNCTION__, ptr->wsid_idx);
|
ssv_skb_free(skb);
|
if(ops == SSV6XXX_WSID_OPS_ADD)
|
sta_info->hw_wsid = sta_idx;
|
return ret;
|
}
|
#endif
|
static void hw_crypto_key_clear(struct ieee80211_hw *hw, int index, struct ieee80211_key_conf *key,
|
struct ssv_vif_priv_data *vif_priv, struct ssv_sta_priv_data *sta_priv)
|
{
|
#ifdef FW_WSID_WATCH_LIST
|
struct ssv_softc *sc = hw->priv;
|
struct ssv_sta_info *sta_info = NULL;
|
if ((index == 0) && (sta_priv == NULL))
|
return;
|
#endif
|
if ((index < 0) || (index >= 4))
|
return;
|
#if 0
|
if(sta_info){
|
sta_info->s_flags &= ~STA_FLAG_ENCRYPT;
|
}
|
#endif
|
if (index > 0)
|
{
|
if (vif_priv)
|
vif_priv->group_key_idx = 0;
|
if (sta_priv)
|
sta_priv->group_key_idx = 0;
|
}
|
#ifdef FW_WSID_WATCH_LIST
|
if(sta_priv)
|
{
|
sta_info = &sc->sta_info[sta_priv->sta_idx];
|
if ((index == 0) && (sta_priv->has_hw_decrypt == true) && (sta_info->hw_wsid >= SSV_NUM_HW_STA))
|
{
|
hw_update_watch_wsid(sc, sta_info->sta, sta_info, sta_priv->sta_idx, SSV6XXX_WSID_SEC_PAIRWISE
|
, SSV6XXX_WSID_OPS_DISABLE_CAPS);
|
}
|
}
|
if(vif_priv)
|
{
|
if((index != 0) && !list_empty(&vif_priv->sta_list))
|
{
|
struct ssv_sta_priv_data *sta_priv_iter;
|
list_for_each_entry(sta_priv_iter, &vif_priv->sta_list, list)
|
{
|
if (((sta_priv_iter->sta_info->s_flags & STA_FLAG_VALID) == 0)
|
|| (sta_priv_iter->sta_info->hw_wsid < SSV_NUM_HW_STA))
|
continue;
|
hw_update_watch_wsid(sc, sta_priv_iter->sta_info->sta,
|
sta_priv_iter->sta_info, sta_priv_iter->sta_idx, SSV6XXX_WSID_SEC_GROUP
|
, SSV6XXX_WSID_OPS_DISABLE_CAPS);
|
}
|
}
|
}
|
#endif
|
#if 0
|
if (index == 0) {
|
address = sec_key_tbl+(3*sizeof(struct ssv6xxx_hw_key))
|
+ wsid*sizeof(struct ssv6xxx_hw_sta_key);
|
for(i=0;i<(sizeof(struct ssv6xxx_hw_sta_key)/4);i++)
|
SMAC_REG_WRITE(sc->sh, address+i*4, 0x0);
|
}
|
else{
|
address = sec_key_tbl+((index-1)*sizeof(struct ssv6xxx_hw_key));
|
for(i=0;i<(sizeof(struct ssv6xxx_hw_key)/4);i++)
|
SMAC_REG_WRITE(sc->sh,address+i*4, 0x0);
|
}
|
#endif
|
}
|
static void _set_wep_sw_crypto_key (struct ssv_softc *sc,
|
struct ssv_vif_info *vif_info,
|
struct ssv_sta_info *sta_info,
|
void *param)
|
{
|
struct ssv_sta_priv_data *sta_priv = (struct ssv_sta_priv_data *)sta_info->sta->drv_priv;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)vif_info->vif->drv_priv;
|
sta_priv->has_hw_encrypt = vif_priv->has_hw_encrypt;
|
sta_priv->has_hw_decrypt = vif_priv->has_hw_decrypt;
|
sta_priv->need_sw_encrypt = vif_priv->need_sw_encrypt;
|
sta_priv->need_sw_decrypt = vif_priv->need_sw_decrypt;
|
#ifdef USE_LOCAL_WEP_CRYPTO
|
sta_priv->crypto_data.ops = vif_priv->crypto_data.ops;
|
sta_priv->crypto_data.priv = vif_priv->crypto_data.priv;
|
#endif
|
}
|
static void _set_wep_hw_crypto_pair_key (struct ssv_softc *sc,
|
struct ssv_vif_info *vif_info,
|
struct ssv_sta_info *sta_info,
|
void *param)
|
{
|
int wsid = sta_info->hw_wsid;
|
struct ssv6xxx_hw_sec *sram_key = (struct ssv6xxx_hw_sec *)param;
|
int address = 0;
|
int *pointer = NULL;
|
#ifdef SSV6200_ECO
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key[0];
|
#else
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key;
|
#endif
|
u32 sec_key_tbl = sec_key_tbl_base;
|
int i;
|
u8 *key = sram_key->sta_key[0].pair.key;
|
u32 key_len = *(u16 *)&sram_key->sta_key[0].reserve[0];
|
struct ssv_sta_priv_data *sta_priv = (struct ssv_sta_priv_data *)sta_info->sta->drv_priv;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)vif_info->vif->drv_priv;
|
if (wsid == (-1))
|
return;
|
sram_key->sta_key[wsid].pair_key_idx = 0;
|
sram_key->sta_key[wsid].group_key_idx = 0;
|
sta_priv->has_hw_encrypt = vif_priv->has_hw_encrypt;
|
sta_priv->has_hw_decrypt = vif_priv->has_hw_decrypt;
|
sta_priv->need_sw_encrypt = vif_priv->need_sw_encrypt;
|
sta_priv->need_sw_decrypt = vif_priv->need_sw_decrypt;
|
if (wsid != 0)
|
memcpy(sram_key->sta_key[wsid].pair.key, key, key_len);
|
address = sec_key_tbl
|
+ (3*sizeof(struct ssv6xxx_hw_key))
|
+ wsid*sizeof(struct ssv6xxx_hw_sta_key);
|
#ifdef SSV6200_ECO
|
address += (0x10000*wsid);
|
#endif
|
pointer = (int *)&sram_key->sta_key[wsid];
|
#if 0
|
printk(KERN_ERR "Set STA %d WEP pairwise key to %08X.", wsid, address);
|
printk(KERN_ERR "Set WEP %02X %02X %02X %02X %02X %02X %02X %02X... \n",
|
key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]);
|
#endif
|
for (i = 0; i < (sizeof(struct ssv6xxx_hw_sta_key)/4); i++)
|
SMAC_REG_WRITE(sc->sh, address+(i*4), *(pointer++));
|
}
|
static void _set_wep_hw_crypto_group_key (struct ssv_softc *sc,
|
struct ssv_vif_info *vif_info,
|
struct ssv_sta_info *sta_info,
|
void *param)
|
{
|
int wsid = sta_info->hw_wsid;
|
struct ssv6xxx_hw_sec *sram_key = (struct ssv6xxx_hw_sec *)param;
|
int address = 0;
|
int *pointer = NULL;
|
u32 key_idx = sram_key->sta_key[0].pair_key_idx;
|
#ifdef SSV6200_ECO
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key[0];
|
u32 key_len = *(u16 *)&sram_key->sta_key[0].reserve[0];
|
u8 *key = sram_key->group_key[key_idx - 1].key;
|
#else
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key;
|
#endif
|
u32 sec_key_tbl = sec_key_tbl_base;
|
struct ssv_sta_priv_data *sta_priv = (struct ssv_sta_priv_data *)sta_info->sta->drv_priv;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)vif_info->vif->drv_priv;
|
if (wsid == (-1))
|
return;
|
if (wsid != 0)
|
{
|
sram_key->sta_key[wsid].pair_key_idx = key_idx;
|
sram_key->sta_key[wsid].group_key_idx = key_idx;
|
sta_priv->has_hw_encrypt = vif_priv->has_hw_encrypt;
|
sta_priv->has_hw_decrypt = vif_priv->has_hw_decrypt;
|
sta_priv->need_sw_encrypt = vif_priv->need_sw_encrypt;
|
sta_priv->need_sw_decrypt = vif_priv->need_sw_decrypt;
|
}
|
#ifdef SSV6200_ECO
|
if (wsid != 0)
|
memcpy(sram_key->group_key[key_idx - 1].key, key, key_len);
|
sec_key_tbl += (0x10000 * wsid);
|
address = sec_key_tbl
|
+ ((key_idx - 1) * sizeof(struct ssv6xxx_hw_key));
|
pointer = (int *)&sram_key->group_key[key_idx - 1];
|
{
|
int i;
|
for (i = 0; i < (sizeof(struct ssv6xxx_hw_key)/4); i++)
|
SMAC_REG_WRITE(sc->sh, address+(i*4), *(pointer++));
|
}
|
#endif
|
address = sec_key_tbl
|
+ (3*sizeof(struct ssv6xxx_hw_key))
|
+ (wsid*sizeof(struct ssv6xxx_hw_sta_key));
|
pointer = (int *)&sram_key->sta_key[wsid];
|
SMAC_REG_WRITE(sc->sh, address, *(pointer));
|
}
|
static int hw_crypto_key_write_wep(struct ieee80211_hw *hw,
|
struct ieee80211_key_conf *key,
|
u8 algorithm,
|
struct ssv_vif_info *vif_info)
|
{
|
struct ssv_softc *sc = hw->priv;
|
struct ssv6xxx_hw_sec *sramKey = &vif_info->sramKey;
|
#ifndef SSV6200_ECO
|
int address = 0x00;
|
int *pointer=NULL;
|
u32 sec_key_tbl=sc->sh->hw_sec_key;
|
int i;
|
#endif
|
#ifdef FW_WSID_WATCH_LIST
|
#endif
|
if (key->keyidx == 0)
|
{
|
ssv6xxx_foreach_vif_sta(sc, vif_info, _set_wep_hw_crypto_pair_key, sramKey);
|
}
|
else
|
{
|
#ifndef SSV6200_ECO
|
address = sec_key_tbl
|
+ ((key->keyidx-1) * sizeof(struct ssv6xxx_hw_key));
|
pointer = (int *)&sramKey->group_key[key->keyidx-1];
|
for (i=0;i<(sizeof(struct ssv6xxx_hw_key)/4);i++)
|
SMAC_REG_WRITE(sc->sh, address+(i*4), *(pointer++));
|
#endif
|
ssv6xxx_foreach_vif_sta(sc, vif_info, _set_wep_hw_crypto_group_key, sramKey);
|
}
|
return 0;
|
}
|
static void _set_aes_tkip_hw_crypto_group_key (struct ssv_softc *sc,
|
struct ssv_vif_info *vif_info,
|
struct ssv_sta_info *sta_info,
|
void *param)
|
{
|
int wsid = sta_info->hw_wsid;
|
#ifdef SSV6200_ECO
|
int j;
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key[0];
|
#else
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key;
|
#endif
|
u32 sec_key_tbl = sec_key_tbl_base;
|
int address = 0;
|
int *pointer = 0;
|
struct ssv6xxx_hw_sec *sramKey = &(vif_info->sramKey);
|
int index = *(u8 *)param;
|
if (wsid == (-1))
|
return;
|
BUG_ON(index == 0);
|
sramKey->sta_key[wsid].group_key_idx = index;
|
#ifdef SSV6200_ECO
|
sec_key_tbl += (0x10000 * wsid);
|
address = sec_key_tbl
|
+ ((index-1) * sizeof(struct ssv6xxx_hw_key));
|
if (vif_info->vif_priv != NULL)
|
dev_info(sc->dev, "Write group key %d to VIF %d to %08X\n",
|
index, vif_info->vif_priv->vif_idx, address);
|
else
|
dev_err(sc->dev, "NULL VIF.\n");
|
pointer = (int *)&sramKey->group_key[index-1];
|
for (j = 0; j < (sizeof(struct ssv6xxx_hw_key)/4); j++)
|
SMAC_REG_WRITE(sc->sh, address+(j*4), *(pointer++));
|
#endif
|
address = sec_key_tbl
|
+ (3*sizeof(struct ssv6xxx_hw_key))
|
+ (wsid * sizeof(struct ssv6xxx_hw_sta_key));
|
pointer = (int *)&sramKey->sta_key[wsid];
|
SMAC_REG_WRITE(sc->sh, address, *(pointer));
|
#ifdef FW_WSID_WATCH_LIST
|
if (wsid >= SSV_NUM_HW_STA)
|
{
|
hw_update_watch_wsid(sc, sta_info->sta, sta_info,
|
wsid, SSV6XXX_WSID_SEC_GROUP, SSV6XXX_WSID_OPS_ENABLE_CAPS);
|
}
|
#endif
|
}
|
static int _write_pairwise_key_to_hw (struct ssv_softc *sc,
|
int index, u8 algorithm,
|
const u8 *key, int key_len,
|
struct ieee80211_key_conf *keyconf,
|
struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv)
|
{
|
int i;
|
struct ssv6xxx_hw_sec *sramKey;
|
int address = 0;
|
int *pointer = NULL;
|
#ifdef SSV6200_ECO
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key[0];
|
#else
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key;
|
#endif
|
u32 sec_key_tbl;
|
int wsid = (-1);
|
if (sta_priv == NULL)
|
{
|
dev_err(sc->dev, "Set pair-wise key with NULL STA.\n");
|
return -EOPNOTSUPP;
|
}
|
wsid = sta_priv->sta_info->hw_wsid;
|
if ((wsid < 0) || (wsid >= SSV_NUM_STA))
|
{
|
dev_err(sc->dev, "Set pair-wise key to invalid WSID %d.\n", wsid);
|
return -EOPNOTSUPP;
|
}
|
#if 0
|
sta_info->s_flags |= STA_FLAG_ENCRYPT;
|
#endif
|
dev_info(sc->dev, "Set STA %d's pair-wise key of %d bytes.\n", wsid, key_len);
|
sramKey = &(sc->vif_info[vif_priv->vif_idx].sramKey);
|
sramKey->sta_key[wsid].pair_key_idx = 0;
|
sramKey->sta_key[wsid].group_key_idx = vif_priv->group_key_idx;
|
memcpy(sramKey->sta_key[wsid].pair.key, key, key_len);
|
sec_key_tbl = sec_key_tbl_base;
|
#ifdef SSV6200_ECO
|
sec_key_tbl += (0x10000 * wsid);
|
#endif
|
address = sec_key_tbl
|
+ (3 * sizeof(struct ssv6xxx_hw_key))
|
+ wsid * sizeof(struct ssv6xxx_hw_sta_key);
|
pointer = (int *)&sramKey->sta_key[wsid];
|
for (i = 0; i < (sizeof(struct ssv6xxx_hw_sta_key)/4); i++)
|
SMAC_REG_WRITE(sc->sh, (address + (i*4)), *(pointer++));
|
#ifdef FW_WSID_WATCH_LIST
|
if (wsid >= SSV_NUM_HW_STA)
|
{
|
hw_update_watch_wsid(sc, sta_priv->sta_info->sta, sta_priv->sta_info,
|
sta_priv->sta_idx, SSV6XXX_WSID_SEC_PAIRWISE, SSV6XXX_WSID_OPS_ENABLE_CAPS);
|
}
|
#endif
|
return 0;
|
}
|
static int _write_group_key_to_hw (struct ssv_softc *sc,
|
int index, u8 algorithm,
|
const u8 *key, int key_len,
|
struct ieee80211_key_conf *keyconf,
|
struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv)
|
{
|
struct ssv6xxx_hw_sec *sramKey;
|
#ifndef SSV6200_ECO
|
u32 sec_key_tbl_base = sc->sh->hw_sec_key;
|
int address = 0;
|
int *pointer = NULL;
|
int i;
|
#endif
|
int wsid = sta_priv ? sta_priv->sta_info->hw_wsid : (-1);
|
int ret = 0;
|
if (vif_priv == NULL)
|
{
|
dev_err(sc->dev, "Setting group key to NULL VIF\n");
|
return -EOPNOTSUPP;
|
}
|
dev_info(sc->dev, "Setting VIF %d group key %d of length %d to WSID %d.\n",
|
vif_priv->vif_idx, index, key_len, wsid);
|
sramKey = &(sc->vif_info[vif_priv->vif_idx].sramKey);
|
vif_priv->group_key_idx = index;
|
if (sta_priv)
|
sta_priv->group_key_idx = index;
|
memcpy(sramKey->group_key[index-1].key, key, key_len);
|
#ifndef SSV6200_ECO
|
address = sec_key_tbl_base + ((index-1)*sizeof(struct ssv6xxx_hw_key));
|
pointer = (int *)&sramKey->group_key[index-1];
|
for (i = 0; i < (sizeof(struct ssv6xxx_hw_key)/4); i++)
|
SMAC_REG_WRITE(sc->sh, address+(i*4), *(pointer++));
|
#endif
|
WARN_ON(sc->vif_info[vif_priv->vif_idx].vif_priv == NULL);
|
ssv6xxx_foreach_vif_sta(sc, &sc->vif_info[vif_priv->vif_idx],
|
_set_aes_tkip_hw_crypto_group_key, &index);
|
ret = 0;
|
return ret;
|
}
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
|
static enum SSV_CIPHER_E _prepare_key (struct ieee80211_key_conf *key)
|
{
|
enum SSV_CIPHER_E cipher;
|
switch (key->cipher) {
|
case WLAN_CIPHER_SUITE_WEP40:
|
cipher = SSV_CIPHER_WEP40;
|
break;
|
case WLAN_CIPHER_SUITE_WEP104:
|
cipher = SSV_CIPHER_WEP104;
|
break;
|
case WLAN_CIPHER_SUITE_TKIP:
|
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
|
cipher = SSV_CIPHER_TKIP;
|
break;
|
case WLAN_CIPHER_SUITE_CCMP:
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
|
#else
|
key->flags |= (IEEE80211_KEY_FLAG_SW_MGMT_TX | IEEE80211_KEY_FLAG_RX_MGMT);
|
#endif
|
cipher = SSV_CIPHER_CCMP;
|
break;
|
#ifdef CONFIG_SSV_WAPI
|
case WLAN_CIPHER_SUITE_SMS4:
|
printk("[I] %s, algorithm = WLAN_CIPHER_SUITE_SMS4\n", __func__);
|
cipher = SSV_CIPHER_SMS4;
|
break;
|
#endif
|
default:
|
cipher = SSV_CIPHER_INVALID;
|
break;
|
}
|
return cipher;
|
}
|
#else
|
static enum SSV_CIPHER_E _prepare_key (struct ieee80211_key_conf *key)
|
{
|
enum SSV_CIPHER_E cipher;
|
switch (key->alg) {
|
case ALG_WEP:
|
if(key->keylen == 5)
|
cipher = SSV_CIPHER_WEP40;
|
else
|
cipher = SSV_CIPHER_WEP104;
|
break;
|
case ALG_TKIP:
|
cipher = SSV_CIPHER_TKIP;
|
key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
|
break;
|
case ALG_CCMP:
|
cipher = SSV_CIPHER_CCMP;
|
key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
|
break;
|
default:
|
cipher = SSV_CIPHER_INVALID;
|
break;
|
}
|
return cipher;
|
}
|
#endif
|
int _set_key_wep (struct ssv_softc *sc, struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv, enum SSV_CIPHER_E cipher,
|
struct ieee80211_key_conf *key)
|
{
|
int ret = 0;
|
struct ssv_vif_info *vif_info = &sc->vif_info[vif_priv->vif_idx];
|
struct ssv6xxx_hw_sec *sram_key = &vif_info->sramKey;
|
sram_key->sta_key[0].pair_key_idx = key->keyidx;
|
sram_key->sta_key[0].group_key_idx = key->keyidx;
|
*(u16 *)&sram_key->sta_key[0].reserve[0] = key->keylen;
|
printk(KERN_ERR "Set WEP %02X %02X %02X %02X %02X %02X %02X %02X... (%d %d)\n",
|
key->key[0], key->key[1], key->key[2], key->key[3],
|
key->key[4], key->key[5], key->key[6], key->key[7],
|
key->keyidx, key->keylen);
|
if (key->keyidx == 0)
|
{
|
memcpy(sram_key->sta_key[0].pair.key, key->key, key->keylen);
|
}
|
else
|
{
|
memcpy(sram_key->group_key[key->keyidx - 1].key, key->key, key->keylen);
|
}
|
#if 1
|
if (sc->sh->cfg.use_wpa2_only)
|
{
|
dev_warn(sc->dev, "Use WPA2 HW security mode only.\n");
|
}
|
#endif
|
if ( (sc->sh->cfg.use_wpa2_only == 0)
|
&& vif_priv->vif_idx == 0)
|
{
|
vif_priv->has_hw_decrypt = true;
|
vif_priv->has_hw_encrypt = true;
|
vif_priv->need_sw_decrypt = false;
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->use_mac80211_decrypt = false;
|
ssv6200_hw_set_pair_type(sc->sh, cipher);
|
ssv6200_hw_set_group_type(sc->sh, cipher);
|
hw_crypto_key_write_wep(sc->hw, key, cipher,
|
&sc->vif_info[vif_priv->vif_idx]);
|
}
|
else
|
#ifdef USE_LOCAL_WEP_CRYPTO
|
{
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &vif_priv->crypto_data);
|
vif_priv->has_hw_decrypt = false;
|
vif_priv->has_hw_encrypt = false;
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
if (crypto_data->ops && crypto_data->priv)
|
{
|
crypto_data->ops->deinit(crypto_data->priv);
|
crypto_data->ops = NULL;
|
crypto_data->priv = NULL;
|
}
|
crypto_data->ops = get_crypto_wep_ops();
|
crypto_data->priv = NULL;
|
if (crypto_data->ops)
|
crypto_data->priv = crypto_data->ops->init(key->keyidx);
|
if (crypto_data->priv)
|
{
|
crypto_data->ops->set_key(key->key, key->keylen, NULL, crypto_data->priv);
|
dev_err(sc->dev, "[Local Crypto]: VIF gets WEP crypto OK!\n");
|
dev_err(sc->dev, "[Local Crypto]: Use driver's encrypter.\n");
|
vif_priv->need_sw_decrypt = true;
|
vif_priv->need_sw_encrypt = true;
|
vif_priv->use_mac80211_decrypt = false;
|
}
|
else
|
{
|
dev_err(sc->dev, "[Local Crypto]: Failed to initialize driver's crypto!\n");
|
dev_info(sc->dev, "[Local Crypto]: Use MAC80211's encrypter.\n");
|
vif_priv->need_sw_decrypt = false;
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->use_mac80211_decrypt = true;
|
ret = -EOPNOTSUPP;
|
}
|
ssv6xxx_foreach_vif_sta(sc, vif_info, _set_wep_sw_crypto_key, NULL);
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
}
|
#else
|
{
|
vif_priv->has_hw_decrypt = false;
|
vif_priv->has_hw_encrypt = false;
|
vif_priv->need_sw_decrypt = false;
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->use_mac80211_decrypt = true;
|
ssv6xxx_foreach_vif_sta(sc, vif_info, _set_wep_sw_crypto_key, NULL);
|
ret = -EOPNOTSUPP;
|
}
|
#endif
|
vif_priv->pair_cipher = vif_priv->group_cipher = cipher;
|
vif_priv->is_security_valid = true;
|
return ret;
|
}
|
static int _set_pairwise_key_tkip_ccmp (struct ssv_softc *sc, struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv, enum SSV_CIPHER_E cipher,
|
struct ieee80211_key_conf *key)
|
{
|
int ret = 0;
|
const char *cipher_name = (cipher == SSV_CIPHER_CCMP) ? "CCMP" : "TKIP";
|
struct ssv_vif_info *vif_info = &sc->vif_info[vif_priv->vif_idx];
|
bool tdls_link = false, tdls_use_sw_cipher = false, tkip_use_sw_cipher = false;
|
bool use_non_ccmp = false;
|
int another_vif_idx = ((vif_priv->vif_idx + 1) % 2);
|
struct ssv_vif_priv_data *another_vif_priv =
|
(struct ssv_vif_priv_data *)sc->vif_info[another_vif_idx].vif_priv;
|
if (sta_priv == NULL)
|
{
|
dev_err(sc->dev, "Setting pairwise TKIP/CCMP key to NULL STA.\n");
|
return -EOPNOTSUPP;
|
}
|
#if 1
|
if (sc->sh->cfg.use_wpa2_only)
|
{
|
dev_warn(sc->dev, "Use WPA2 HW security mode only.\n");
|
}
|
#endif
|
if (vif_info->if_type == NL80211_IFTYPE_STATION){
|
struct ssv_sta_priv_data *first_sta_priv =
|
list_first_entry(&vif_priv->sta_list, struct ssv_sta_priv_data, list);
|
if (first_sta_priv->sta_idx != sta_priv->sta_idx){
|
tdls_link = true;
|
}
|
printk("first sta idx %d, current sta idx %d\n",first_sta_priv->sta_idx,sta_priv->sta_idx);
|
}
|
if ((tdls_link) && (vif_priv->pair_cipher != SSV_CIPHER_CCMP)
|
&& (sc->sh->cfg.use_wpa2_only == false)){
|
tdls_use_sw_cipher = true;
|
}
|
if (another_vif_priv != NULL){
|
if ((another_vif_priv->pair_cipher != SSV_CIPHER_CCMP)
|
&& (another_vif_priv->pair_cipher != SSV_CIPHER_NONE)){
|
use_non_ccmp = true;
|
printk("another vif use none ccmp\n");
|
}
|
}
|
if ((((tdls_link) && (vif_priv->pair_cipher != SSV_CIPHER_CCMP)) || (use_non_ccmp))
|
&& (sc->sh->cfg.use_wpa2_only == 1) && (cipher == SSV_CIPHER_CCMP)){
|
u32 val;
|
SMAC_REG_READ(sc->sh, ADR_RX_FLOW_DATA, &val);
|
if (((val >>4) & 0xF) != M_ENG_CPU){
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA, ((val & 0xf) | (M_ENG_CPU<<4)
|
| (val & 0xfffffff0) <<4));
|
dev_info(sc->dev, "orginal Rx_Flow %x , modified flow %x \n", val,
|
((val & 0xf) | (M_ENG_CPU<<4) | (val & 0xfffffff0) <<4));
|
}
|
}
|
if ((cipher == SSV_CIPHER_TKIP) && (sc->sh->cfg.use_wpa2_only == 1)){
|
tkip_use_sw_cipher = true;
|
}
|
if(tkip_use_sw_cipher == true)
|
printk ("%s==> tkip use sw cipher\n",__func__);
|
if ((((vif_priv->vif_idx == 0) && (tdls_use_sw_cipher == false) && (tkip_use_sw_cipher == false)))
|
|| ((cipher == SSV_CIPHER_CCMP) && (sc->sh->cfg.use_wpa2_only == 1)))
|
{
|
sta_priv->has_hw_decrypt = true;
|
sta_priv->need_sw_decrypt = false;
|
if ((cipher == SSV_CIPHER_TKIP)
|
|| ((!(sc->sh->cfg.hw_caps & SSV6200_HW_CAP_AMPDU_TX) ||
|
(sta_priv->sta_info->sta->ht_cap.ht_supported == false))
|
&& (vif_priv->force_sw_encrypt == false)))
|
{
|
dev_info(sc->dev, "STA %d uses HW encrypter for pairwise.\n", sta_priv->sta_idx);
|
sta_priv->has_hw_encrypt = true;
|
sta_priv->need_sw_encrypt = false;
|
sta_priv->use_mac80211_decrypt = false;
|
ret = 0;
|
}
|
else
|
{
|
sta_priv->has_hw_encrypt = false;
|
#ifdef USE_LOCAL_CCMP_CRYPTO
|
sta_priv->need_sw_encrypt = true;
|
sta_priv->use_mac80211_decrypt = false;
|
ret = 0;
|
#else
|
sta_priv->need_sw_encrypt = false;
|
sta_priv->use_mac80211_decrypt = true;
|
ret = -EOPNOTSUPP;
|
#endif
|
}
|
}
|
else
|
{
|
sta_priv->has_hw_encrypt = false;
|
sta_priv->has_hw_decrypt = false;
|
#ifdef USE_LOCAL_CCMP_CRYPTO
|
sta_priv->need_sw_encrypt = true;
|
sta_priv->need_sw_decrypt = true;
|
sta_priv->use_mac80211_decrypt = false;
|
ret = 0;
|
#else
|
dev_err(sc->dev, "STA %d MAC80211's %s cipher.\n", sta_priv->sta_idx, cipher_name);
|
sta_priv->need_sw_encrypt = false;
|
sta_priv->need_sw_decrypt = false;
|
sta_priv->use_mac80211_decrypt = true;
|
ret = -EOPNOTSUPP;
|
#endif
|
}
|
#ifdef USE_LOCAL_CRYPTO
|
if (sta_priv->need_sw_encrypt || sta_priv->need_sw_decrypt)
|
{
|
struct ssv_crypto_ops *temp_crypt;
|
void *temp_crypt_priv = NULL;
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &sta_priv->crypto_data);
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
if (crypto_data->ops && crypto_data->priv)
|
{
|
crypto_data->ops->deinit(crypto_data->priv);
|
}
|
temp_crypt = (cipher == SSV_CIPHER_CCMP)
|
#ifdef USE_LOCAL_CCMP_CRYPTO
|
? get_crypto_ccmp_ops()
|
#else
|
? NULL
|
#endif
|
#ifdef USE_LOCAL_TKIP_CRYPTO
|
: get_crypto_tkip_ops();
|
#else
|
: NULL;
|
#endif
|
if (temp_crypt)
|
temp_crypt_priv = temp_crypt->init(key->keyidx);
|
if (temp_crypt_priv)
|
{
|
dev_err(sc->dev, "Use driver's %s cipher OK!\n", cipher_name);
|
temp_crypt->set_key(key->key, key->keylen, NULL, temp_crypt_priv);
|
crypto_data->priv = temp_crypt_priv;
|
crypto_data->ops = temp_crypt;
|
}
|
else
|
{
|
dev_err(sc->dev, "Failed to initialize driver's %s crypto! "
|
"Use MAC80211's instead.\n", cipher_name);
|
sta_priv->need_sw_encrypt = false;
|
sta_priv->need_sw_decrypt = false;
|
sta_priv->use_mac80211_decrypt = true;
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->need_sw_decrypt = false;
|
vif_priv->use_mac80211_decrypt = true;
|
ret = -EOPNOTSUPP;
|
}
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
}
|
#endif
|
if (sta_priv->has_hw_encrypt || sta_priv->has_hw_decrypt)
|
{
|
ssv6200_hw_set_pair_type(sc->sh, cipher);
|
#if 0
|
ssv6200_hw_set_pair_type(sc->sh, SSV_CIPHER_NONE);
|
sta_priv->has_hw_encrypt = false;
|
sta_priv->has_hw_decrypt = false;
|
sta_priv->need_sw_encrypt = true;
|
sta_priv->need_sw_encrypt = true;
|
#endif
|
_write_pairwise_key_to_hw(sc, key->keyidx, cipher,
|
key->key, key->keylen, key,
|
vif_priv, sta_priv);
|
}
|
if ( (vif_priv->has_hw_encrypt || vif_priv->has_hw_decrypt)
|
&& (vif_priv->group_key_idx > 0))
|
{
|
_set_aes_tkip_hw_crypto_group_key(sc, &sc->vif_info[vif_priv->vif_idx],
|
sta_priv->sta_info, &vif_priv->group_key_idx);
|
}
|
return ret;
|
}
|
static int _set_group_key_tkip_ccmp (struct ssv_softc *sc, struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv, enum SSV_CIPHER_E cipher,
|
struct ieee80211_key_conf *key)
|
{
|
int ret = 0;
|
const char *cipher_name = (cipher == SSV_CIPHER_CCMP) ? "CCMP" : "TKIP";
|
bool tkip_use_sw_cipher = false;
|
vif_priv->group_cipher = cipher;
|
#if 1
|
if (sc->sh->cfg.use_wpa2_only)
|
{
|
dev_warn(sc->dev, "Use WPA2 HW security mode only.\n");
|
}
|
#endif
|
if ((cipher == SSV_CIPHER_TKIP) && (sc->sh->cfg.use_wpa2_only == 1)){
|
tkip_use_sw_cipher = true;
|
}
|
if (((vif_priv->vif_idx == 0) && (tkip_use_sw_cipher == false))
|
|| ((cipher == SSV_CIPHER_CCMP) && (sc->sh->cfg.use_wpa2_only == 1)))
|
{
|
dev_info(sc->dev, "VIF %d uses HW %s cipher for group.\n", vif_priv->vif_idx, cipher_name);
|
#ifdef USE_MAC80211_DECRYPT_BROADCAST
|
vif_priv->has_hw_decrypt = false;
|
ret = -EOPNOTSUPP;
|
#else
|
vif_priv->has_hw_decrypt = true;
|
#endif
|
vif_priv->has_hw_encrypt = true;
|
vif_priv->need_sw_decrypt = false;
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->use_mac80211_decrypt = false;
|
}
|
else
|
{
|
vif_priv->has_hw_decrypt = false;
|
vif_priv->has_hw_encrypt = false;
|
#ifdef USE_LOCAL_CRYPTO
|
vif_priv->need_sw_encrypt = true;
|
vif_priv->need_sw_decrypt = true;
|
vif_priv->use_mac80211_decrypt = false;
|
ret = 0;
|
#else
|
dev_err(sc->dev, "VIF %d uses MAC80211's %s cipher.\n", vif_priv->vif_idx, cipher_name);
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->use_mac80211_decrypt = true;
|
ret = -EOPNOTSUPP;
|
#endif
|
}
|
#ifdef USE_LOCAL_CRYPTO
|
if (vif_priv->need_sw_encrypt || vif_priv->need_sw_decrypt)
|
{
|
struct ssv_crypto_ops *temp_crypt = NULL;
|
void *temp_crypt_priv = NULL;
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &vif_priv->crypto_data);
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
if (crypto_data->ops && crypto_data->priv)
|
crypto_data->ops->deinit(crypto_data->priv);
|
crypto_data->priv = NULL;
|
temp_crypt = (cipher == SSV_CIPHER_CCMP)
|
#ifdef USE_LOCAL_CCMP_CRYPTO
|
? get_crypto_ccmp_ops()
|
#else
|
? NULL
|
#endif
|
#ifdef USE_LOCAL_TKIP_CRYPTO
|
: get_crypto_tkip_ops();
|
#else
|
: NULL;
|
#endif
|
if (temp_crypt)
|
temp_crypt_priv = temp_crypt->init(key->keyidx);
|
if (temp_crypt_priv)
|
{
|
dev_err(sc->dev, "VIF %d gets %s crypto OK! Use driver's crypto.\n",
|
vif_priv->vif_idx, cipher_name);
|
temp_crypt->set_key(key->key, key->keylen, NULL, temp_crypt_priv);
|
crypto_data->priv = temp_crypt_priv;
|
crypto_data->ops = temp_crypt;
|
}
|
else
|
{
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->need_sw_decrypt = false;
|
vif_priv->use_mac80211_decrypt = true;
|
dev_err(sc->dev, "VIF %d failed to initialize %s crypto!"
|
" Use MAC80211's instead.\n", vif_priv->vif_idx, cipher_name);
|
ret = -EOPNOTSUPP;
|
}
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
}
|
#endif
|
if (vif_priv->has_hw_encrypt || vif_priv->has_hw_decrypt)
|
{
|
#ifdef USE_MAC80211_DECRYPT_BROADCAST
|
ssv6200_hw_set_group_type(sc->sh, ME_NONE);
|
#else
|
ssv6200_hw_set_group_type(sc->sh, cipher);
|
#endif
|
key->hw_key_idx = key->keyidx;
|
_write_group_key_to_hw(sc, key->keyidx, cipher,
|
key->key, key->keylen, key,
|
vif_priv, sta_priv);
|
}
|
vif_priv->is_security_valid = true;
|
{
|
int another_vif_idx = ((vif_priv->vif_idx + 1) % 2);
|
struct ssv_vif_priv_data *another_vif_priv =
|
(struct ssv_vif_priv_data *)sc->vif_info[another_vif_idx].vif_priv;
|
if ( another_vif_priv != NULL){
|
if (((SSV6XXX_USE_SW_DECRYPT(vif_priv)
|
&& SSV6XXX_USE_HW_DECRYPT (another_vif_priv)))
|
|| ((SSV6XXX_USE_HW_DECRYPT (vif_priv)
|
&& (SSV6XXX_USE_SW_DECRYPT(another_vif_priv))))){
|
u32 val;
|
SMAC_REG_READ(sc->sh, ADR_RX_FLOW_DATA, &val);
|
if (((val >>4) & 0xF) != M_ENG_CPU){
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA, ((val & 0xf) | (M_ENG_CPU<<4)
|
| (val & 0xfffffff0) <<4));
|
dev_info(sc->dev, "orginal Rx_Flow %x , modified flow %x \n", val,
|
((val & 0xf) | (M_ENG_CPU<<4) | (val & 0xfffffff0) <<4));
|
} else {
|
printk(" doesn't need to change rx flow\n");
|
}
|
}
|
}
|
}
|
return ret;
|
}
|
static int _set_key_tkip_ccmp (struct ssv_softc *sc, struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv, enum SSV_CIPHER_E cipher,
|
struct ieee80211_key_conf *key)
|
{
|
if (key->keyidx == 0)
|
return _set_pairwise_key_tkip_ccmp(sc, vif_priv, sta_priv, cipher, key);
|
else
|
return _set_group_key_tkip_ccmp(sc, vif_priv, sta_priv, cipher, key);
|
}
|
#ifdef USE_LOCAL_SMS4_CRYPTO
|
static int _set_pairwise_key_sms4 (struct ssv_softc *sc, struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv, enum SSV_CIPHER_E cipher,
|
struct ieee80211_key_conf *key)
|
{
|
int ret = 0;
|
INIT_WRITE_CRYPTO_DATA(crypto_data, NULL);
|
if (sta_priv == NULL)
|
{
|
dev_err(sc->dev, "Setting pairwise SMS4 key to NULL STA.\n");
|
return -EOPNOTSUPP;
|
}
|
crypto_data = &sta_priv->crypto_data;
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
sta_priv->has_hw_encrypt = false;
|
sta_priv->has_hw_decrypt = false;
|
sta_priv->need_sw_encrypt = true;
|
sta_priv->need_sw_decrypt = true;
|
sta_priv->use_mac80211_decrypt = false;
|
crypto_data->ops = get_crypto_wpi_ops();
|
if (crypto_data->ops)
|
crypto_data->priv = crypto_data->ops->init(key->keyidx);
|
if (crypto_data->priv)
|
{
|
dev_err(sc->dev, "Use driver's SMS4 cipher OK!\n");
|
crypto_data->ops->set_key(key->key, key->keylen, NULL, crypto_data->priv);
|
}
|
else
|
{
|
dev_err(sc->dev, "Failed to initialize driver's SMS4 crypto!\n");
|
crypto_data->ops = NULL;
|
sta_priv->need_sw_encrypt = false;
|
sta_priv->need_sw_decrypt = false;
|
ret = -EOPNOTSUPP;
|
}
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
return ret;
|
}
|
static int _set_group_key_sms4 (struct ssv_softc *sc, struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv, enum SSV_CIPHER_E cipher,
|
struct ieee80211_key_conf *key)
|
{
|
int ret = 0;
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &vif_priv->crypto_data);
|
vif_priv->has_hw_encrypt = false;
|
vif_priv->has_hw_decrypt = false;
|
vif_priv->need_sw_encrypt = true;
|
vif_priv->need_sw_decrypt = true;
|
vif_priv->use_mac80211_decrypt = false;
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
crypto_data->ops = get_crypto_wpi_ops();
|
if (crypto_data->ops)
|
crypto_data->priv = crypto_data->ops->init(key->keyidx);
|
if (crypto_data->priv)
|
{
|
dev_err(sc->dev, "Use driver's SMS4 cipher OK!\n");
|
crypto_data->ops->set_key(key->key, key->keylen, NULL, crypto_data->priv);
|
vif_priv->is_security_valid = true;
|
}
|
else
|
{
|
dev_err(sc->dev, "Failed to initialize driver's SMS4 crypto!\n");
|
crypto_data->ops = NULL;
|
vif_priv->need_sw_encrypt = false;
|
vif_priv->need_sw_decrypt = false;
|
ret = -EOPNOTSUPP;
|
vif_priv->is_security_valid = false;
|
}
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
return ret;
|
}
|
static int _set_key_sms4 (struct ssv_softc *sc, struct ssv_vif_priv_data *vif_priv,
|
struct ssv_sta_priv_data *sta_priv, enum SSV_CIPHER_E cipher,
|
struct ieee80211_key_conf *key)
|
{
|
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
|
return _set_pairwise_key_sms4(sc, vif_priv, sta_priv, cipher, key);
|
else
|
return _set_group_key_sms4(sc, vif_priv, sta_priv, cipher, key);
|
}
|
#endif
|
static int ssv6200_set_key(struct ieee80211_hw *hw,
|
enum set_key_cmd cmd,
|
struct ieee80211_vif *vif,
|
struct ieee80211_sta *sta,
|
struct ieee80211_key_conf *key)
|
{
|
struct ssv_softc *sc = hw->priv;
|
int ret = 0;
|
enum SSV_CIPHER_E cipher = SSV_CIPHER_NONE;
|
int sta_idx = (-1);
|
struct ssv_sta_info *sta_info = NULL;
|
struct ssv_sta_priv_data *sta_priv = NULL;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)vif->drv_priv;
|
struct ssv_vif_info *vif_info = &sc->vif_info[vif_priv->vif_idx];
|
#if 0
|
int another_vif_idx = ((vif_priv->vif_idx + 1) % 2);
|
struct ssv_vif_priv_data *another_vif_priv = NULL;
|
u32 another_vif_pair_cipher = 0;
|
u32 another_vif_group_cipher = 0;
|
if (sc->vif_info[another_vif_idx].vif)
|
{
|
another_vif_priv = sc->vif_info[another_vif_idx].vif_priv;
|
another_vif_pair_cipher = another_vif_priv->pair_cipher;
|
another_vif_group_cipher = another_vif_priv->group_cipher;
|
}
|
#endif
|
if (sta)
|
{
|
sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
sta_idx = sta_priv->sta_idx;
|
sta_info = sta_priv->sta_info;
|
}
|
BUG_ON((cmd!=SET_KEY) && (cmd!=DISABLE_KEY));
|
if (!(sc->sh->cfg.hw_caps & SSV6200_HW_CAP_SECURITY))
|
{
|
dev_warn(sc->dev, "HW does not support security.\n");
|
return -EOPNOTSUPP;
|
}
|
#ifndef USE_LOCAL_CRYPTO
|
if (sta_info && (sta_info->hw_wsid == (-1)))
|
{
|
dev_warn(sc->dev, "Add STA without HW resource. Use MAC80211's solution.\n");
|
return -EOPNOTSUPP;
|
}
|
#endif
|
cipher = _prepare_key(key);
|
dev_err(sc->dev,"Set key VIF %d VIF type %d STA %d algorithm = %d, key->keyidx = %d, cmd = %d\n",
|
vif_priv->vif_idx, vif->type, sta_idx, cipher, key->keyidx, cmd);
|
if (cipher == SSV_CIPHER_INVALID)
|
{
|
dev_warn(sc->dev, "Unsupported cipher type.\n");
|
return -EOPNOTSUPP;
|
}
|
mutex_lock(&sc->mutex);
|
switch (cmd)
|
{
|
case SET_KEY:
|
{
|
#if 0
|
int i;
|
printk("================================SET KEY=======================================\n");
|
if (sta_info == NULL)
|
{
|
printk("NULL STA cmd[%d] alg[%d] keyidx[%d] ", cmd, algorithm, key->keyidx);
|
}
|
else
|
{
|
printk("STA WSID[%d] cmd[%d] alg[%d] keyidx[%d] ", sta_info->hw_wsid, cmd, algorithm, key->keyidx);
|
}
|
printk("SET_KEY index[%d] flags[0x%x] algorithm[%d] key->keylen[%d]\n",
|
key->keyidx, key->flags, algorithm, key->keylen);
|
for(i = 0; i < key->keylen; i++)
|
{
|
printk("[%02x]", key->key[i]);
|
}
|
printk("\n");
|
printk("===============================================================================\n");
|
#endif
|
switch (cipher)
|
{
|
case SSV_CIPHER_WEP40:
|
case SSV_CIPHER_WEP104:
|
ret = _set_key_wep(sc, vif_priv, sta_priv, cipher, key);
|
break;
|
case SSV_CIPHER_TKIP:
|
case SSV_CIPHER_CCMP:
|
ret = _set_key_tkip_ccmp(sc, vif_priv, sta_priv, cipher, key);
|
break;
|
#ifdef CONFIG_SSV_WAPI
|
case SSV_CIPHER_SMS4:
|
ret = _set_key_sms4(sc, vif_priv, sta_priv, cipher, key);
|
break;
|
#endif
|
default:
|
break;
|
}
|
if (sta){
|
struct ssv_sta_priv_data *first_sta_priv =
|
list_first_entry(&vif_priv->sta_list, struct ssv_sta_priv_data, list);
|
if (first_sta_priv->sta_idx == sta_priv->sta_idx){
|
vif_priv->pair_cipher = cipher;
|
}
|
if (SSV6200_USE_HW_WSID(sta_idx)){
|
if (SSV6XXX_USE_SW_DECRYPT(sta_priv)){
|
u32 cipher_setting;
|
cipher_setting = ssv6200_hw_get_pair_type(sc->sh);
|
if (cipher_setting != ME_NONE) {
|
u32 val;
|
SMAC_REG_READ(sc->sh, ADR_RX_FLOW_DATA, &val);
|
if (((val >>4) & 0xF) != M_ENG_CPU){
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA,
|
((val & 0xf) | (M_ENG_CPU<<4)
|
| (val & 0xfffffff0) <<4));
|
dev_info(sc->dev, "orginal Rx_Flow %x , modified flow %x \n",
|
val, ((val & 0xf) | (M_ENG_CPU<<4) | (val & 0xfffffff0) <<4));
|
} else {
|
printk(" doesn't need to change rx flow\n");
|
}
|
}
|
}
|
if (sta_priv->has_hw_decrypt){
|
hw_update_watch_wsid(sc, sta, sta_info, sta_idx,
|
SSV6XXX_WSID_SEC_HW, SSV6XXX_WSID_OPS_HWWSID_PAIRWISE_SET_TYPE);
|
printk("set hw wsid %d cipher mode to HW cipher for pairwise key\n", sta_idx);
|
}
|
}
|
} else {
|
if (vif_info->if_type == NL80211_IFTYPE_STATION){
|
struct ssv_sta_priv_data *first_sta_priv =
|
list_first_entry(&vif_priv->sta_list, struct ssv_sta_priv_data, list);
|
if (SSV6200_USE_HW_WSID(first_sta_priv->sta_idx)){
|
if (vif_priv->has_hw_decrypt){
|
hw_update_watch_wsid(sc, sta, sta_info, first_sta_priv->sta_idx,
|
SSV6XXX_WSID_SEC_HW, SSV6XXX_WSID_OPS_HWWSID_GROUP_SET_TYPE);
|
printk("set hw wsid %d cipher mode to HW cipher for group key\n", first_sta_priv->sta_idx);
|
}
|
}
|
}
|
}
|
}
|
break;
|
case DISABLE_KEY:
|
{
|
int another_vif_idx = ((vif_priv->vif_idx + 1) % 2);
|
struct ssv_vif_priv_data *another_vif_priv =
|
(struct ssv_vif_priv_data *)sc->vif_info[another_vif_idx].vif_priv;
|
#if 0
|
printk("================================DEL KEY=======================================\n");
|
if(sta_info == NULL){
|
printk("NULL STA cmd[%d] alg[%d] keyidx[%d] ", cmd, cipher, key->keyidx);
|
}
|
else{
|
printk("STA WSID[%d] cmd[%d] alg[%d] keyidx[%d] ", sta_info->hw_wsid, cmd, cipher, key->keyidx);
|
}
|
printk("DISABLE_KEY index[%d]\n",key->keyidx);
|
printk("==============================================================================\n");
|
#endif
|
#if 0
|
if(key->keyidx == 0)
|
{
|
sta_info->ampdu_ccmp_encrypt = false;
|
}
|
#endif
|
if (another_vif_priv != NULL) {
|
struct ssv_vif_info *vif_info = &sc->vif_info[vif_priv->vif_idx];
|
if (vif_info->if_type != NL80211_IFTYPE_AP) {
|
if ((SSV6XXX_USE_SW_DECRYPT(vif_priv)
|
&& SSV6XXX_USE_HW_DECRYPT (another_vif_priv))
|
|| (SSV6XXX_USE_SW_DECRYPT(another_vif_priv)
|
&& SSV6XXX_USE_HW_DECRYPT (vif_priv))){
|
#ifdef CONFIG_SSV_HW_ENCRYPT_SW_DECRYPT
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA,
|
M_ENG_MACRX|(M_ENG_HWHCI<<4));
|
#else
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA,
|
M_ENG_MACRX|(M_ENG_ENCRYPT_SEC<<4)|(M_ENG_HWHCI<<8));
|
#endif
|
printk("redirect Rx flow for disconnect\n");
|
}
|
}else {
|
if (sta == NULL) {
|
if (SSV6XXX_USE_SW_DECRYPT(another_vif_priv)
|
&& SSV6XXX_USE_HW_DECRYPT (vif_priv)){
|
#ifdef CONFIG_SSV_HW_ENCRYPT_SW_DECRYPT
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA,
|
M_ENG_MACRX|(M_ENG_HWHCI<<4));
|
#else
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA,
|
M_ENG_MACRX|(M_ENG_ENCRYPT_SEC<<4)|(M_ENG_HWHCI<<8));
|
#endif
|
printk("redirect Rx flow for disconnect\n");
|
}
|
}
|
}
|
}
|
if ( sta == NULL){
|
vif_priv->group_cipher = ME_NONE;
|
if ((another_vif_priv == NULL)
|
|| ((another_vif_priv != NULL) && (!SSV6XXX_USE_HW_DECRYPT(another_vif_priv)))){
|
#ifdef SSV_SUPPORT_HAL
|
HAL_SET_GROUP_CIPHER_TYPE(sc->sh, ME_NONE);
|
#else
|
ssv6200_hw_set_group_type(sc->sh, ME_NONE);
|
#endif
|
}
|
} else {
|
struct ssv_vif_info *vif_info = &sc->vif_info[vif_priv->vif_idx];
|
if ((vif_info->if_type != NL80211_IFTYPE_AP) && (another_vif_priv == NULL)){
|
struct ssv_sta_priv_data *first_sta_priv =
|
list_first_entry(&vif_priv->sta_list, struct ssv_sta_priv_data, list);
|
if (sta_priv == first_sta_priv){
|
#ifdef SSV_SUPPORT_HAL
|
HAL_SET_PAIRWISE_CIPHER_TYPE(sc->sh, ME_NONE, sta_info->hw_wsid);
|
#else
|
ssv6200_hw_set_pair_type(sc->sh, ME_NONE);
|
#endif
|
}
|
}
|
vif_priv->pair_cipher = ME_NONE;
|
}
|
if ((cipher == ME_TKIP) || (cipher == ME_CCMP))
|
{
|
printk(KERN_ERR "Clear key %d VIF %d, STA %d\n",
|
key->keyidx, (vif != NULL), (sta != NULL));
|
hw_crypto_key_clear(hw, key->keyidx, key, vif_priv, sta_priv);
|
}
|
{
|
if ((key->keyidx == 0) && (sta_priv != NULL))
|
{
|
#ifdef USE_LOCAL_CRYPTO
|
unsigned long flags;
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &sta_priv->crypto_data);
|
#endif
|
sta_priv->has_hw_decrypt = false;
|
sta_priv->has_hw_encrypt = false;
|
sta_priv->need_sw_encrypt = false;
|
sta_priv->use_mac80211_decrypt = false;
|
#ifdef USE_LOCAL_CRYPTO
|
if (crypto_data->ops && crypto_data->priv)
|
{
|
u32 sta_addr0_3 = *(u32 *)&sta->addr[0];
|
u32 sta_addr4_5 = (u32)*(u16 *)&sta->addr[4];
|
u32 removed_skb_num;
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
crypto_data->ops->deinit(crypto_data->priv);
|
crypto_data->priv = NULL;
|
crypto_data->ops = NULL;
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
removed_skb_num = _remove_sta_skb_from_q(sc, &sc->preprocess_q,
|
sta_addr0_3, sta_addr4_5);
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
dev_err(sc->dev, "Clean up %d skb for STA %pM.\n", removed_skb_num, sta->addr);
|
}
|
#endif
|
}
|
#ifdef USE_LOCAL_CRYPTO
|
else
|
{
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &vif_priv->crypto_data);
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
if (crypto_data->ops && crypto_data->priv)
|
crypto_data->ops->deinit(crypto_data->priv);
|
crypto_data->priv = NULL;
|
crypto_data->ops = NULL;
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
}
|
#endif
|
if ((vif_priv->is_security_valid) && (key->keyidx != 0))
|
{
|
#if 0
|
vif_priv->has_hw_decrypt = false;
|
vif_priv->has_hw_encrypt = false;
|
vif_priv->need_sw_encrypt = false;
|
#endif
|
vif_priv->is_security_valid = false;
|
}
|
}
|
ret = 0;
|
}
|
break;
|
default:
|
ret = -EINVAL;
|
}
|
mutex_unlock(&sc->mutex);
|
if(sta_priv != NULL)
|
{
|
printk("sta: hw_en:%d, sw_en:%d, hw_de:%d, sw_de:%d,\n",
|
(sta_priv->has_hw_encrypt==true),(sta_priv->need_sw_encrypt==true),
|
(sta_priv->has_hw_decrypt==true),(sta_priv->need_sw_decrypt==true));
|
}
|
if(vif_priv)
|
{
|
printk("vif: hw_en:%d, sw_en:%d, hw_de:%d, sw_de:%d, use_mac80211 %d, valid:%d\n",
|
(vif_priv->has_hw_encrypt==true),(vif_priv->need_sw_encrypt==true),
|
(vif_priv->has_hw_decrypt==true),(vif_priv->need_sw_decrypt==true),
|
(vif_priv->use_mac80211_decrypt == true), (vif_priv->is_security_valid==true));
|
}
|
#ifdef CONFIG_SSV_SW_ENCRYPT_HW_DECRYPT
|
ret = -EOPNOTSUPP;
|
#endif
|
#ifndef USE_LOCAL_CRYPTO
|
if ( vif_priv->force_sw_encrypt
|
|| (sta_info && (sta_info->hw_wsid != 1) && (sta_info->hw_wsid != 0)))
|
{
|
if (vif_priv->force_sw_encrypt == false)
|
vif_priv->force_sw_encrypt = true;
|
ret = -EOPNOTSUPP;
|
}
|
#endif
|
printk(KERN_ERR "SET KEY %d\n", ret);
|
return ret;
|
}
|
u32 _process_tx_done (struct ssv_softc *sc)
|
{
|
struct ieee80211_tx_info *tx_info;
|
struct sk_buff *skb;
|
while ((skb = skb_dequeue(&sc->tx_done_q)))
|
{
|
struct ssv6200_tx_desc *tx_desc;
|
tx_info = IEEE80211_SKB_CB(skb);
|
tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
if(tx_desc->c_type > M2_TXREQ)
|
{
|
ssv_skb_free(skb);
|
printk(KERN_INFO "free cmd skb!\n");
|
continue;
|
}
|
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
|
{
|
ssv6200_ampdu_release_skb(skb, sc->hw);
|
continue;
|
}
|
skb_pull(skb, SSV6XXX_TX_DESC_LEN);
|
ieee80211_tx_info_clear_status(tx_info);
|
tx_info->flags |= IEEE80211_TX_STAT_ACK;
|
tx_info->status.ack_signal = 100;
|
#ifdef REPORT_TX_DONE_IN_IRQ
|
ieee80211_tx_status_irqsafe(sc->hw, skb);
|
#else
|
ieee80211_tx_status(sc->hw, skb);
|
if (skb_queue_len(&sc->rx_skb_q))
|
break;
|
#endif
|
}
|
return skb_queue_len(&sc->tx_done_q);
|
}
|
#ifdef REPORT_TX_DONE_IN_IRQ
|
void ssv6xxx_tx_cb(struct sk_buff_head *skb_head, void *args)
|
{
|
struct ssv_softc *sc=(struct ssv_softc *)args;
|
_process_tx_done*(sc);
|
}
|
#else
|
void ssv6xxx_tx_cb(struct sk_buff_head *skb_head, void *args)
|
{
|
struct ssv_softc *sc=(struct ssv_softc *)args;
|
struct sk_buff *skb;
|
while ((skb=skb_dequeue(skb_head)))
|
{
|
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
|
struct ssv6200_tx_desc *tx_desc;
|
tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
if(tx_desc->c_type > M2_TXREQ)
|
{
|
ssv_skb_free(skb);
|
printk(KERN_INFO "free cmd skb!\n");
|
continue;
|
}
|
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
|
ssv6xxx_ampdu_sent(sc->hw, skb);
|
skb_queue_tail(&sc->tx_done_q, skb);
|
}
|
wake_up_interruptible(&sc->rx_wait_q);
|
}
|
#endif
|
#ifdef RATE_CONTROL_REALTIME_UPDATA
|
void ssv6xxx_tx_rate_update(struct sk_buff *skb, void *args)
|
{
|
struct ieee80211_hdr *hdr;
|
struct ssv_softc *sc = args;
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
struct ssv6200_tx_desc *tx_desc;
|
struct ssv_rate_info ssv_rate;
|
u32 nav=0;
|
int ret = 0;
|
tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
if(tx_desc->c_type > M2_TXREQ)
|
return;
|
if (!(info->flags & IEEE80211_TX_CTL_AMPDU))
|
{
|
hdr = (struct ieee80211_hdr *)(skb->data+SSV6XXX_TX_DESC_LEN);
|
if ( ( ieee80211_is_data_qos(hdr->frame_control)
|
|| ieee80211_is_data(hdr->frame_control))
|
&& (tx_desc->wsid < SSV_RC_MAX_HARDWARE_SUPPORT))
|
{
|
ret = ssv6xxx_rc_hw_rate_update_check(skb, sc, tx_desc->do_rts_cts);
|
if (ret & RC_FIRMWARE_REPORT_FLAG)
|
{
|
{
|
tx_desc->RSVD_0 = SSV6XXX_RC_REPORT;
|
tx_desc->tx_report = 1;
|
}
|
ret &= 0xf;
|
}
|
if(ret)
|
{
|
ssv6xxx_rc_hw_rate_idx(sc, info, &ssv_rate);
|
tx_desc->crate_idx = ssv_rate.crate_hw_idx;
|
tx_desc->drate_idx = ssv_rate.drate_hw_idx;
|
nav = ssv6xxx_set_frame_duration(info, &ssv_rate, skb->len+FCS_LEN, tx_desc, NULL, NULL);
|
if (tx_desc->tx_burst == 0)
|
{
|
if (tx_desc->ack_policy != 0x01)
|
hdr->duration_id = nav;
|
}
|
}
|
}
|
}
|
else
|
{
|
}
|
return;
|
}
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
|
#define RTS_CTS_PROTECT(_flg) \
|
((_flg)&IEEE80211_TX_RC_USE_RTS_CTS)? 1: \
|
((_flg)&IEEE80211_TX_RC_USE_CTS_PROTECT)? 2: 0
|
#endif
|
void ssv6xxx_update_txinfo (struct ssv_softc *sc, struct sk_buff *skb)
|
{
|
struct ieee80211_hdr *hdr;
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
struct ieee80211_sta *sta;
|
struct ssv_sta_info *sta_info = NULL;
|
struct ssv_sta_priv_data *ssv_sta_priv = NULL;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)info->control.vif->drv_priv;
|
struct ssv6200_tx_desc *tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
struct ieee80211_tx_rate *tx_drate;
|
struct ssv_rate_info ssv_rate;
|
int ac, hw_txqid;
|
u32 nav=0;
|
if (info->flags & IEEE80211_TX_CTL_AMPDU)
|
{
|
struct ampdu_hdr_st *ampdu_hdr = (struct ampdu_hdr_st *)skb->head;
|
sta = ampdu_hdr->ampdu_tid->sta;
|
hdr = (struct ieee80211_hdr *)(skb->data + TXPB_OFFSET + AMPDU_DELIMITER_LEN);
|
}
|
else
|
{
|
struct SKB_info_st *skb_info = (struct SKB_info_st *)skb->head;
|
sta = skb_info->sta;
|
hdr = (struct ieee80211_hdr *)(skb->data + TXPB_OFFSET);
|
}
|
if (sta)
|
{
|
ssv_sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
sta_info = ssv_sta_priv->sta_info;
|
}
|
if ((!sc->bq4_dtim) &&
|
(ieee80211_is_mgmt(hdr->frame_control) ||
|
ieee80211_is_nullfunc(hdr->frame_control) ||
|
ieee80211_is_qos_nullfunc(hdr->frame_control))) {
|
ac = 4;
|
hw_txqid = 4;
|
}
|
else if((sc->bq4_dtim) &&
|
info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM){
|
hw_txqid = 4;
|
ac = 4;
|
}
|
else{
|
ac = skb_get_queue_mapping(skb);
|
hw_txqid = sc->tx.hw_txqid[ac];
|
}
|
tx_drate = &info->control.rates[0];
|
ssv6xxx_rc_hw_rate_idx(sc, info, &ssv_rate);
|
tx_desc->len = skb->len;
|
tx_desc->c_type = M2_TXREQ;
|
tx_desc->f80211 = 1;
|
tx_desc->qos = (ieee80211_is_data_qos(hdr->frame_control))? 1: 0;
|
if (tx_drate->flags & IEEE80211_TX_RC_MCS) {
|
if (ieee80211_is_mgmt(hdr->frame_control) &&
|
ieee80211_has_order(hdr->frame_control))
|
tx_desc->ht = 1;
|
}
|
tx_desc->use_4addr = (ieee80211_has_a4(hdr->frame_control))? 1: 0;
|
tx_desc->more_data = (ieee80211_has_morefrags(hdr->frame_control))? 1: 0;
|
tx_desc->stype_b5b4 = (cpu_to_le16(hdr->frame_control)>>4)&0x3;
|
tx_desc->frag = (tx_desc->more_data||(hdr->seq_ctrl&0xf))? 1: 0;
|
tx_desc->unicast = (is_multicast_ether_addr(hdr->addr1)) ? 0: 1;
|
tx_desc->tx_burst = (tx_desc->frag)? 1: 0;
|
tx_desc->wsid = (!sta_info || (sta_info->hw_wsid < 0)) ? 0x0F : sta_info->hw_wsid;
|
tx_desc->txq_idx = hw_txqid;
|
tx_desc->hdr_offset = TXPB_OFFSET;
|
tx_desc->hdr_len = ssv6xxx_frame_hdrlen(hdr, tx_desc->ht);
|
tx_desc->payload_offset = tx_desc->hdr_offset + tx_desc->hdr_len;
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0)
|
if(info->control.use_rts)
|
tx_desc->do_rts_cts = IEEE80211_TX_RC_USE_RTS_CTS;
|
else if(info->control.use_cts_prot)
|
tx_desc->do_rts_cts = IEEE80211_TX_RC_USE_CTS_PROTECT;
|
#else
|
tx_desc->do_rts_cts = RTS_CTS_PROTECT(tx_drate->flags);
|
#endif
|
if(tx_desc->do_rts_cts == IEEE80211_TX_RC_USE_CTS_PROTECT)
|
tx_desc->do_rts_cts = IEEE80211_TX_RC_USE_RTS_CTS;
|
if(tx_desc->do_rts_cts == IEEE80211_TX_RC_USE_CTS_PROTECT)
|
{
|
tx_desc->crate_idx = 0;
|
}
|
else
|
tx_desc->crate_idx = ssv_rate.crate_hw_idx;
|
tx_desc->drate_idx = ssv_rate.drate_hw_idx;
|
if (tx_desc->unicast == 0)
|
tx_desc->ack_policy = 1;
|
else if (tx_desc->qos == 1)
|
tx_desc->ack_policy = (*ieee80211_get_qos_ctl(hdr)&0x60)>>5;
|
else if(ieee80211_is_ctl(hdr->frame_control))
|
tx_desc->ack_policy = 1;
|
tx_desc->security = 0;
|
tx_desc->fCmdIdx = 0;
|
tx_desc->fCmd = (hw_txqid+M_ENG_TX_EDCA0);
|
if (info->flags & IEEE80211_TX_CTL_AMPDU)
|
{
|
#ifdef AMPDU_HAS_LEADING_FRAME
|
tx_desc->fCmd = (tx_desc->fCmd << 4) | M_ENG_CPU;
|
#else
|
tx_desc->RSVD_1 = 1;
|
#endif
|
tx_desc->aggregation = 1;
|
tx_desc->ack_policy = 0x01;
|
if ( (tx_desc->do_rts_cts == 0)
|
&& ( (sc->hw->wiphy->rts_threshold == (-1))
|
|| ((skb->len - sc->sh->tx_desc_len) > sc->hw->wiphy->rts_threshold)))
|
{
|
tx_drate->flags |= IEEE80211_TX_RC_USE_RTS_CTS;
|
tx_desc->do_rts_cts = 1;
|
}
|
}
|
if ( ieee80211_has_protected(hdr->frame_control)
|
&& ( ieee80211_is_data_qos(hdr->frame_control)
|
|| ieee80211_is_data(hdr->frame_control)))
|
{
|
if ( (tx_desc->unicast && ssv_sta_priv && ssv_sta_priv->has_hw_encrypt)
|
|| (!tx_desc->unicast && vif_priv && vif_priv->has_hw_encrypt))
|
{
|
if (!tx_desc->unicast && !list_empty(&vif_priv->sta_list))
|
{
|
struct ssv_sta_priv_data *one_sta_priv;
|
int hw_wsid;
|
one_sta_priv = list_first_entry(&vif_priv->sta_list, struct ssv_sta_priv_data, list);
|
hw_wsid = one_sta_priv->sta_info->hw_wsid;
|
if (hw_wsid != (-1))
|
{
|
tx_desc->wsid = hw_wsid;
|
}
|
#if 0
|
printk(KERN_ERR "HW ENC %d %02X:%02X:%02X:%02X:%02X:%02X\n",
|
tx_desc->wsid,
|
hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
|
hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
|
_ssv6xxx_hexdump("M ", (const u8 *)skb->data, (skb->len > 128) ? 128 : skb->len);
|
tx_desc->fCmd = (tx_desc->fCmd << 4) | M_ENG_CPU;
|
#endif
|
}
|
tx_desc->fCmd = (tx_desc->fCmd << 4) | M_ENG_ENCRYPT;
|
#if 0
|
if (dump_count++ < 10)
|
{
|
printk(KERN_ERR "HW ENC %d %02X:%02X:%02X:%02X:%02X:%02X\n",
|
tx_desc->wsid,
|
hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
|
hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
|
tx_desc->tx_report = 1;
|
_ssv6xxx_hexdump("M ", (const u8 *)skb->data, (skb->len > 128) ? 128 : skb->len);
|
}
|
#endif
|
}
|
else if (ssv_sta_priv->need_sw_encrypt)
|
{
|
}
|
else
|
{
|
}
|
}
|
else
|
{
|
}
|
tx_desc->fCmd = (tx_desc->fCmd << 4) | M_ENG_HWHCI;
|
#if 0
|
if ( ieee80211_is_data_qos(hdr->frame_control)
|
|| ieee80211_is_data(hdr->frame_control))
|
#endif
|
#if 0
|
if (ieee80211_is_probe_resp(hdr->frame_control))
|
{
|
{
|
printk(KERN_ERR "Probe Resp %d %02X:%02X:%02X:%02X:%02X:%02X\n",
|
tx_desc->wsid,
|
hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
|
hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
|
_ssv6xxx_hexdump("M ", (const u8 *)skb->data, (skb->len > 128) ? 128 : skb->len);
|
}
|
}
|
#endif
|
#if 0
|
if ( (sc->sh->cfg.hw_caps & SSV6200_HW_CAP_SECURITY)
|
&& (sc->algorithm != ME_NONE)) {
|
if ( (tx_desc->unicast == 0)
|
|| (sc->algorithm == ME_WEP104 || sc->algorithm == ME_WEP40))
|
{
|
tx_desc->wsid = 0;
|
}
|
}
|
#endif
|
#if 0
|
if (tx_desc->aggregation) {
|
tx_desc->do_rts_cts = 0;
|
tx_desc->fCmd = M_ENG_HWHCI|((hw_txqid+M_ENG_TX_EDCA0)<<4);
|
tx_desc->ack_policy = 0x01;
|
}
|
#endif
|
if (tx_desc->aggregation == 1)
|
{
|
struct ampdu_hdr_st *ampdu_hdr = (struct ampdu_hdr_st *)skb->head;
|
memcpy(&tx_desc->rc_params[0], ampdu_hdr->rates, sizeof(tx_desc->rc_params));
|
nav = ssv6xxx_set_frame_duration(info, &ssv_rate, (skb->len+FCS_LEN), tx_desc, &tx_desc->rc_params[0], sc);
|
#ifdef FW_RC_RETRY_DEBUG
|
{
|
printk("[FW_RC]:param[0]: drate =%d, count =%d, crate=%d, dl_length =%d, frame_consume_time =%d, rts_cts_nav=%d\n",
|
tx_desc->rc_params[0].drate,tx_desc->rc_params[0].count,tx_desc->rc_params[0].crate,
|
tx_desc->rc_params[0].dl_length, tx_desc->rc_params[0].frame_consume_time, tx_desc->rc_params[0].rts_cts_nav);
|
printk("[FW_RC]:param[1]: drate =%d, count =%d, crate=%d, dl_length =%d, frame_consume_time =%d, rts_cts_nav=%d\n",
|
tx_desc->rc_params[1].drate,tx_desc->rc_params[1].count,tx_desc->rc_params[1].crate,
|
tx_desc->rc_params[1].dl_length, tx_desc->rc_params[1].frame_consume_time, tx_desc->rc_params[1].rts_cts_nav);
|
printk("[FW_RC]:param[2]: drate =%d, count =%d, crate=%d, dl_length =%d, frame_consume_time =%d, rts_cts_nav=%d\n",
|
tx_desc->rc_params[2].drate,tx_desc->rc_params[2].count,tx_desc->rc_params[2].crate,
|
tx_desc->rc_params[2].dl_length, tx_desc->rc_params[2].frame_consume_time, tx_desc->rc_params[2].rts_cts_nav);
|
}
|
#endif
|
}
|
else
|
{
|
nav = ssv6xxx_set_frame_duration(info, &ssv_rate, (skb->len+FCS_LEN), tx_desc, NULL, NULL);
|
}
|
if ( (tx_desc->aggregation==0)) {
|
if (tx_desc->tx_burst == 0) {
|
if (tx_desc->ack_policy != 0x01)
|
hdr->duration_id = nav;
|
}
|
else {
|
}
|
}
|
}
|
void ssv6xxx_add_txinfo (struct ssv_softc *sc, struct sk_buff *skb)
|
{
|
struct ssv6200_tx_desc *tx_desc;
|
skb_push(skb, sc->sh->tx_desc_len);
|
tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
memset((void *)tx_desc, 0, sc->sh->tx_desc_len);
|
ssv6xxx_update_txinfo(sc, skb);
|
}
|
int ssv6xxx_get_real_index(struct ssv_softc *sc, struct sk_buff *skb)
|
{
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
struct ieee80211_tx_rate *tx_drate;
|
struct ssv_rate_info ssv_rate;
|
tx_drate = &info->control.rates[0];
|
ssv6xxx_rc_hw_rate_idx(sc, info, &ssv_rate);
|
return ssv_rate.drate_hw_idx;
|
}
|
static void _ssv6xxx_tx (struct ieee80211_hw *hw, struct sk_buff *skb)
|
{
|
struct ssv_softc *sc = hw->priv;
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
struct ieee80211_vif *vif = info->control.vif;
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
#ifdef USE_LOCAL_CRYPTO
|
struct SKB_info_st *skb_info = (struct SKB_info_st *)skb->head;
|
struct ieee80211_sta *sta = skb_info->sta;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)info->control.vif->drv_priv;
|
struct ssv_sta_priv_data *ssv_sta_priv = sta
|
? (struct ssv_sta_priv_data *)sta->drv_priv
|
: NULL;
|
#endif
|
struct ssv6200_tx_desc *tx_desc;
|
int ret;
|
unsigned long flags;
|
bool send_hci=false;
|
#ifdef CONFIG_SSV_SUPPORT_ANDROID
|
if(sc->ps_status == PWRSV_PREPARE)
|
{
|
if(ieee80211_is_data(hdr->frame_control))
|
ssv_wake_timeout(sc, 1);
|
}
|
#endif
|
do {
|
if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
|
if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
|
sc->tx.seq_no += 0x10;
|
hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
|
hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
|
}
|
if (sc->dbg_tx_frame) {
|
printk("================================================\n");
|
_ssv6xxx_hexdump("TX frame", (const u8 *)skb->data, skb->len);
|
}
|
#if 0
|
if ( (skb->protocol == cpu_to_be16(ETH_P_PAE))
|
&& ieee80211_is_data_qos(hdr->frame_control))
|
{
|
printk(KERN_ERR "EAPOL frame is %d\n", skb_get_queue_mapping(skb));
|
}
|
#endif
|
#ifdef USE_LOCAL_CRYPTO
|
if (
|
#ifdef MULTI_THREAD_ENCRYPT
|
(skb_info->crypt_st == PKT_CRYPT_ST_NOT_SUPPORT) &&
|
#endif
|
ieee80211_has_protected(hdr->frame_control)
|
&& ( ieee80211_is_data_qos(hdr->frame_control)
|
|| ieee80211_is_data(hdr->frame_control)))
|
{
|
bool unicast = !is_broadcast_ether_addr(hdr->addr1);
|
if ( ( unicast
|
&& (ssv_sta_priv != NULL)
|
&& ssv_sta_priv->need_sw_encrypt)
|
|| ( !unicast
|
&& vif_priv->is_security_valid
|
&& vif_priv->need_sw_encrypt))
|
{
|
if(ssv6xxx_skb_encrypt(skb, sc) == (-1))
|
{
|
ssv_skb_free(skb);
|
break;
|
}
|
}
|
}
|
else
|
{
|
}
|
#endif
|
if (info->flags & IEEE80211_TX_CTL_AMPDU)
|
{
|
if(ssv6xxx_get_real_index(sc, skb) < SSV62XX_RATE_MCS_INDEX)
|
{
|
info->flags &= (~IEEE80211_TX_CTL_AMPDU);
|
goto tx_mpdu;
|
}
|
#if 0
|
u8 tidno;
|
struct ieee80211_sta *sta = info->control.sta;
|
struct ssv_sta_priv_data *ssv_sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
|
if((ssv_sta_priv->ampdu_tid[tidno].state == AMPDU_STATE_OPERATION) &&
|
(sc->ampdu_rekey_pause < AMPDU_REKEY_PAUSE_ONGOING))
|
#endif
|
if (ssv6200_ampdu_tx_handler(hw, skb))
|
{
|
break;
|
}
|
else
|
{
|
info->flags &= (~IEEE80211_TX_CTL_AMPDU);
|
}
|
}
|
tx_mpdu:
|
ssv6xxx_add_txinfo(sc, skb);
|
if( vif &&
|
vif->type == NL80211_IFTYPE_AP &&
|
(sc->bq4_dtim) &&
|
info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM )
|
{
|
struct ssv_vif_priv_data *priv_vif = (struct ssv_vif_priv_data *)vif->drv_priv;
|
u8 buffered = 0;
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
if (priv_vif->sta_asleep_mask)
|
{
|
buffered = ssv6200_bcast_enqueue(sc, &sc->bcast_txq, skb);
|
if (1 == buffered) {
|
#ifdef BCAST_DEBUG
|
printk("ssv6200_tx:ssv6200_bcast_start\n");
|
#endif
|
ssv6200_bcast_start(sc);
|
}
|
}
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
if (buffered)
|
break;
|
}
|
if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)vif->drv_priv;
|
dev_dbg(sc->dev, "vif[%d] sc->bq4_dtim[%d]\n", vif_priv->vif_idx, sc->bq4_dtim);
|
}
|
tx_desc = (struct ssv6200_tx_desc *)skb->data;
|
ret = HCI_SEND(sc->sh, skb, tx_desc->txq_idx);
|
send_hci = true;
|
} while (0);
|
#ifdef ENABLE_TX_Q_FLOW_CONTROL
|
if ( (skb_queue_len(&sc->tx_skb_q) < LOW_TX_Q_LEN)
|
#ifdef MULTI_THREAD_ENCRYPT
|
&& (skb_queue_len(&sc->preprocess_q) < LOW_CRYPTO_Q_LEN)
|
&& (skb_queue_len(&sc->crypted_q) < LOW_CRYPTO_Q_LEN)
|
#endif
|
)
|
{
|
if (sc->tx.flow_ctrl_status != 0)
|
{
|
int ac;
|
for (ac = 0; ac < sc->hw->queues; ac++){
|
if ((sc->tx.flow_ctrl_status & BIT(ac)) == 0)
|
ieee80211_wake_queue(sc->hw, ac);
|
}
|
}
|
else
|
{
|
ieee80211_wake_queues(sc->hw);
|
}
|
}
|
#endif
|
if(sc->dbg_tx_frame){
|
if(send_hci)
|
printk("Tx frame send to HCI\n");
|
else
|
printk("Tx frame queued\n");
|
printk("================================================\n");
|
}
|
}
|
#ifdef MULTI_THREAD_ENCRYPT
|
bool _is_encrypt_needed(struct sk_buff *skb)
|
{
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
struct SKB_info_st *skb_info = (struct SKB_info_st *)skb->head;
|
struct ieee80211_sta *sta = skb_info->sta;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)info->control.vif->drv_priv;
|
struct ssv_sta_priv_data *ssv_sta_priv = sta ? (struct ssv_sta_priv_data *)sta->drv_priv : NULL;
|
if ( ( !ieee80211_is_data_qos(hdr->frame_control)
|
&& !ieee80211_is_data(hdr->frame_control))
|
|| !ieee80211_has_protected(hdr->frame_control))
|
return false;
|
if (!is_unicast_ether_addr(hdr->addr1))
|
{
|
if ( vif_priv->is_security_valid
|
&& vif_priv->need_sw_encrypt
|
#ifdef USE_LOCAL_CRYPTO
|
&& (vif_priv->crypto_data.ops != NULL)
|
#endif
|
)
|
{
|
return true;
|
}
|
}
|
else if (ssv_sta_priv != NULL)
|
{
|
if ( ssv_sta_priv->need_sw_encrypt
|
#ifdef USE_LOCAL_CRYPTO
|
&& (ssv_sta_priv->crypto_data.ops != NULL)
|
#endif
|
)
|
return true;
|
}
|
return false;
|
}
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
|
static int ssv6200_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
static void ssv6200_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
#else
|
static void ssv6200_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb)
|
#endif
|
{
|
struct ssv_softc *sc = (struct ssv_softc *)hw->priv;
|
struct SKB_info_st *skb_info = (struct SKB_info_st *)skb->head;
|
#ifdef MULTI_THREAD_ENCRYPT
|
struct ssv_encrypt_task_list *ta = NULL;
|
unsigned long flags;
|
int ret = -EOPNOTSUPP;
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
skb_info->sta = info->control.sta;
|
#else
|
skb_info->sta = control ? control->sta : NULL;
|
#endif
|
#ifdef CONFIG_DEBUG_SKB_TIMESTAMP
|
skb_info->timestamp = ktime_get();
|
#endif
|
#if 0
|
_ssv6xxx_tx(hw, skb);
|
#else
|
#ifndef MULTI_THREAD_ENCRYPT
|
skb_queue_tail(&sc->tx_skb_q, skb);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
if (sc->max_tx_skb_q_len < skb_queue_len(&sc->tx_skb_q))
|
sc->max_tx_skb_q_len = skb_queue_len(&sc->tx_skb_q);
|
#endif
|
wake_up_interruptible(&sc->tx_wait_q);
|
#else
|
skb_info->crypt_st = PKT_CRYPT_ST_ENC_DONE;
|
if(_is_encrypt_needed(skb))
|
{
|
skb_info->crypt_st = PKT_CRYPT_ST_ENC_PRE;
|
ret = ssv6xxx_skb_pre_encrypt(skb, sc);
|
}
|
if (ret == 0)
|
{
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
__skb_queue_tail(&sc->preprocess_q, skb);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
if (sc->max_preprocess_q_len < skb_queue_len(&sc->preprocess_q))
|
sc->max_preprocess_q_len = skb_queue_len(&sc->preprocess_q);
|
#endif
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
list_for_each_entry(ta, &sc->encrypt_task_head, list)
|
{
|
if((cpu_online(ta->cpu_no)) && (ta->running == 0))
|
{
|
wake_up(&ta->encrypt_wait_q);
|
break;
|
}
|
}
|
}
|
else if(ret == -EOPNOTSUPP)
|
{
|
skb_info->crypt_st = PKT_CRYPT_ST_NOT_SUPPORT;
|
skb_queue_tail(&sc->tx_skb_q, skb);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
if (sc->max_tx_skb_q_len < skb_queue_len(&sc->tx_skb_q))
|
sc->max_tx_skb_q_len = skb_queue_len(&sc->tx_skb_q);
|
#endif
|
wake_up_interruptible(&sc->tx_wait_q);
|
}
|
else
|
{
|
dev_err(sc->dev, "strange fail to pre-encrypt packet/n");
|
}
|
#endif
|
#endif
|
#ifdef ENABLE_TX_Q_FLOW_CONTROL
|
do {
|
#ifdef MULTI_THREAD_ENCRYPT
|
if ( (skb_queue_len(&sc->preprocess_q) >= MAX_CRYPTO_Q_LEN)
|
|| (skb_queue_len(&sc->crypted_q) >= MAX_CRYPTO_Q_LEN))
|
{
|
ieee80211_stop_queues(sc->hw);
|
break;
|
}
|
#endif
|
if (skb_queue_len(&sc->tx_skb_q) >= MAX_TX_Q_LEN)
|
ieee80211_stop_queues(sc->hw);
|
} while (0);
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
|
return NETDEV_TX_OK;
|
#endif
|
}
|
#ifdef MULTI_THREAD_ENCRYPT
|
int ssv6xxx_encrypt_task (void *data)
|
{
|
struct ssv_softc *sc = (struct ssv_softc *)data;
|
unsigned long flags;
|
struct ssv_encrypt_task_list *ta = NULL;
|
struct task_struct *this_task = current;
|
int ori_prio;
|
int min_prio;
|
int cur_prio;
|
u32 cont_crypto_failure = 0;
|
const u32 max_cont_crypto_failure = 10;
|
ori_prio = this_task->prio;
|
min_prio = 0;
|
cur_prio = ori_prio;
|
dev_err(sc->dev, "Crypto task %d running with priority %d.\n", this_task->pid, ori_prio);
|
list_for_each_entry(ta, &sc->encrypt_task_head, list)
|
{
|
if(ta->encrypt_task == current)
|
break;
|
}
|
while (!kthread_should_stop())
|
{
|
struct sk_buff *skb = NULL;
|
struct ieee80211_hdr *hdr = NULL;
|
unsigned long CPUMask = 0;
|
int enc_ret = 0;
|
volatile bool wakeup_tx;
|
if(skb_queue_len_safe(&sc->preprocess_q) == 0 || (ta->cpu_offline != 0) )
|
{
|
ta->running = 0;
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
wait_event_timeout(ta->encrypt_wait_q,
|
( (ta->cpu_offline == 0)
|
&& (ta->paused == 0)
|
&& skb_queue_len_safe(&sc->preprocess_q))
|
|| kthread_should_stop(),
|
msecs_to_jiffies(60000));
|
set_current_state(TASK_RUNNING);
|
ta->running = 1;
|
CPUMask = *(cpumask_bits(¤t->cpus_allowed));
|
}
|
if (kthread_should_stop())
|
{
|
printk("[MT-ENCRYPT]: Quit Encryption task loop ...\n");
|
ta->running = 0;
|
break;
|
}
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
if ((skb = __skb_dequeue(&sc->preprocess_q)) != NULL)
|
{
|
SKB_info * skb_info = (SKB_info *)skb->head;
|
__skb_queue_tail(&sc->crypted_q, skb);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
if (sc->max_crypted_q_len < skb_queue_len(&sc->crypted_q))
|
sc->max_crypted_q_len = skb_queue_len(&sc->crypted_q);
|
#endif
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
if(skb_info->crypt_st == PKT_CRYPT_ST_ENC_PRE)
|
{
|
enc_ret = ssv6xxx_skb_encrypt(skb, sc);
|
if (enc_ret == 0)
|
{
|
skb_info->crypt_st = PKT_CRYPT_ST_ENC_DONE;
|
cont_crypto_failure = 0;
|
if (cur_prio != ori_prio)
|
{
|
struct sched_param sp = { .sched_priority = ori_prio };
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
dev_err(sc->dev, "Set crypto task %d priority to %d.\n",
|
this_task->pid, sp.sched_priority);
|
sched_setscheduler(this_task, this_task->policy, &sp);
|
cur_prio = ori_prio;
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
}
|
}
|
else
|
{
|
skb_info->crypt_st = PKT_CRYPT_ST_FAIL;
|
}
|
}
|
else if(skb_info->crypt_st == PKT_CRYPT_ST_DEC_PRE)
|
{
|
struct ieee80211_sta *sta = skb_info->sta;
|
struct ieee80211_rx_status *rxs = IEEE80211_SKB_RXCB(skb);
|
enc_ret = ssv6xxx_skb_decrypt(skb, sta, sc);
|
if (enc_ret >= 0)
|
{
|
skb_info->crypt_st = PKT_CRYPT_ST_DEC_DONE;
|
hdr = (struct ieee80211_hdr *)(skb->data);
|
hdr->frame_control = hdr->frame_control & ~(cpu_to_le16(IEEE80211_FCTL_PROTECTED));
|
rxs->flag |= (RX_FLAG_DECRYPTED|RX_FLAG_IV_STRIPPED);
|
}
|
else
|
{
|
dev_err(sc->dev, "Decrypt fail, skb = %p\n", skb);
|
skb_info->crypt_st = PKT_CRYPT_ST_FAIL;
|
}
|
}
|
if (skb_info->crypt_st == PKT_CRYPT_ST_FAIL)
|
{
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
__skb_unlink(skb, &sc->crypted_q);
|
#ifdef CONFIG_SMP
|
dev_err(sc->dev, "%d-%d(%d) crypto fail, skb = %p %d %d\n",
|
this_task->on_cpu, this_task->pid, cur_prio, skb,
|
cont_crypto_failure, ta->paused);
|
#endif
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
ssv_skb_free(skb);
|
skb = NULL;
|
if (cont_crypto_failure == max_cont_crypto_failure)
|
{
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
if (cur_prio != min_prio)
|
{
|
struct sched_param sp = { .sched_priority = min_prio };
|
sched_setscheduler(this_task, this_task->policy, &sp);
|
cur_prio = min_prio;
|
dev_err(sc->dev, "Set crypto task %d priority to %d.\n",
|
this_task->pid, sp.sched_priority);
|
}
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
}
|
else
|
{
|
++cont_crypto_failure;
|
}
|
schedule();
|
}
|
}
|
else
|
{
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
}
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
skb = NULL;
|
wakeup_tx = false;
|
while((skb = skb_peek(&sc->crypted_q)) != NULL)
|
{
|
SKB_info* skb_info = (SKB_info*)skb->head;
|
if(skb_info->crypt_st == PKT_CRYPT_ST_ENC_DONE)
|
{
|
__skb_unlink(skb, &sc->crypted_q);
|
skb_queue_tail(&sc->tx_skb_q, skb);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
if (sc->max_tx_skb_q_len < skb_queue_len(&sc->tx_skb_q))
|
sc->max_tx_skb_q_len = skb_queue_len(&sc->tx_skb_q);
|
#endif
|
wakeup_tx = true;
|
skb = NULL;
|
}
|
else if(skb_info->crypt_st == PKT_CRYPT_ST_DEC_DONE)
|
{
|
__skb_unlink(skb, &sc->crypted_q);
|
ieee80211_rx_irqsafe(sc->hw, skb);
|
}
|
else
|
break;
|
}
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
skb = NULL;
|
if(wakeup_tx)
|
{
|
wake_up_interruptible(&sc->tx_wait_q);
|
}
|
}
|
return 0;
|
}
|
void _stop_crypto_task (struct ssv_softc *sc)
|
{
|
struct ssv_encrypt_task_list *ta = NULL;
|
int num_running = 0;
|
list_for_each_entry(ta, &sc->encrypt_task_head, list)
|
{
|
ta->paused = 1;
|
}
|
do {
|
num_running = 0;
|
list_for_each_entry(ta, &sc->encrypt_task_head, list)
|
{
|
if (ta->running != 0)
|
{
|
int cpu_id = smp_processor_id();
|
if (ta->cpu_no == cpu_id)
|
{
|
unsigned long flags;
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
dev_warn(sc->dev, "Crypto running on the same core. (%d, %d)\n", cpu_id, num_running);
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
continue;
|
}
|
++num_running;
|
break;
|
}
|
}
|
} while (num_running != 0);
|
}
|
void _resume_crypto_task (struct ssv_softc *sc)
|
{
|
struct ssv_encrypt_task_list *ta = NULL;
|
list_for_each_entry(ta, &sc->encrypt_task_head, list)
|
{
|
ta->paused = 0;
|
wake_up(&ta->encrypt_wait_q);
|
}
|
}
|
u32 _remove_sta_skb_from_q (struct ssv_softc *sc, struct sk_buff_head *q,
|
u32 addr0_3, u32 addr4_5)
|
{
|
struct sk_buff *skb, *skb_tmp;
|
u32 removed_skb_num = 0;
|
skb_queue_walk_safe(q, skb, skb_tmp)
|
{
|
struct ieee80211_sta *skb_sta;
|
u32 skb_addr0_3;
|
u32 skb_addr4_5;
|
struct SKB_info_st *skb_info = (struct SKB_info_st *)skb->head;
|
skb_sta = skb_info->sta;
|
if (skb_sta == NULL)
|
continue;
|
skb_addr0_3 = *(u32 *)&skb_sta->addr[0];
|
skb_addr4_5 = (u32)*(u16 *)&skb_sta->addr[4];
|
if ((addr0_3 != skb_addr0_3) || (addr4_5 != skb_addr4_5))
|
continue;
|
__skb_unlink(skb, q);
|
ssv6xxx_txbuf_free_skb(skb, sc);
|
++removed_skb_num;
|
}
|
return removed_skb_num;
|
}
|
void _clean_up_crypto_skb (struct ssv_softc *sc, struct ieee80211_sta *sta)
|
{
|
unsigned long flags;
|
u32 sta_addr0_3 = *(u32 *)&sta->addr[0];
|
u32 sta_addr4_5 = (u32)*(u16 *)&sta->addr[4];
|
u32 removed_skb_num;
|
dev_info(sc->dev, "Clean up skb for STA %pM.\n", sta->addr);
|
_stop_crypto_task(sc);
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
removed_skb_num = _remove_sta_skb_from_q(sc, &sc->preprocess_q,
|
sta_addr0_3, sta_addr4_5);
|
dev_info(sc->dev, "Removed %u MPDU from precess queue.\n", removed_skb_num);
|
removed_skb_num = _remove_sta_skb_from_q(sc, &sc->crypted_q,
|
sta_addr0_3, sta_addr4_5);
|
dev_info(sc->dev, "Removed %u MPDU from encrypted queue.\n", removed_skb_num);
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
_resume_crypto_task(sc);
|
}
|
#endif
|
int ssv6xxx_tx_task (void *data)
|
{
|
struct ssv_softc *sc = (struct ssv_softc *)data;
|
u32 wait_period = SSV_AMPDU_timer_period / 2;
|
printk("SSV6XXX TX Task started.\n");
|
while (!kthread_should_stop())
|
{
|
u32 before_timeout = (-1);
|
set_current_state(TASK_INTERRUPTIBLE);
|
before_timeout = wait_event_interruptible_timeout(sc->tx_wait_q,
|
( skb_queue_len(&sc->tx_skb_q)
|
|| kthread_should_stop()
|
|| sc->tx_q_empty),
|
msecs_to_jiffies(wait_period));
|
if (kthread_should_stop())
|
{
|
printk("Quit TX task loop...\n");
|
break;
|
}
|
set_current_state(TASK_RUNNING);
|
do {
|
struct sk_buff *tx_skb = skb_dequeue(&sc->tx_skb_q);
|
if (tx_skb == NULL)
|
break;
|
_ssv6xxx_tx(sc->hw, tx_skb);
|
} while (1);
|
#ifdef CONFIG_DEBUG_SKB_TIMESTAMP
|
{
|
struct ssv_hw_txq *hw_txq = NULL;
|
struct ieee80211_tx_info *tx_info = NULL;
|
struct sk_buff *skb = NULL;
|
int txqid;
|
unsigned int timeout;
|
u32 status;
|
for (txqid=0; txqid<SSV_HW_TXQ_NUM; txqid++) {
|
hw_txq = &ssv_dbg_ctrl_hci->hw_txq[txqid];
|
skb = skb_peek(&hw_txq->qhead);
|
if (skb != NULL) {
|
tx_info = IEEE80211_SKB_CB(skb);
|
if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
|
timeout = cal_duration_of_ampdu(skb, SKB_DURATION_STAGE_IN_HWQ);
|
else
|
timeout = cal_duration_of_mpdu(skb);
|
if (timeout > SKB_DURATION_TIMEOUT_MS) {
|
HCI_IRQ_STATUS(ssv_dbg_ctrl_hci, &status);
|
printk("hci int_mask: %08x\n", ssv_dbg_ctrl_hci->int_mask);
|
printk("sdio status: %08x\n", status);
|
printk("hwq%d len: %d\n", txqid, skb_queue_len(&hw_txq->qhead));
|
}
|
}
|
}
|
}
|
#endif
|
if (sc->tx_q_empty || (before_timeout == 0))
|
{
|
u32 flused_ampdu = ssv6xxx_ampdu_flush(sc->hw);
|
sc->tx_q_empty = false;
|
if (flused_ampdu == 0 && before_timeout == 0)
|
{
|
wait_period *= 2;
|
if (wait_period > 1000)
|
wait_period = 1000;
|
}
|
}
|
else
|
wait_period = SSV_AMPDU_timer_period / 2;
|
}
|
return 0;
|
}
|
int ssv6xxx_rx_task (void *data)
|
{
|
struct ssv_softc *sc = (struct ssv_softc *)data;
|
unsigned long wait_period = msecs_to_jiffies(200);
|
unsigned long last_timeout_check_jiffies = jiffies;
|
unsigned long cur_jiffies;
|
printk("SSV6XXX RX Task started.\n");
|
while (!kthread_should_stop())
|
{
|
u32 before_timeout = (-1);
|
set_current_state(TASK_INTERRUPTIBLE);
|
before_timeout = wait_event_interruptible_timeout(sc->rx_wait_q,
|
( skb_queue_len(&sc->rx_skb_q)
|
|| skb_queue_len(&sc->tx_done_q)
|
|| kthread_should_stop()),
|
wait_period);
|
if (kthread_should_stop())
|
{
|
printk("Quit RX task loop...\n");
|
break;
|
}
|
set_current_state(TASK_RUNNING);
|
cur_jiffies = jiffies;
|
if ( (before_timeout == 0)
|
|| time_before((last_timeout_check_jiffies + wait_period), cur_jiffies))
|
{
|
ssv6xxx_ampdu_check_timeout(sc->hw);
|
last_timeout_check_jiffies = cur_jiffies;
|
}
|
if (skb_queue_len(&sc->rx_skb_q))
|
_process_rx_q(sc, &sc->rx_skb_q, NULL);
|
if (skb_queue_len(&sc->tx_done_q))
|
_process_tx_done(sc);
|
}
|
return 0;
|
}
|
#ifdef CONFIG_SSV_CABRIO_E
|
struct ssv6xxx_iqk_cfg init_iqk_cfg = {
|
SSV6XXX_IQK_CFG_XTAL_26M,
|
#ifdef CONFIG_SSV_DPD
|
SSV6XXX_IQK_CFG_PA_LI_MPB,
|
#else
|
SSV6XXX_IQK_CFG_PA_DEF,
|
#endif
|
0,
|
0,
|
26,
|
3,
|
0x75,
|
0x75,
|
0x80,
|
0x80,
|
SSV6XXX_IQK_CMD_INIT_CALI,
|
{ SSV6XXX_IQK_TEMPERATURE
|
+ SSV6XXX_IQK_RXDC
|
+ SSV6XXX_IQK_RXRC
|
+ SSV6XXX_IQK_TXDC
|
+ SSV6XXX_IQK_TXIQ
|
+ SSV6XXX_IQK_RXIQ
|
#ifdef CONFIG_SSV_DPD
|
+ SSV6XXX_IQK_PAPD
|
#endif
|
},
|
};
|
#endif
|
static int ssv6200_start(struct ieee80211_hw *hw)
|
{
|
struct ssv_softc *sc=hw->priv;
|
struct ssv_hw *sh=sc->sh;
|
struct ieee80211_channel *chan;
|
mutex_lock(&sc->mutex);
|
if (ssv6xxx_init_mac(sc->sh) != 0) {
|
printk("Initialize ssv6200 mac fail!!\n");
|
ssv6xxx_deinit_mac(sc);
|
mutex_unlock(&sc->mutex);
|
return -1;
|
}
|
#ifdef CONFIG_P2P_NOA
|
ssv6xxx_noa_reset(sc);
|
#endif
|
HCI_START(sh);
|
ieee80211_wake_queues(hw);
|
ssv6200_ampdu_init(hw);
|
sc->watchdog_flag = WD_KICKED;
|
mutex_unlock(&sc->mutex);
|
mod_timer(&sc->watchdog_timeout, jiffies + WATCHDOG_TIMEOUT);
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
#ifdef CONFIG_SSV_SMARTLINK
|
{
|
extern int ksmartlink_init(void);
|
(void)ksmartlink_init();
|
}
|
#endif
|
#endif
|
#ifdef CONFIG_SSV_CABRIO_E
|
{
|
int ret = ssv6xxx_do_iq_calib(sc->sh, &init_iqk_cfg);
|
if (ret != 0) {
|
printk("IQ Calibration failed (%d)!!\n", ret);
|
return ret;
|
}
|
}
|
|
printk("+++++++++++++++++++++++++++222222222222222!!\n");
|
|
SMAC_REG_WRITE(sc->sh, ADR_PHY_EN_1, 0x217f);
|
if((sh->cfg.chip_identity == SSV6051Z) || (sc->sh->cfg.chip_identity == SSV6051P))
|
{
|
int i;
|
for (i = 0; i < sh->ch_cfg_size; i++)
|
{
|
SMAC_REG_READ(sh, sh->p_ch_cfg[i].reg_addr, &sh->p_ch_cfg[i].ch1_12_value);
|
}
|
}
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
chan = hw->conf.channel;
|
#else
|
chan = hw->conf.chandef.chan;
|
#endif
|
sc->cur_channel = chan;
|
printk("%s(): current channel: %d,sc->ps_status=%d\n", __FUNCTION__, sc->cur_channel->hw_value,sc->ps_status);
|
ssv6xxx_set_channel(sc, chan->hw_value);
|
ssv6xxx_rf_enable(sh);
|
return 0;
|
}
|
static void ssv6200_stop(struct ieee80211_hw *hw)
|
{
|
struct ssv_softc *sc=hw->priv;
|
u32 count=0;
|
#ifdef CONFIG_SSV_RSSI
|
struct rssi_res_st *rssi_tmp0, *rssi_tmp1;
|
#endif
|
printk(KERN_INFO "%s(): sc->ps_status=%d\n", __FUNCTION__,sc->ps_status);
|
mutex_lock(&sc->mutex);
|
#ifdef CONFIG_SSV_RSSI
|
list_for_each_entry_safe(rssi_tmp0, rssi_tmp1, &rssi_res.rssi_list, rssi_list) {
|
list_del(&rssi_tmp0->rssi_list);
|
kfree(rssi_tmp0);
|
}
|
#endif
|
ssv6200_ampdu_deinit(hw);
|
ssv6xxx_rf_disable(sc->sh);
|
HCI_STOP(sc->sh);
|
#ifndef NO_USE_RXQ_LOCK
|
while(0) {
|
#else
|
while (skb_queue_len(&sc->rx.rxq_head)) {
|
#endif
|
printk("sc->rx.rxq_count=%d\n", sc->rx.rxq_count);
|
count ++;
|
if (count > 90000000) {
|
printk("ERROR....ERROR......ERROR..........\n");
|
break;
|
}
|
}
|
HCI_TXQ_FLUSH(sc->sh, (TXQ_EDCA_0|TXQ_EDCA_1|TXQ_EDCA_2|
|
TXQ_EDCA_3|TXQ_MGMT));
|
#if 0
|
cancel_work_sync(&sc->rx_work);
|
#endif
|
if((sc->ps_status == PWRSV_PREPARE)||(sc->ps_status == PWRSV_ENABLE)){
|
ssv6xxx_enable_ps(sc);
|
ssv6xxx_rf_enable(sc->sh);
|
}
|
sc->watchdog_flag = WD_SLEEP;
|
mutex_unlock(&sc->mutex);
|
del_timer_sync(&sc->watchdog_timeout);
|
#if (defined(CONFIG_SSV_SUPPORT_ANDROID)||defined(CONFIG_SSV_BUILD_AS_ONE_KO))
|
#ifdef CONFIG_SSV_SMARTLINK
|
{
|
extern void ksmartlink_exit(void);
|
ksmartlink_exit();
|
}
|
#endif
|
#endif
|
printk("%s(): leave\n", __FUNCTION__);
|
}
|
void inline ssv62xxx_set_bssid(struct ssv_softc *sc, u8 *bssid)
|
{
|
memcpy(sc->bssid, bssid, 6);
|
SMAC_REG_WRITE(sc->sh, ADR_BSSID_0, *((u32 *)&sc->bssid[0]));
|
SMAC_REG_WRITE(sc->sh, ADR_BSSID_1, *((u32 *)&sc->bssid[4]));
|
}
|
struct ssv_vif_priv_data * ssv6xxx_config_vif_res(struct ssv_softc *sc,
|
struct ieee80211_vif *vif)
|
{
|
int i;
|
struct ssv_vif_priv_data *priv_vif;
|
struct ssv_vif_info *vif_info;
|
lockdep_assert_held(&sc->mutex);
|
for(i=0 ; i<SSV6200_MAX_VIF ;i++){
|
if (sc->vif_info[i].vif == NULL)
|
break;
|
}
|
BUG_ON(i >= SSV6200_MAX_VIF);
|
printk("ssv6xxx_config_vif_res id[%d].\n", i);
|
priv_vif = (struct ssv_vif_priv_data *)vif->drv_priv;
|
memset(priv_vif, 0, sizeof(struct ssv_vif_priv_data));
|
priv_vif->vif_idx = i;
|
memset(&sc->vif_info[i], 0, sizeof(sc->vif_info[0]));
|
sc->vif_info[i].vif = vif;
|
sc->vif_info[i].vif_priv = priv_vif;
|
INIT_LIST_HEAD(&priv_vif->sta_list);
|
priv_vif->pair_cipher = SSV_CIPHER_NONE;
|
priv_vif->group_cipher = SSV_CIPHER_NONE;
|
priv_vif->has_hw_decrypt = false;
|
priv_vif->has_hw_encrypt = false;
|
priv_vif->need_sw_encrypt = false;
|
priv_vif->need_sw_decrypt = false;
|
priv_vif->use_mac80211_decrypt = false;
|
priv_vif->is_security_valid = false;
|
priv_vif->force_sw_encrypt = (vif->type == NL80211_IFTYPE_AP);
|
#ifdef USE_LOCAL_CRYPTO
|
priv_vif->crypto_data.ops = NULL;
|
priv_vif->crypto_data.priv = NULL;
|
#ifdef HAS_CRYPTO_LOCK
|
rwlock_init(&priv_vif->crypto_data.lock);
|
#endif
|
#endif
|
vif_info = &sc->vif_info[priv_vif->vif_idx];
|
vif_info->if_type = vif->type;
|
vif_info->vif = vif;
|
return priv_vif;
|
}
|
static int ssv6200_add_interface(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif)
|
{
|
struct ssv_softc *sc=hw->priv;
|
int ret=0;
|
struct ssv_vif_priv_data *vif_priv = NULL;
|
printk("[I] %s(): vif->type = %d, NL80211_IFTYPE_AP=%d\n", __FUNCTION__, vif->type,NL80211_IFTYPE_AP);
|
if ( (sc->nvif >= SSV6200_MAX_VIF)
|
|| ( ( (vif->type == NL80211_IFTYPE_AP)
|
|| (vif->p2p))
|
&& (sc->ap_vif != NULL)))
|
{
|
dev_err(sc->dev, "Add interface of type %d (p2p: %d) failed.\n", vif->type, vif->p2p);
|
return -EOPNOTSUPP;
|
}
|
mutex_lock(&sc->mutex);
|
vif_priv = ssv6xxx_config_vif_res(sc, vif);
|
if ((vif_priv->vif_idx == 0) && (vif->p2p == 0) && (vif->type == NL80211_IFTYPE_AP))
|
{
|
printk("VIF[0] set bssid and config opmode to ap\n");
|
ssv62xxx_set_bssid(sc, sc->sh->cfg.maddr[0]);
|
SMAC_REG_SET_BITS(sc->sh, ADR_GLBLE_SET, SSV6200_OPMODE_AP, OP_MODE_MSK);
|
}
|
if (vif->type == NL80211_IFTYPE_AP)
|
{
|
BUG_ON(sc->ap_vif != NULL);
|
sc->ap_vif = vif;
|
if ( !vif->p2p
|
&& (vif_priv->vif_idx == 0))
|
{
|
printk("Normal AP mode. Config Q4 to DTIM Q.\n");
|
SMAC_REG_SET_BITS(sc->sh, ADR_MTX_BCN_EN_MISC,
|
MTX_HALT_MNG_UNTIL_DTIM_MSK,
|
MTX_HALT_MNG_UNTIL_DTIM_MSK);
|
sc->bq4_dtim = true;
|
}
|
#ifdef CONFIG_SSV_SUPPORT_ANDROID
|
if(vif->p2p == 0)
|
{
|
printk(KERN_INFO "AP mode init wifi_alive_lock\n");
|
ssv_wake_lock(sc);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_CONTROL, 0x00160200);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_1, 0x20380050);
|
}
|
#endif
|
}
|
sc->nvif++;
|
dev_err(sc->dev, "VIF %02x:%02x:%02x:%02x:%02x:%02x of type %d is added.\n",
|
vif->addr[0], vif->addr[1], vif->addr[2],
|
vif->addr[3], vif->addr[4], vif->addr[5], vif->type);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
ssv6xxx_debugfs_add_interface(sc, vif);
|
#endif
|
mutex_unlock(&sc->mutex);
|
return ret;
|
}
|
static void ssv6200_remove_interface(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif)
|
{
|
struct ssv_softc *sc = hw->priv;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)vif->drv_priv;
|
#ifdef USE_LOCAL_CRYPTO
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &vif_priv->crypto_data);
|
#endif
|
dev_err(sc->dev,
|
"Removing interface %02x:%02x:%02x:%02x:%02x:%02x. PS=%d\n",
|
vif->addr[0], vif->addr[1], vif->addr[2],
|
vif->addr[3], vif->addr[4], vif->addr[5], sc->ps_status);
|
mutex_lock(&sc->mutex);
|
#ifdef USE_LOCAL_CRYPTO
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
if (crypto_data->ops && crypto_data->priv)
|
{
|
crypto_data->ops->deinit(crypto_data->priv);
|
}
|
crypto_data->ops = NULL;
|
crypto_data->priv = NULL;
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
#endif
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
ssv6xxx_debugfs_remove_interface(sc, vif);
|
#endif
|
if (vif->type == NL80211_IFTYPE_AP)
|
{
|
if (sc->bq4_dtim)
|
{
|
sc->bq4_dtim = false;
|
ssv6200_release_bcast_frame_res(sc, vif);
|
SMAC_REG_SET_BITS(sc->sh, ADR_MTX_BCN_EN_MISC,
|
0, MTX_HALT_MNG_UNTIL_DTIM_MSK);
|
printk("Config Q4 to normal Q \n");
|
}
|
ssv6xxx_beacon_release(sc);
|
sc->ap_vif = NULL;
|
#ifdef CONFIG_SSV_SUPPORT_ANDROID
|
if(vif->p2p == 0)
|
{
|
ssv_wake_unlock(sc);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_CONTROL, 0x0);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_1, 0x20300050);
|
printk(KERN_INFO "AP mode destroy wifi_alive_lock\n");
|
}
|
#endif
|
}
|
memset(&sc->vif_info[vif_priv->vif_idx], 0, sizeof(struct ssv_vif_info));
|
sc->nvif--;
|
mutex_unlock(&sc->mutex);
|
}
|
static int ssv6200_change_interface(struct ieee80211_hw *dev,
|
struct ieee80211_vif *vif,
|
enum nl80211_iftype new_type,
|
bool p2p)
|
{
|
int ret = 0;
|
printk("change_interface new: %d (%d), old: %d (%d)\n", new_type,
|
p2p, vif->type, vif->p2p);
|
|
if (new_type != vif->type || vif->p2p != p2p) {
|
ssv6200_remove_interface(dev, vif);
|
vif->type = new_type;
|
vif->p2p = p2p;
|
ret = ssv6200_add_interface(dev, vif);
|
}
|
|
return ret;
|
}
|
void ssv6xxx_ps_callback_func(unsigned long data)
|
{
|
struct ssv_softc *sc = (struct ssv_softc *)data;
|
struct sk_buff *skb;
|
struct cfg_host_cmd *host_cmd;
|
int retry_cnt=20;
|
#ifdef SSV_WAKEUP_HOST
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_CPU<<4)|(M_ENG_HWHCI<<8));
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_CPU<<4)|(M_ENG_HWHCI<<8));
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_TB0+6*4, (sc->mac_deci_tbl[6]|1));
|
#else
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_TRASH_CAN<<4));
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_TRASH_CAN<<4));
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_MNG, M_ENG_MACRX|(M_ENG_TRASH_CAN<<4));
|
#endif
|
skb = ssv_skb_alloc(sizeof(struct cfg_host_cmd));
|
skb->data_len = sizeof(struct cfg_host_cmd);
|
skb->len = skb->data_len;
|
host_cmd = (struct cfg_host_cmd *)skb->data;
|
host_cmd->c_type = HOST_CMD;
|
host_cmd->RSVD0 = 0;
|
host_cmd->h_cmd = (u8)SSV6XXX_HOST_CMD_PS;
|
host_cmd->len = skb->data_len;
|
#ifdef SSV_WAKEUP_HOST
|
host_cmd->dummy = sc->ps_aid;
|
#else
|
host_cmd->dummy = 0;
|
#endif
|
sc->ps_aid = 0;
|
while((HCI_SEND_CMD(sc->sh, skb)!=0)&&(retry_cnt)){
|
printk(KERN_INFO "PS cmd retry=%d!!\n",retry_cnt);
|
retry_cnt--;
|
}
|
ssv_skb_free(skb);
|
printk(KERN_INFO "SSV6XXX_HOST_CMD_PS,ps_aid = %d,len=%d,tabl=0x%x\n",host_cmd->dummy,skb->len,(sc->mac_deci_tbl[6]|1));
|
}
|
void ssv6xxx_enable_ps(struct ssv_softc *sc)
|
{
|
sc->ps_status = PWRSV_ENABLE;
|
}
|
void ssv6xxx_disable_ps(struct ssv_softc *sc)
|
{
|
sc->ps_status = PWRSV_DISABLE;
|
printk(KERN_INFO "PowerSave disabled\n");
|
}
|
int ssv6xxx_watchdog_controller(struct ssv_hw *sh ,u8 flag)
|
{
|
struct sk_buff *skb;
|
struct cfg_host_cmd *host_cmd;
|
int ret = 0;
|
printk("ssv6xxx_watchdog_controller %d\n",flag);
|
skb = ssv_skb_alloc(HOST_CMD_HDR_LEN);
|
if(skb == NULL)
|
{
|
printk("init ssv6xxx_watchdog_controller fail!!!\n");
|
return (-1);
|
}
|
skb->data_len = HOST_CMD_HDR_LEN;
|
skb->len = skb->data_len;
|
host_cmd = (struct cfg_host_cmd *)skb->data;
|
host_cmd->c_type = HOST_CMD;
|
host_cmd->h_cmd = (u8)flag;
|
host_cmd->len = skb->data_len;
|
sh->hci.hci_ops->hci_send_cmd(skb);
|
ssv_skb_free(skb);
|
return ret;
|
}
|
static int ssv6200_config(struct ieee80211_hw *hw, u32 changed)
|
{
|
struct ssv_softc *sc=hw->priv;
|
int ret=0;
|
mutex_lock(&sc->mutex);
|
if (changed & IEEE80211_CONF_CHANGE_PS) {
|
struct ieee80211_conf *conf = &hw->conf;
|
if (conf->flags & IEEE80211_CONF_PS) {
|
printk("Enable IEEE80211_CONF_PS ps_aid=%d\n",sc->ps_aid);
|
}else{
|
printk("Disable IEEE80211_CONF_PS ps_aid=%d\n",sc->ps_aid);
|
}
|
}
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
|
if (changed & IEEE80211_CONF_CHANGE_QOS) {
|
struct ieee80211_conf *conf = &hw->conf;
|
bool qos_active = !!(conf->flags & IEEE80211_CONF_QOS);
|
SMAC_REG_SET_BITS(sc->sh, ADR_GLBLE_SET,
|
(qos_active<<QOS_EN_SFT), QOS_EN_MSK);
|
}
|
#endif
|
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
|
struct ieee80211_channel *chan;
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
chan = hw->conf.channel;
|
#else
|
chan = hw->conf.chandef.chan;
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
|
{
|
struct ieee80211_channel *curchan = hw->conf.channel;
|
if(sc->bScanning == true &&
|
sc->channel_center_freq != curchan->center_freq && sc->isAssoc){
|
hw->conf.flags |= IEEE80211_CONF_OFFCHANNEL;
|
}
|
else{
|
hw->conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
|
}
|
}
|
#endif
|
#ifdef CONFIG_P2P_NOA
|
if(sc->p2p_noa.active_noa_vif){
|
printk("NOA operating-active vif[%02x] skip scan\n", sc->p2p_noa.active_noa_vif);
|
goto out;
|
}
|
#endif
|
if (hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
|
{
|
if ( (sc->ap_vif == NULL)
|
|| list_empty(&((struct ssv_vif_priv_data *)sc->ap_vif->drv_priv)->sta_list))
|
{
|
HCI_PAUSE(sc->sh, (TXQ_EDCA_0|TXQ_EDCA_1|TXQ_EDCA_2|TXQ_EDCA_3| TXQ_MGMT));
|
sc->sc_flags |= SC_OP_OFFCHAN;
|
ssv6xxx_set_channel(sc, chan->hw_value);
|
sc->hw_chan = chan->hw_value;
|
HCI_RESUME(sc->sh, TXQ_MGMT);
|
}
|
else
|
{
|
dev_dbg(sc->dev, "Off-channel to %d is ignored when AP mode enabled.\n", chan->hw_value);
|
}
|
}
|
else {
|
if ( (sc->cur_channel == NULL)
|
|| (sc->sc_flags & SC_OP_OFFCHAN)
|
|| (sc->hw_chan != chan->hw_value))
|
{
|
HCI_PAUSE(sc->sh, (TXQ_EDCA_0|TXQ_EDCA_1|TXQ_EDCA_2|TXQ_EDCA_3| TXQ_MGMT));
|
ssv6xxx_set_channel(sc, chan->hw_value);
|
sc->cur_channel = chan;
|
HCI_RESUME(sc->sh, (TXQ_EDCA_0|TXQ_EDCA_1|TXQ_EDCA_2|TXQ_EDCA_3|TXQ_MGMT));
|
sc->sc_flags &= ~SC_OP_OFFCHAN;
|
}
|
else
|
{
|
dev_dbg(sc->dev, "Change to the same channel %d\n", chan->hw_value);
|
}
|
}
|
}
|
#ifdef CONFIG_P2P_NOA
|
out:
|
#endif
|
mutex_unlock(&sc->mutex);
|
return ret;
|
}
|
#if 0
|
static int sv6200_conf_tx(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif, u16 queue,
|
const struct ieee80211_tx_queue_params *params)
|
{
|
struct ssv_softc *sc = hw->priv;
|
u32 cw;
|
u8 hw_txqid = sc->tx.hw_txqid[queue];
|
printk("[I] sv6200_conf_tx qos[%d] queue[%d] aifsn[%d] cwmin[%d] cwmax[%d] txop[%d] \n",
|
vif->bss_conf.qos, queue, params->aifs, params->cw_min, params->cw_max, params->txop);
|
if (queue > NL80211_TXQ_Q_BK)
|
return 1;
|
mutex_lock(&sc->mutex);
|
#define QOS_EN_MSK 0x00000010
|
#define QOS_EN_I_MSK 0xffffffef
|
#define QOS_EN_SFT 4
|
#define QOS_EN_HI 4
|
#define QOS_EN_SZ 1
|
SMAC_REG_SET_BITS(sc->sh, ADR_GLBLE_SET, (vif->bss_conf.qos<<QOS_EN_SFT), QOS_EN_MSK);
|
cw = params->aifs&0xf;
|
cw|= ((ilog2(params->cw_min+1))&0xf)<<8;
|
cw|= ((ilog2(params->cw_max+1))&0xf)<<12;
|
cw|= ((params->txop)&0xff)<<16;
|
SMAC_REG_WRITE(sc->sh, ADR_TXQ0_MTX_Q_AIFSN+0x100*hw_txqid, cw);
|
mutex_unlock(&sc->mutex);
|
return 0;
|
}
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0)
|
#define SUPPORTED_FILTERS \
|
(FIF_PROMISC_IN_BSS | \
|
FIF_ALLMULTI | \
|
FIF_CONTROL | \
|
FIF_PSPOLL | \
|
FIF_OTHER_BSS | \
|
FIF_BCN_PRBRESP_PROMISC | \
|
FIF_PROBE_REQ | \
|
FIF_FCSFAIL)
|
#else
|
#define SUPPORTED_FILTERS \
|
(FIF_ALLMULTI | \
|
FIF_CONTROL | \
|
FIF_PSPOLL | \
|
FIF_OTHER_BSS | \
|
FIF_BCN_PRBRESP_PROMISC | \
|
FIF_PROBE_REQ | \
|
FIF_FCSFAIL)
|
#endif
|
static void ssv6200_config_filter(struct ieee80211_hw *hw,
|
unsigned int changed_flags,
|
unsigned int *total_flags,
|
u64 multicast)
|
{
|
changed_flags &= SUPPORTED_FILTERS;
|
*total_flags &= SUPPORTED_FILTERS;
|
}
|
static void ssv6200_bss_info_changed(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif, struct ieee80211_bss_conf *info,
|
u32 changed)
|
{
|
struct ssv_vif_priv_data *priv_vif = (struct ssv_vif_priv_data *)vif->drv_priv;
|
struct ssv_softc *sc = hw->priv;
|
#ifdef CONFIG_P2P_NOA
|
u8 null_address[6]={0};
|
#endif
|
mutex_lock(&sc->mutex);
|
if (changed & BSS_CHANGED_ERP_PREAMBLE) {
|
printk("BSS Changed use_short_preamble[%d]\n", info->use_short_preamble);
|
if (info->use_short_preamble)
|
sc->sc_flags |= SC_OP_SHORT_PREAMBLE;
|
else
|
sc->sc_flags &= ~SC_OP_SHORT_PREAMBLE;
|
}
|
if (!priv_vif->vif_idx)
|
{
|
if (changed & BSS_CHANGED_BSSID)
|
{
|
#ifdef CONFIG_P2P_NOA
|
struct ssv_vif_priv_data *vif_priv;
|
vif_priv = (struct ssv_vif_priv_data *)vif->drv_priv;
|
#endif
|
ssv62xxx_set_bssid(sc, (u8*)info->bssid);
|
printk("BSS_CHANGED_BSSID: %02x:%02x:%02x:%02x:%02x:%02x\n",
|
info->bssid[0], info->bssid[1], info->bssid[2],
|
info->bssid[3], info->bssid[4], info->bssid[5]);
|
#ifdef CONFIG_P2P_NOA
|
if(memcmp(info->bssid, null_address, 6))
|
ssv6xxx_noa_hdl_bss_change(sc, MONITOR_NOA_CONF_ADD, vif_priv->vif_idx);
|
else
|
ssv6xxx_noa_hdl_bss_change(sc, MONITOR_NOA_CONF_REMOVE, vif_priv->vif_idx);
|
#endif
|
}
|
if (changed & BSS_CHANGED_ERP_SLOT)
|
{
|
u32 regval=0;
|
printk("BSS_CHANGED_ERP_SLOT: use_short_slot[%d]\n", info->use_short_slot);
|
if (info->use_short_slot)
|
{
|
SMAC_REG_READ(sc->sh, ADR_MTX_DUR_IFS, ®val);
|
regval = regval & MTX_DUR_SLOT_I_MSK;
|
regval |= 9 << MTX_DUR_SLOT_SFT;
|
SMAC_REG_WRITE(sc->sh, ADR_MTX_DUR_IFS, regval);
|
SMAC_REG_READ(sc->sh, ADR_MTX_DUR_SIFS_G, ®val);
|
#if 1
|
regval = regval & MTX_DUR_BURST_SIFS_G_I_MSK;
|
regval |= 0xa << MTX_DUR_BURST_SIFS_G_SFT;
|
#endif
|
regval = regval & MTX_DUR_SLOT_G_I_MSK;
|
regval |= 9 << MTX_DUR_SLOT_G_SFT;
|
SMAC_REG_WRITE(sc->sh, ADR_MTX_DUR_SIFS_G, regval);
|
}
|
else
|
{
|
SMAC_REG_READ(sc->sh, ADR_MTX_DUR_IFS, ®val);
|
regval = regval & MTX_DUR_SLOT_I_MSK;
|
regval |= 20 << MTX_DUR_SLOT_SFT;
|
SMAC_REG_WRITE(sc->sh, ADR_MTX_DUR_IFS, regval);
|
SMAC_REG_READ(sc->sh, ADR_MTX_DUR_SIFS_G, ®val);
|
#if 1
|
regval = regval & MTX_DUR_BURST_SIFS_G_I_MSK;
|
regval |= 0xa << MTX_DUR_BURST_SIFS_G_SFT;
|
#endif
|
regval = regval & MTX_DUR_SLOT_G_I_MSK;
|
regval |= 20 << MTX_DUR_SLOT_G_SFT;
|
SMAC_REG_WRITE(sc->sh, ADR_MTX_DUR_SIFS_G, regval);
|
}
|
}
|
}
|
if (changed & BSS_CHANGED_HT) {
|
printk("BSS_CHANGED_HT: Untreated!!\n");
|
}
|
if (changed & BSS_CHANGED_BASIC_RATES)
|
{
|
printk("ssv6xxx_rc_update_basic_rate!!\n");
|
ssv6xxx_rc_update_basic_rate(sc, info->basic_rates);
|
}
|
if (vif->type == NL80211_IFTYPE_STATION){
|
printk("NL80211_IFTYPE_STATION!!\n");
|
if ((changed & BSS_CHANGED_ASSOC) && (vif->p2p == 0)){
|
sc->isAssoc = info->assoc;
|
if(!sc->isAssoc){
|
sc->channel_center_freq = 0;
|
sc->ps_aid = 0;
|
#ifdef CONFIG_SSV_MRX_EN3_CTRL
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_EN3, 0x0400);
|
#endif
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_CONTROL, 0x0);
|
}
|
else{
|
struct ieee80211_channel *curchan;
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
curchan = hw->conf.channel;
|
#else
|
curchan = hw->conf.chandef.chan;
|
#endif
|
sc->channel_center_freq = curchan->center_freq;
|
printk(KERN_INFO "!!info->aid = %d\n",info->aid);
|
sc->ps_aid = info->aid;
|
#ifdef CONFIG_SSV_MRX_EN3_CTRL
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_EN3, 0x1000);
|
#endif
|
}
|
}
|
#ifdef CONFIG_SSV_MRX_EN3_CTRL
|
else if((changed & BSS_CHANGED_ASSOC) && vif->p2p == 1)
|
{
|
if(info->assoc)
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_EN3, 0x0400);
|
else if(sc->ps_aid != 0)
|
SMAC_REG_WRITE(sc->sh, ADR_MRX_FLT_EN3, 0x1000);
|
}
|
#endif
|
}
|
if (vif->type == NL80211_IFTYPE_AP)
|
{
|
if (changed & ( BSS_CHANGED_BEACON
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
|
| BSS_CHANGED_SSID
|
#endif
|
| BSS_CHANGED_BSSID
|
| BSS_CHANGED_BASIC_RATES))
|
{
|
#ifdef BROADCAST_DEBUG
|
printk("[A] ssv6200_bss_info_changed:beacon changed\n");
|
#endif
|
queue_work(sc->config_wq, &sc->set_tim_work);
|
}
|
if (changed & BSS_CHANGED_BEACON_INT)
|
{
|
printk("[A] BSS_CHANGED_BEACON_INT beacon_interval(%d)\n", info->beacon_int);
|
if (sc->beacon_interval != info->beacon_int)
|
{
|
sc->beacon_interval = info->beacon_int;
|
ssv6xxx_beacon_set_info(sc, sc->beacon_interval, sc->beacon_dtim_cnt);
|
}
|
}
|
if (changed & BSS_CHANGED_BEACON_ENABLED)
|
{
|
#ifdef BEACON_DEBUG
|
printk("[A] BSS_CHANGED_BEACON_ENABLED (0x%x)\n", info->enable_beacon);
|
#endif
|
if (0 != ssv6xxx_beacon_enable(sc, info->enable_beacon))
|
{
|
dev_err(sc->dev, "Beacon enable %d error.\n", info->enable_beacon);
|
}
|
}
|
}
|
mutex_unlock(&sc->mutex);
|
printk("[I] %s(): leave\n", __FUNCTION__);
|
}
|
static int ssv6200_sta_add(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif,
|
struct ieee80211_sta *sta)
|
{
|
struct ssv_sta_priv_data *sta_priv_dat=NULL;
|
struct ssv_softc *sc=hw->priv;
|
struct ssv_sta_info *sta_info;
|
u32 reg_wsid[] = {ADR_WSID0, ADR_WSID1};
|
int s,i;
|
u32 reg_wsid_tid0[] = {ADR_WSID0_TID0_RX_SEQ, ADR_WSID1_TID0_RX_SEQ};
|
u32 reg_wsid_tid7[] = {ADR_WSID0_TID7_RX_SEQ, ADR_WSID1_TID7_RX_SEQ};
|
unsigned long flags;
|
int ret = 0;
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)vif->drv_priv;
|
#ifdef FW_WSID_WATCH_LIST
|
int fw_sec_caps = SSV6XXX_WSID_SEC_NONE;
|
#endif
|
bool tdls_use_sw_cipher = false, tdls_link= false;
|
printk("[I] %s(): vif[%d] ", __FUNCTION__, vif_priv->vif_idx);
|
if (sc->force_triger_reset == true)
|
{
|
vif_priv->sta_asleep_mask = 0;
|
do {
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
for (s=0; s<SSV_NUM_STA; s++, sta_info++)
|
{
|
sta_info = &sc->sta_info[s];
|
if ((sta_info->s_flags & STA_FLAG_VALID))
|
{
|
if (sta_info->sta == sta)
|
{
|
printk("search stat %02x:%02x:%02x:%02x:%02x:%02x to wsid=%d\n",
|
sta->addr[0], sta->addr[1], sta->addr[2],
|
sta->addr[3], sta->addr[4], sta->addr[5], sta_info->hw_wsid);
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
return ret;
|
}
|
}
|
}
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
if (s >= SSV_NUM_STA)
|
{
|
break;
|
}
|
} while(0);
|
}
|
do {
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
if ( !list_empty(&vif_priv->sta_list) && vif->type == NL80211_IFTYPE_STATION){
|
tdls_link = true;
|
}
|
if ((tdls_link) && (vif_priv->pair_cipher != SSV_CIPHER_NONE)
|
&& (vif_priv->pair_cipher != SSV_CIPHER_CCMP)
|
&& (sc->sh->cfg.use_wpa2_only == false)){
|
tdls_use_sw_cipher = true;
|
}
|
#if 1
|
if (((vif_priv->vif_idx == 0) && (tdls_use_sw_cipher == false))
|
|| sc->sh->cfg.use_wpa2_only)
|
s = 0;
|
else
|
s = 2;
|
#else
|
#endif
|
for (; s < SSV_NUM_STA; s++)
|
{
|
sta_info = &sc->sta_info[s];
|
if ((sta_info->s_flags & STA_FLAG_VALID) == 0)
|
{
|
sta_info->aid = sta->aid;
|
sta_info->sta = sta;
|
sta_info->vif = vif;
|
sta_info->s_flags = STA_FLAG_VALID;
|
sta_priv_dat =
|
(struct ssv_sta_priv_data *)sta->drv_priv;
|
sta_priv_dat->sta_idx = s;
|
sta_priv_dat->sta_info = sta_info;
|
sta_priv_dat->has_hw_encrypt = false;
|
sta_priv_dat->has_hw_decrypt = false;
|
sta_priv_dat->need_sw_decrypt = false;
|
sta_priv_dat->need_sw_encrypt = false;
|
sta_priv_dat->use_mac80211_decrypt = false;
|
#ifdef USE_LOCAL_CRYPTO
|
sta_priv_dat->crypto_data.ops = NULL;
|
sta_priv_dat->crypto_data.priv = NULL;
|
#ifdef HAS_CRYPTO_LOCK
|
rwlock_init(&sta_priv_dat->crypto_data.lock);
|
#endif
|
#endif
|
if ( (vif_priv->pair_cipher == SSV_CIPHER_WEP40)
|
|| (vif_priv->pair_cipher == SSV_CIPHER_WEP104))
|
{
|
#ifdef USE_LOCAL_CRYPTO
|
if (vif_priv->crypto_data.ops != NULL)
|
{
|
sta_priv_dat->crypto_data.ops = vif_priv->crypto_data.ops;
|
sta_priv_dat->crypto_data.priv = vif_priv->crypto_data.priv;
|
}
|
#endif
|
sta_priv_dat->has_hw_encrypt = vif_priv->has_hw_encrypt;
|
sta_priv_dat->has_hw_decrypt = vif_priv->has_hw_decrypt;
|
sta_priv_dat->need_sw_encrypt = vif_priv->need_sw_encrypt;
|
sta_priv_dat->need_sw_decrypt = vif_priv->need_sw_decrypt;
|
}
|
list_add_tail(&sta_priv_dat->list, &vif_priv->sta_list);
|
break;
|
}
|
}
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
if (s >= SSV_NUM_STA)
|
{
|
dev_err(sc->dev, "Number of STA exceeds driver limitation %d\n.", SSV_NUM_STA);
|
ret = -1;
|
break;
|
}
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
ssv6xxx_debugfs_add_sta(sc, sta_info);
|
#endif
|
sta_info->hw_wsid = -1;
|
if (sta_priv_dat->sta_idx < SSV_NUM_HW_STA)
|
{
|
#if 0
|
SMAC_REG_READ(sc->sh, reg_wsid[s], ®_val);
|
if ((reg_val & 0x01) == 0)
|
{
|
#endif
|
SMAC_REG_WRITE(sc->sh, reg_wsid[s]+4, *((u32 *)&sta->addr[0]));
|
SMAC_REG_WRITE(sc->sh, reg_wsid[s]+8, *((u32 *)&sta->addr[4]));
|
SMAC_REG_WRITE(sc->sh, reg_wsid[s], 1);
|
for (i = reg_wsid_tid0[s]; i <= reg_wsid_tid7[s]; i += 4)
|
SMAC_REG_WRITE(sc->sh, i, 0);
|
ssv6xxx_rc_hw_reset(sc, sta_priv_dat->rc_idx, s);
|
sta_info->hw_wsid = sta_priv_dat->sta_idx;
|
}
|
#ifdef FW_WSID_WATCH_LIST
|
else if ( (vif_priv->vif_idx == 0)
|
|| sc->sh->cfg.use_wpa2_only
|
)
|
{
|
sta_info->hw_wsid = sta_priv_dat->sta_idx;
|
}
|
#endif
|
#ifdef SSV6200_ECO
|
if ((sta_priv_dat->has_hw_encrypt || sta_priv_dat->has_hw_decrypt) &&
|
((vif_priv->pair_cipher == SSV_CIPHER_WEP40) || (vif_priv->pair_cipher == SSV_CIPHER_WEP104)))
|
{
|
struct ssv_vif_info *vif_info = &sc->vif_info[vif_priv->vif_idx];
|
struct ssv6xxx_hw_sec *sramKey = &vif_info->sramKey;
|
_set_wep_hw_crypto_pair_key(sc, vif_info, sta_info, (void*)sramKey);
|
if (sramKey->sta_key[0].pair_key_idx != 0)
|
{
|
_set_wep_hw_crypto_group_key(sc, vif_info, sta_info, (void*)sramKey);
|
}
|
}
|
#endif
|
ssv6200_ampdu_tx_add_sta(hw, sta);
|
#ifdef FW_WSID_WATCH_LIST
|
if (sta_info->hw_wsid >= SSV_NUM_HW_STA)
|
{
|
if (sta_priv_dat->has_hw_decrypt)
|
fw_sec_caps = SSV6XXX_WSID_SEC_PAIRWISE;
|
if (vif_priv->has_hw_decrypt)
|
fw_sec_caps |= SSV6XXX_WSID_SEC_GROUP;
|
hw_update_watch_wsid(sc, sta, sta_info, sta_priv_dat->sta_idx,
|
fw_sec_caps, SSV6XXX_WSID_OPS_ADD);
|
} else if (SSV6200_USE_HW_WSID(sta_priv_dat->sta_idx)) {
|
hw_update_watch_wsid(sc, sta, sta_info, sta_priv_dat->sta_idx,
|
SSV6XXX_WSID_SEC_SW, SSV6XXX_WSID_OPS_HWWSID_PAIRWISE_SET_TYPE);
|
hw_update_watch_wsid(sc, sta, sta_info, sta_priv_dat->sta_idx,
|
SSV6XXX_WSID_SEC_SW, SSV6XXX_WSID_OPS_HWWSID_GROUP_SET_TYPE);
|
}
|
#endif
|
printk("Add %02x:%02x:%02x:%02x:%02x:%02x to VIF %d sw_idx=%d, wsid=%d\n",
|
sta->addr[0], sta->addr[1], sta->addr[2],
|
sta->addr[3], sta->addr[4], sta->addr[5],
|
vif_priv->vif_idx,
|
sta_priv_dat->sta_idx, sta_info->hw_wsid);
|
} while (0);
|
return ret;
|
}
|
void ssv6200_rx_flow_check(struct ssv_sta_priv_data *sta_priv_dat,
|
struct ssv_softc *sc)
|
{
|
if (SSV6200_USE_HW_WSID(sta_priv_dat->sta_idx) && (sta_priv_dat->need_sw_decrypt)){
|
int other_hw_wsid = (sta_priv_dat->sta_idx+ 1) & 1;
|
struct ssv_sta_info *sta_info = &sc->sta_info[other_hw_wsid];
|
struct ieee80211_sta *sta = sta_info->sta;
|
struct ssv_sta_priv_data *sta_priv = (struct ssv_sta_priv_data *) sta->drv_priv;
|
mutex_lock(&sc->mutex);
|
if ((sta_info-> s_flags == 0)
|
|| ((sta_info-> s_flags && STA_FLAG_VALID) && (sta_priv->has_hw_decrypt))){
|
#ifdef CONFIG_SSV_HW_ENCRYPT_SW_DECRYPT
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_HWHCI<<4));
|
#else
|
SMAC_REG_WRITE(sc->sh, ADR_RX_FLOW_DATA, M_ENG_MACRX|(M_ENG_ENCRYPT_SEC<<4)|(M_ENG_HWHCI<<8));
|
#endif
|
printk("redirect Rx flow for sta %d disconnect\n",sta_priv_dat->sta_idx);
|
}
|
mutex_unlock(&sc->mutex);
|
}
|
}
|
static int ssv6200_sta_remove(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif,
|
struct ieee80211_sta *sta)
|
{
|
u32 reg_wsid[] = {ADR_WSID0, ADR_WSID1};
|
struct ssv_sta_priv_data *sta_priv_dat = (struct ssv_sta_priv_data *)sta->drv_priv;
|
struct ssv_softc *sc = hw->priv;
|
struct ssv_sta_info *sta_info = sta_priv_dat->sta_info;
|
unsigned long flags;
|
u32 bit;
|
struct ssv_vif_priv_data *priv_vif = (struct ssv_vif_priv_data *)vif->drv_priv;
|
u8 hw_wsid = -1;
|
#ifdef USE_LOCAL_CRYPTO
|
INIT_WRITE_CRYPTO_DATA(crypto_data, &sta_priv_dat->crypto_data);
|
#endif
|
BUG_ON(sta_priv_dat->sta_idx >= SSV_NUM_STA);
|
dev_notice(sc->dev,
|
"Removing STA %d (%02X:%02X:%02X:%02X:%02X:%02X) from VIF %d\n.",
|
sta_priv_dat->sta_idx, sta->addr[0], sta->addr[1], sta->addr[2],
|
sta->addr[3], sta->addr[4], sta->addr[5], priv_vif->vif_idx);
|
ssv6200_rx_flow_check(sta_priv_dat, sc);
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
#endif
|
#ifdef USE_LOCAL_CRYPTO
|
START_WRITE_CRYPTO_DATA(crypto_data);
|
if (crypto_data->ops)
|
{
|
if ( (priv_vif->crypto_data.ops != crypto_data->ops)
|
&& crypto_data->priv)
|
{
|
crypto_data->ops->deinit(crypto_data->priv);
|
dev_info(sc->dev, "STA releases crypto OK!\n");
|
}
|
crypto_data->priv = NULL;
|
crypto_data->ops = NULL;
|
}
|
END_WRITE_CRYPTO_DATA(crypto_data);
|
#ifdef MULTI_THREAD_ENCRYPT
|
_clean_up_crypto_skb(sc, sta);
|
#endif
|
#endif
|
#if 0
|
if ((sc->ps_status == PWRSV_PREPARE)||(sc->ps_status == PWRSV_ENABLE)) {
|
memset(sta_info, 0, sizeof(*sta_info));
|
sta_priv_dat->sta_idx = -1;
|
list_del(&sta_priv_dat->list);
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
return 0;
|
}
|
#endif
|
bit = BIT(sta_priv_dat->sta_idx);
|
priv_vif->sta_asleep_mask &= ~bit;
|
if (sta_info->hw_wsid != -1) {
|
#ifndef FW_WSID_WATCH_LIST
|
BUG_ON(sta_info->hw_wsid >= SSV_NUM_HW_STA);
|
#endif
|
hw_wsid = sta_info->hw_wsid;
|
}
|
#ifdef FW_WSID_WATCH_LIST
|
if (sta_info->hw_wsid >= SSV_NUM_HW_STA)
|
{
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
hw_update_watch_wsid(sc, sta, sta_info, sta_info->hw_wsid, 0, SSV6XXX_WSID_OPS_DEL);
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
}
|
#endif
|
#if 0
|
printk("%s(): sw_idx=%d, hw_idx=%d sta_asleep_mask[%08x]\n", __FUNCTION__,
|
sta_priv_dat->sta_idx , sta_info->hw_wsid, sc->sta_asleep_mask);
|
printk("Remove %02x:%02x:%02x:%02x:%02x:%02x to sw_idx=%d, wsid=%d\n",
|
sta->addr[0], sta->addr[1], sta->addr[2],
|
sta->addr[3], sta->addr[4], sta->addr[5], sta_priv_dat->sta_idx, sta_info->hw_wsid);
|
#endif
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
{
|
ssv6xxx_debugfs_remove_sta(sc, sta_info);
|
}
|
#endif
|
memset(sta_info, 0, sizeof(*sta_info));
|
sta_priv_dat->sta_idx = -1;
|
list_del(&sta_priv_dat->list);
|
if (list_empty(&priv_vif->sta_list) && vif->type == NL80211_IFTYPE_STATION)
|
{
|
priv_vif->pair_cipher = 0;
|
priv_vif->group_cipher = 0;
|
}
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
#if 0
|
sta_info = sc->sta_info;
|
for(s=0; s<SSV_NUM_STA; s++, sta_info++) {
|
if (sta_info->s_flags & STA_FLAG_VALID)
|
continue;
|
if (sta_info->sta == sta &&
|
sta_info->vif == vif)
|
sta_info->s_flags = 0;
|
}
|
#endif
|
#ifndef FW_WSID_WATCH_LIST
|
if(hw_wsid != -1)
|
#else
|
if((hw_wsid != -1) && (hw_wsid < SSV_NUM_HW_STA))
|
#endif
|
SMAC_REG_WRITE(sc->sh, reg_wsid[hw_wsid], 0x00);
|
return 0;
|
}
|
static void ssv6200_sta_notify(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif,
|
enum sta_notify_cmd cmd,
|
struct ieee80211_sta *sta)
|
{
|
struct ssv_softc *sc = hw->priv;
|
struct ssv_vif_priv_data *priv_vif = (struct ssv_vif_priv_data *)vif->drv_priv;
|
struct ssv_sta_priv_data *sta_priv_dat =
|
sta != NULL ? (struct ssv_sta_priv_data *)sta->drv_priv : NULL;
|
struct ssv_sta_info *sta_info;
|
u32 bit, prev;
|
unsigned long flags;
|
#ifdef BROADCAST_DEBUG
|
#endif
|
spin_lock_irqsave(&sc->ps_state_lock, flags);
|
if (sta_priv_dat != NULL){
|
bit = BIT(sta_priv_dat->sta_idx);
|
prev = priv_vif->sta_asleep_mask & bit;
|
sta_info = sta_priv_dat->sta_info;
|
switch (cmd)
|
{
|
case STA_NOTIFY_SLEEP:
|
if(!prev)
|
{
|
sta_info->sleeping = true;
|
if ( (vif->type == NL80211_IFTYPE_AP)
|
&& sc->bq4_dtim
|
&& !priv_vif->sta_asleep_mask
|
&& ssv6200_bcast_queue_len(&sc->bcast_txq)){
|
printk("%s(): ssv6200_bcast_start\n", __FUNCTION__);
|
ssv6200_bcast_start(sc);
|
}
|
priv_vif->sta_asleep_mask |= bit;
|
}
|
break;
|
case STA_NOTIFY_AWAKE:
|
if(prev)
|
{
|
sta_info->sleeping = false;
|
priv_vif->sta_asleep_mask &= ~bit;
|
}
|
break;
|
default:
|
break;
|
}
|
}
|
spin_unlock_irqrestore(&sc->ps_state_lock, flags);
|
}
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
|
static u64 ssv6200_get_tsf(struct ieee80211_hw *hw)
|
#else
|
static u64 ssv6200_get_tsf(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif)
|
#endif
|
{
|
u64 time = jiffies*1000*1000/HZ;
|
//struct timeval tv;
|
|
//do_gettimeofday(&tv);
|
//time = tv.tv_sec * 1000 * 1000 + tv.tv_usec;
|
printk("%s(): time = %llu\n", __FUNCTION__, time);
|
return time;
|
//return 0;
|
}
|
|
static u64 ssv6200_get_systime_us(void)
|
{
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
|
struct timespec ts;
|
get_monotonic_boottime(&ts);
|
return ((u64)ts.tv_sec * 1000000) + ts.tv_nsec / 1000;
|
#else
|
struct timeval tv;
|
do_gettimeofday(&tv);
|
return ((u64)tv.tv_sec * 1000000) + tv.tv_usec;
|
#endif
|
}
|
|
static u32 pre_11b_cca_control;
|
static u32 pre_11b_cca_1;
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
|
static void ssv6200_sw_scan_start(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif, const u8 *mac_addr)
|
#else
|
static void ssv6200_sw_scan_start(struct ieee80211_hw *hw)
|
#endif
|
{
|
//#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
|
((struct ssv_softc *)(hw->priv))->bScanning = true;
|
//#endif
|
SMAC_REG_READ(((struct ssv_softc *)(hw->priv))->sh, ADR_RX_11B_CCA_CONTROL, &pre_11b_cca_control);
|
SMAC_REG_WRITE(((struct ssv_softc *)(hw->priv))->sh, ADR_RX_11B_CCA_CONTROL, 0x0);
|
SMAC_REG_READ(((struct ssv_softc *)(hw->priv))->sh, ADR_RX_11B_CCA_1, &pre_11b_cca_1);
|
SMAC_REG_WRITE(((struct ssv_softc *)(hw->priv))->sh, ADR_RX_11B_CCA_1, RX_11B_CCA_IN_SCAN);
|
#ifdef CONFIG_SSV_MRX_EN3_CTRL
|
SMAC_REG_WRITE(((struct ssv_softc *)(hw->priv))->sh, ADR_MRX_FLT_EN3, 0x0400);
|
#endif
|
//printk("--------------%s(): \n", __FUNCTION__);
|
}
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,0)
|
static void ssv6200_sw_scan_complete(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif)
|
#else
|
static void ssv6200_sw_scan_complete(struct ieee80211_hw *hw)
|
#endif
|
{
|
|
#ifdef CONFIG_SSV_MRX_EN3_CTRL
|
bool is_p2p_assoc;
|
#endif
|
((struct ssv_softc *)(hw->priv))->bScanning = false;
|
SMAC_REG_WRITE(((struct ssv_softc *)(hw->priv))->sh, ADR_RX_11B_CCA_CONTROL, pre_11b_cca_control);
|
SMAC_REG_WRITE(((struct ssv_softc *)(hw->priv))->sh, ADR_RX_11B_CCA_1, pre_11b_cca_1);
|
#ifdef CONFIG_SSV_MRX_EN3_CTRL
|
is_p2p_assoc = ((struct ssv_softc *)(hw->priv))->vif_info[1].vif->bss_conf.assoc;
|
if(((struct ssv_softc *)(hw->priv))->ps_aid != 0 && (!is_p2p_assoc))
|
SMAC_REG_WRITE(((struct ssv_softc *)(hw->priv))->sh, ADR_MRX_FLT_EN3, 0x1000);
|
#endif
|
//printk("==============%s(): \n", __FUNCTION__);
|
}
|
static int ssv6200_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
|
bool set)
|
{
|
struct ssv_softc *sc = hw->priv;
|
struct ssv_sta_info *sta_info = sta
|
? ((struct ssv_sta_priv_data *)sta->drv_priv)->sta_info
|
: NULL;
|
if (sta_info && (sta_info->tim_set^set))
|
{
|
#ifdef BROADCAST_DEBUG
|
printk("[I] [A] ssvcabrio_set_tim");
|
#endif
|
sta_info->tim_set = set;
|
queue_work(sc->config_wq, &sc->set_tim_work);
|
}
|
return 0;
|
}
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,2,0)
|
static int ssv6200_conf_tx(struct ieee80211_hw *hw, u16 queue,
|
const struct ieee80211_tx_queue_params *params)
|
#else
|
static int ssv6200_conf_tx(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif, u16 queue,
|
const struct ieee80211_tx_queue_params *params)
|
#endif
|
{
|
struct ssv_softc *sc = hw->priv;
|
u32 cw;
|
u8 hw_txqid = sc->tx.hw_txqid[queue];
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
|
struct ssv_vif_priv_data *priv_vif = (struct ssv_vif_priv_data *)vif->drv_priv;
|
printk("[I] sv6200_conf_tx vif[%d] qos[%d] queue[%d] aifsn[%d] cwmin[%d] cwmax[%d] txop[%d] \n",
|
priv_vif->vif_idx ,vif->bss_conf.qos, queue, params->aifs, params->cw_min, params->cw_max, params->txop);
|
#else
|
printk("[I] sv6200_conf_tx queue[%d] aifsn[%d] cwmin[%d] cwmax[%d] txop[%d] \n",
|
queue, params->aifs, params->cw_min, params->cw_max, params->txop);
|
#endif
|
if (queue > NL80211_TXQ_Q_BK)
|
return 1;
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
|
if (priv_vif->vif_idx != 0) {
|
dev_warn(sc->dev, "WMM setting applicable to primary interface only.\n");
|
return 1;
|
}
|
#endif
|
mutex_lock(&sc->mutex);
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
|
SMAC_REG_SET_BITS(sc->sh, ADR_GLBLE_SET,
|
(vif->bss_conf.qos<<QOS_EN_SFT), QOS_EN_MSK);
|
#endif
|
#if 0
|
{
|
cw = 0x4;
|
SMAC_REG_WRITE(sc->sh, ADR_TXQ0_MTX_Q_MISC_EN+0x100*hw_txqid, cw);
|
cw = 0x0;
|
SMAC_REG_WRITE(sc->sh, ADR_TXQ0_MTX_Q_BKF_CNT+0x100*hw_txqid, cw);
|
}
|
#endif
|
#if 1
|
cw = (params->aifs-1)&0xf;
|
#else
|
cw = params->aifs&0xf;
|
#endif
|
cw|= ((ilog2(params->cw_min+1))&0xf)<<TXQ1_MTX_Q_ECWMIN_SFT;
|
cw|= ((ilog2(params->cw_max+1))&0xf)<<TXQ1_MTX_Q_ECWMAX_SFT;
|
cw|= ((params->txop)&0xff)<<TXQ1_MTX_Q_TXOP_LIMIT_SFT;
|
SMAC_REG_WRITE(sc->sh, ADR_TXQ0_MTX_Q_AIFSN+0x100*hw_txqid, cw);
|
mutex_unlock(&sc->mutex);
|
return 0;
|
}
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
|
static int ssv6200_ampdu_action(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif,
|
enum ieee80211_ampdu_mlme_action action,
|
struct ieee80211_sta *sta,
|
u16 tid, u16 *ssn)
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0) && LINUX_VERSION_CODE < KERNEL_VERSION(4,4,0)
|
static int ssv6200_ampdu_action(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif,
|
enum ieee80211_ampdu_mlme_action action,
|
struct ieee80211_sta *sta,
|
u16 tid, u16 *ssn, u8 buf_size)
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0) && LINUX_VERSION_CODE < KERNEL_VERSION(4,4,69)
|
static int ssv6200_ampdu_action(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif,
|
enum ieee80211_ampdu_mlme_action action,
|
struct ieee80211_sta *sta,
|
u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
|
#else
|
static int ssv6200_ampdu_action(struct ieee80211_hw *hw,
|
struct ieee80211_vif *vif,
|
struct ieee80211_ampdu_params *params)
|
#endif
|
{
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0)
|
u8 buf_size = 32;
|
#endif
|
struct ssv_softc *sc = hw->priv;
|
int ret = 0;
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,69)
|
struct ieee80211_sta *sta = params->sta;
|
enum ieee80211_ampdu_mlme_action action = params->action;
|
u16 tid = params->tid;
|
u16 *ssn = &(params->ssn);
|
u8 buf_size = params->buf_size;
|
#endif
|
if(sta == NULL)
|
return ret;
|
#if (!Enable_AMPDU_Rx)
|
if(action == IEEE80211_AMPDU_RX_START || action == IEEE80211_AMPDU_RX_STOP )
|
{
|
ampdu_db_log("Disable AMPDU_RX for test(1).\n");
|
return -EOPNOTSUPP;
|
}
|
#endif
|
#if (!Enable_AMPDU_Tx)
|
if(action == IEEE80211_AMPDU_TX_START || action == IEEE80211_AMPDU_TX_STOP || action == IEEE80211_AMPDU_TX_OPERATIONAL )
|
{
|
ampdu_db_log("Disable AMPDU_TX for test(1).\n");
|
return -EOPNOTSUPP;
|
}
|
#endif
|
if((action == IEEE80211_AMPDU_RX_START || action == IEEE80211_AMPDU_RX_STOP ) &&
|
(!(sc->sh->cfg.hw_caps & SSV6200_HW_CAP_AMPDU_RX)))
|
{
|
ampdu_db_log("Disable AMPDU_RX(2).\n");
|
return -EOPNOTSUPP;
|
}
|
if( ( action == IEEE80211_AMPDU_TX_START
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
|| action == IEEE80211_AMPDU_TX_STOP
|
#else
|
|| action == IEEE80211_AMPDU_TX_STOP_CONT
|
|| action == IEEE80211_AMPDU_TX_STOP_FLUSH
|
|| action == IEEE80211_AMPDU_TX_STOP_FLUSH_CONT
|
#endif
|
|| action == IEEE80211_AMPDU_TX_OPERATIONAL )
|
&& (!(sc->sh->cfg.hw_caps & SSV6200_HW_CAP_AMPDU_TX)))
|
{
|
ampdu_db_log("Disable AMPDU_TX(2).\n");
|
return -EOPNOTSUPP;
|
}
|
switch (action)
|
{
|
case IEEE80211_AMPDU_RX_START:
|
#ifdef WIFI_CERTIFIED
|
if (sc->rx_ba_session_count >= SSV6200_RX_BA_MAX_SESSIONS)
|
{
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0)
|
ieee80211_stop_rx_ba_session(vif,
|
(1<<(sc->ba_tid)),
|
sc->ba_ra_addr);
|
#endif
|
sc->rx_ba_session_count--;
|
}
|
#else
|
if ((sc->rx_ba_session_count >= SSV6200_RX_BA_MAX_SESSIONS) && (sc->rx_ba_sta != sta))
|
{
|
ret = -EBUSY;
|
break;
|
}
|
else if ((sc->rx_ba_session_count >= SSV6200_RX_BA_MAX_SESSIONS) && (sc->rx_ba_sta == sta))
|
{
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(3,1,0)
|
ieee80211_stop_rx_ba_session(vif,(1<<(sc->ba_tid)),sc->ba_ra_addr);
|
#endif
|
sc->rx_ba_session_count--;
|
}
|
#endif
|
printk(KERN_ERR "IEEE80211_AMPDU_RX_START %02X:%02X:%02X:%02X:%02X:%02X %d.\n",
|
sta->addr[0], sta->addr[1], sta->addr[2], sta->addr[3],
|
sta->addr[4], sta->addr[5], tid);
|
sc->rx_ba_session_count++;
|
sc->rx_ba_sta = sta;
|
sc->ba_tid = tid;
|
sc->ba_ssn = *ssn;
|
memcpy(sc->ba_ra_addr, sta->addr, ETH_ALEN);
|
queue_work(sc->config_wq, &sc->set_ampdu_rx_add_work);
|
break;
|
case IEEE80211_AMPDU_RX_STOP:
|
sc->rx_ba_session_count--;
|
if (sc->rx_ba_session_count == 0)
|
sc->rx_ba_sta = NULL;
|
queue_work(sc->config_wq, &sc->set_ampdu_rx_del_work);
|
break;
|
case IEEE80211_AMPDU_TX_START:
|
printk(KERN_ERR "AMPDU_TX_START %02X:%02X:%02X:%02X:%02X:%02X %d.\n",
|
sta->addr[0], sta->addr[1], sta->addr[2], sta->addr[3],
|
sta->addr[4], sta->addr[5], tid);
|
ssv6200_ampdu_tx_start(tid, sta, hw, ssn);
|
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
|
break;
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
case IEEE80211_AMPDU_TX_STOP:
|
#else
|
case IEEE80211_AMPDU_TX_STOP_CONT:
|
case IEEE80211_AMPDU_TX_STOP_FLUSH:
|
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
|
#endif
|
printk(KERN_ERR "AMPDU_TX_STOP %02X:%02X:%02X:%02X:%02X:%02X %d.\n",
|
sta->addr[0], sta->addr[1], sta->addr[2], sta->addr[3],
|
sta->addr[4], sta->addr[5], tid);
|
ssv6200_ampdu_tx_stop(tid, sta, hw);
|
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
|
break;
|
case IEEE80211_AMPDU_TX_OPERATIONAL:
|
printk(KERN_ERR "AMPDU_TX_OPERATIONAL %02X:%02X:%02X:%02X:%02X:%02X %d.\n",
|
sta->addr[0], sta->addr[1], sta->addr[2], sta->addr[3],
|
sta->addr[4], sta->addr[5], tid);
|
ssv6200_ampdu_tx_operation(tid, sta, hw, buf_size);
|
break;
|
default:
|
ret = -EOPNOTSUPP;
|
break;
|
}
|
return ret;
|
}
|
#ifdef CONFIG_PM
|
int ssv6xxx_suspend (struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
{
|
printk("ssv6xxx_suspend \n");
|
return 0;
|
}
|
int ssv6xxx_resume (struct ieee80211_hw *hw)
|
{
|
printk("ssv6xxx_resume \n");
|
return 0;
|
}
|
#endif
|
struct ieee80211_ops ssv6200_ops =
|
{
|
.tx = ssv6200_tx,
|
.start = ssv6200_start,
|
.stop = ssv6200_stop,
|
.add_interface = ssv6200_add_interface,
|
.remove_interface = ssv6200_remove_interface,
|
.change_interface = ssv6200_change_interface,
|
.config = ssv6200_config,
|
.configure_filter = ssv6200_config_filter,
|
.bss_info_changed = ssv6200_bss_info_changed,
|
.sta_add = ssv6200_sta_add,
|
.sta_remove = ssv6200_sta_remove,
|
.sta_notify = ssv6200_sta_notify,
|
.set_key = ssv6200_set_key,
|
.sw_scan_start = ssv6200_sw_scan_start,
|
.sw_scan_complete = ssv6200_sw_scan_complete,
|
.get_tsf = ssv6200_get_tsf,
|
.set_tim = ssv6200_set_tim,
|
.conf_tx = ssv6200_conf_tx,
|
.ampdu_action = ssv6200_ampdu_action,
|
#ifdef CONFIG_PM
|
.suspend = ssv6xxx_suspend,
|
.resume = ssv6xxx_resume,
|
#endif
|
};
|
#ifdef USE_LOCAL_CRYPTO
|
#ifdef MULTI_THREAD_ENCRYPT
|
struct ssv_crypto_data * ssv6xxx_skb_get_tx_cryptops(struct sk_buff *mpdu)
|
{
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(mpdu);
|
struct SKB_info_st *skb_info = (struct SKB_info_st *)mpdu->head;
|
struct ieee80211_sta *sta = skb_info->sta;
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mpdu->data;
|
u32 unicast = (is_multicast_ether_addr(hdr->addr1))? 0: 1;
|
struct ssv_sta_priv_data *sta_priv_dat = NULL;
|
BUG_ON((size_t)info < (size_t)0x01000);
|
if (unicast)
|
{
|
if (sta)
|
{
|
sta_priv_dat = (struct ssv_sta_priv_data *)sta->drv_priv;
|
return &sta_priv_dat->crypto_data;
|
}
|
else
|
{
|
printk(KERN_ERR
|
"Unicast to NULL STA frame. "
|
"%02X:%02X:%02X:%02X:%02X:%02X -> %02X:%02X:%02X:%02X:%02X:%02X)\n",
|
hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
|
hdr->addr1[3], hdr->addr1[4], hdr->addr1[5],
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
|
return NULL;
|
}
|
}
|
else
|
{
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)info->control.vif->drv_priv;
|
return &vif_priv->crypto_data;
|
}
|
}
|
int ssv6xxx_skb_pre_encrypt(struct sk_buff *mpdu, struct ssv_softc *sc)
|
{
|
struct ssv_crypto_data *crypto_data = NULL;
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mpdu->data;
|
int ret = -1;
|
crypto_data = ssv6xxx_skb_get_tx_cryptops(mpdu);
|
if (crypto_data == NULL)
|
return -EOPNOTSUPP;
|
START_READ_CRYPTO_DATA(crypto_data);
|
if ((crypto_data->ops != NULL) && (crypto_data->ops->encrypt_prepare != NULL))
|
{
|
u32 hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
ret = crypto_data->ops->encrypt_prepare(mpdu, hdrlen, crypto_data->priv);
|
}
|
else
|
{
|
ret = -EOPNOTSUPP;
|
}
|
END_READ_CRYPTO_DATA(crypto_data);
|
return ret;
|
}
|
int ssv6xxx_skb_pre_decrypt(struct sk_buff *mpdu, struct ieee80211_sta *sta, struct ssv_softc *sc)
|
{
|
struct ssv_crypto_data *crypto_data = NULL;
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mpdu->data;
|
int ret = -1;
|
crypto_data = ssv6xxx_skb_get_rx_cryptops(sc, sta, mpdu);
|
if (crypto_data == NULL)
|
return -EOPNOTSUPP;
|
START_READ_CRYPTO_DATA(crypto_data);
|
if ((crypto_data->ops != NULL) && (crypto_data->ops->decrypt_prepare != NULL))
|
{
|
u32 hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
ret = crypto_data->ops->decrypt_prepare(mpdu, hdrlen, crypto_data->priv);
|
}
|
else
|
{
|
ret = -EOPNOTSUPP;
|
}
|
END_READ_CRYPTO_DATA(crypto_data);
|
return ret;
|
}
|
#endif
|
int ssv6xxx_skb_encrypt(struct sk_buff *mpdu, struct ssv_softc *sc)
|
{
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mpdu->data;
|
int ret = 0;
|
#ifndef MULTI_THREAD_ENCRYPT
|
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(mpdu);
|
struct SKB_info_st *skb_info = (struct SKB_info_st *)mpdu->head;
|
struct ieee80211_sta *sta = skb_info->sta;
|
struct ssv_sta_priv_data *sta_priv_dat = NULL;
|
#endif
|
struct ssv_crypto_data *crypto_data = NULL;
|
#ifndef MULTI_THREAD_ENCRYPT
|
if (sta || unicast)
|
{
|
sta_priv_dat = (struct ssv_sta_priv_data *)sta->drv_priv;
|
crypto_data = &sta_priv_data->crypto_data;
|
}
|
else
|
{
|
struct ssv_vif_priv_data *vif_priv = (struct ssv_vif_priv_data *)tx_info->control.vif->drv_priv;
|
crypto_data = &vif_priv->crypto_data;
|
}
|
#else
|
crypto_data = ssv6xxx_skb_get_tx_cryptops(mpdu);
|
#endif
|
START_READ_CRYPTO_DATA(crypto_data);
|
if ((crypto_data == NULL) || (crypto_data->ops == NULL) || (crypto_data->priv == NULL))
|
{
|
#if 0
|
u32 unicast = (is_multicast_ether_addr(hdr->addr1))? 0: 1;
|
dev_err(sc->dev, "[Local Crypto]: Encrypt %c %d %02X:%02X:%02X:%02X:%02X:%02X with NULL crypto.\n",
|
unicast ? 'U' : 'B', mpdu->protocol,
|
hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
|
hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
|
#endif
|
ret = -1;
|
}
|
else
|
{
|
u32 hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
ret = crypto_data->ops->encrypt_mpdu(mpdu, hdrlen, crypto_data->priv);
|
}
|
END_READ_CRYPTO_DATA(crypto_data);
|
return ret;
|
}
|
struct ssv_crypto_data *ssv6xxx_skb_get_rx_cryptops(struct ssv_softc *sc, struct ieee80211_sta *sta, struct sk_buff *mpdu)
|
{
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mpdu->data;
|
struct ssv_sta_priv_data *sta_priv;
|
struct ssv_vif_priv_data *vif_priv;
|
struct ssv_vif_info *vif_info;
|
u32 unicast = 0;
|
if(sta == NULL)
|
{
|
printk("No sta, fail to get rx cryptops\n");
|
return NULL;
|
}
|
sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
vif_priv = (struct ssv_vif_priv_data *)sta_priv->sta_info->vif->drv_priv;
|
vif_info = &sc->vif_info[vif_priv->vif_idx];
|
if (vif_info->if_type == NL80211_IFTYPE_STATION)
|
unicast = (is_multicast_ether_addr(hdr->addr1))?0:1;
|
if((sta->drv_priv != NULL) && (vif_info->if_type == NL80211_IFTYPE_AP))
|
{
|
return &sta_priv->crypto_data;
|
}
|
else if((sta->drv_priv != NULL) && (unicast == 1))
|
{
|
return &sta_priv->crypto_data;
|
}
|
else if((unicast != 1) && (vif_priv != NULL))
|
{
|
return &vif_priv->crypto_data;
|
}
|
else
|
{
|
printk("[Local Crypto]: No useful drv_priv, sta = %p, unicast = %d, vif_priv = %p", sta, unicast, vif_priv);
|
if(sta != NULL)
|
printk(", sta_priv = %p", sta->drv_priv);
|
printk("\n");
|
}
|
return NULL;
|
}
|
int ssv6xxx_skb_decrypt(struct sk_buff *mpdu, struct ieee80211_sta *sta, struct ssv_softc *sc)
|
{
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)mpdu->data;
|
struct ssv_crypto_data *crypto_data = NULL;
|
u32 hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
crypto_data = ssv6xxx_skb_get_rx_cryptops(sc, sta, mpdu);
|
if (crypto_data != NULL)
|
{
|
int ret = -1;
|
START_READ_CRYPTO_DATA(crypto_data);
|
if ((crypto_data->ops != NULL) && (crypto_data->priv != NULL))
|
ret = crypto_data->ops->decrypt_mpdu(mpdu, hdrlen, crypto_data->priv);
|
END_READ_CRYPTO_DATA(crypto_data);
|
return ret;
|
}
|
printk("[Local Crypto]: crytp is null\n");
|
return -1;
|
}
|
#endif
|
int ssv6200_tx_flow_control(void *dev, int hw_txqid, bool fc_en,int debug)
|
{
|
struct ssv_softc *sc=dev;
|
int ac;
|
BUG_ON(hw_txqid > 4);
|
if (hw_txqid == 4)
|
return 0;
|
ac = sc->tx.ac_txqid[hw_txqid];
|
if (fc_en == false) {
|
if (sc->tx.flow_ctrl_status & (1<<ac)) {
|
ieee80211_wake_queue(sc->hw, ac);
|
sc->tx.flow_ctrl_status &= ~(1<<ac);
|
} else {
|
}
|
}
|
else {
|
if ((sc->tx.flow_ctrl_status & (1<<ac))==0) {
|
ieee80211_stop_queue(sc->hw, ac);
|
sc->tx.flow_ctrl_status |= (1<<ac);
|
} else {
|
}
|
}
|
return 0;
|
}
|
void ssv6xxx_tx_q_empty_cb (u32 txq_no, void *cb_data)
|
{
|
struct ssv_softc *sc = cb_data;
|
BUG_ON(sc == NULL);
|
sc->tx_q_empty = true;
|
smp_mb();
|
wake_up_interruptible(&sc->tx_wait_q);
|
}
|
struct ssv6xxx_b_cca_control {
|
u32 down_level;
|
u32 upper_level;
|
u32 adjust_cca_control;
|
u32 adjust_cca_1;
|
};
|
struct ssv6xxx_b_cca_control adjust_cci[] = {
|
{ 0 , 43, 0x00162000, 0x20380050},
|
{ 40, 48, 0x00161000, 0x20380050},
|
{ 45, 53, 0x00160800, 0x20380050},
|
{ 50, 63, 0x00160400, 0x20380050},
|
{ 60, 68, 0x00160200, 0x20380050},
|
{ 65, 73, 0x00160100, 0x20380050},
|
{ 70, 128,0x00000000, 0x20300050},
|
};
|
#define MAX_CCI_LEVEL 128
|
static unsigned long last_jiffies = INITIAL_JIFFIES;
|
static s32 size = sizeof(adjust_cci)/sizeof(adjust_cci[0]);
|
static u32 current_level = MAX_CCI_LEVEL;
|
static u32 current_gate = (sizeof(adjust_cci)/sizeof(adjust_cci[0])) - 1;
|
void mitigate_cci(struct ssv_softc *sc, u32 input_level)
|
{
|
s32 i;
|
if(input_level > MAX_CCI_LEVEL) {
|
printk("mitigate_cci input error[%d]!!\n",input_level);
|
return;
|
}
|
if (time_after(jiffies, last_jiffies + msecs_to_jiffies(3000))) {
|
#ifdef DEBUG_MITIGATE_CCI
|
printk("jiffies=%lu, input_level=%d\n", jiffies, input_level);
|
#endif
|
last_jiffies = jiffies;
|
if(( input_level >= adjust_cci[current_gate].down_level) && (input_level <= adjust_cci[current_gate].upper_level)) {
|
current_level = input_level;
|
#ifdef DEBUG_MITIGATE_CCI
|
printk("Keep the 0xce0020a0[%x] 0xce002008[%x]!!\n"
|
,adjust_cci[current_gate].adjust_cca_control,adjust_cci[current_gate].adjust_cca_1);
|
#endif
|
}
|
else {
|
if(current_level < input_level) {
|
for (i = 0; i < size; i++) {
|
if (input_level <= adjust_cci[i].upper_level) {
|
#ifdef DEBUG_MITIGATE_CCI
|
printk("gate=%d, input_level=%d, adjust_cci[%d].upper_level=%d, value=%08x\n",
|
current_gate, input_level, i, adjust_cci[i].upper_level, adjust_cci[i].adjust_cca_control);
|
#endif
|
current_level = input_level;
|
current_gate = i;
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_CONTROL, adjust_cci[i].adjust_cca_control);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_1, adjust_cci[i].adjust_cca_1);
|
#ifdef DEBUG_MITIGATE_CCI
|
printk("##Set to the 0xce0020a0[%x] 0xce002008[%x]##!!\n"
|
,adjust_cci[current_gate].adjust_cca_control,adjust_cci[current_gate].adjust_cca_1);
|
#endif
|
return;
|
}
|
}
|
}
|
else {
|
for (i = (size -1); i >= 0; i--) {
|
if (input_level >= adjust_cci[i].down_level) {
|
#ifdef DEBUG_MITIGATE_CCI
|
printk("gate=%d, input_level=%d, adjust_cci[%d].down_level=%d, value=%08x\n",
|
current_gate, input_level, i, adjust_cci[i].down_level, adjust_cci[i].adjust_cca_control);
|
#endif
|
current_level = input_level;
|
current_gate = i;
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_CONTROL, adjust_cci[i].adjust_cca_control);
|
SMAC_REG_WRITE(sc->sh, ADR_RX_11B_CCA_1, adjust_cci[i].adjust_cca_1);
|
#ifdef DEBUG_MITIGATE_CCI
|
printk("##Set to the 0xce0020a0[%x] 0xce002008[%x]##!!\n"
|
,adjust_cci[current_gate].adjust_cca_control,adjust_cci[current_gate].adjust_cca_1);
|
#endif
|
return;
|
}
|
}
|
}
|
}
|
}
|
}
|
#define RSSI_SMOOTHING_SHIFT 5
|
#define RSSI_DECIMAL_POINT_SHIFT 6
|
#ifdef CONFIG_SSV_SUPPORT_ANDROID
|
extern void ssv6xxx_send_deauth_toself(struct ssv_softc *sc,const u8 *bssid,const u8 *self_addr);
|
#endif
|
static void _proc_data_rx_skb (struct ssv_softc *sc, struct sk_buff *rx_skb)
|
{
|
struct ieee80211_rx_status *rxs;
|
struct ieee80211_hdr *hdr;
|
__le16 fc;
|
struct ssv6200_rx_desc *rxdesc;
|
struct ssv6200_rxphy_info_padding *rxphypad;
|
struct ssv6200_rxphy_info *rxphy;
|
struct ieee80211_channel *chan;
|
struct ieee80211_vif *vif = NULL;
|
struct ieee80211_sta *sta = NULL;
|
bool rx_hw_dec = false;
|
bool do_sw_dec = false;
|
struct ssv_sta_priv_data *sta_priv = NULL;
|
struct ssv_vif_priv_data *vif_priv = NULL;
|
SKB_info *skb_info = NULL;
|
#ifdef CONFIG_SSV_SUPPORT_ANDROID
|
const u8 *p = NULL;
|
struct ieee80211_mgmt *mgmt = NULL;
|
static u8 diff_channel_cnt = 0;
|
#endif
|
u8 is_beacon;
|
u8 is_probe_resp;
|
#ifdef CONFIG_SSV_RSSI
|
s32 found = 0;
|
#endif
|
#ifdef USE_LOCAL_CRYPTO
|
int ret = 0;
|
#ifdef MULTI_THREAD_ENCRYPT
|
struct ssv_encrypt_task_list *ta = NULL;
|
unsigned long flags;
|
#endif
|
#endif
|
#ifdef CONFIG_SSV_SMARTLINK
|
{
|
extern int ksmartlink_smartlink_started(void);
|
void smartlink_nl_send_msg(struct sk_buff *skb);
|
if (unlikely(ksmartlink_smartlink_started()))
|
{
|
skb_pull(rx_skb, SSV6XXX_RX_DESC_LEN);
|
skb_trim(rx_skb, rx_skb->len-sc->sh->rx_pinfo_pad);
|
smartlink_nl_send_msg(rx_skb);
|
return;
|
}
|
}
|
#endif
|
rxdesc = (struct ssv6200_rx_desc *)rx_skb->data;
|
rxphy = (struct ssv6200_rxphy_info *)(rx_skb->data + sizeof(*rxdesc));
|
rxphypad = (struct ssv6200_rxphy_info_padding *)(rx_skb->data + rx_skb->len - sizeof(struct ssv6200_rxphy_info_padding));
|
hdr = (struct ieee80211_hdr *)(rx_skb->data + SSV6XXX_RX_DESC_LEN);
|
fc = hdr->frame_control;
|
skb_info = (SKB_info *)rx_skb->head;
|
if (rxdesc->wsid >= SSV_RC_MAX_HARDWARE_SUPPORT)
|
{
|
if ( (ieee80211_is_data(hdr->frame_control))
|
&& (!(ieee80211_is_nullfunc(hdr->frame_control))))
|
{
|
ssv6xxx_rc_rx_data_handler(sc->hw, rx_skb, rxdesc->rate_idx);
|
}
|
}
|
rxs = IEEE80211_SKB_RXCB(rx_skb);
|
memset(rxs, 0, sizeof(struct ieee80211_rx_status));
|
ssv6xxx_rc_mac8011_rate_idx(sc, rxdesc->rate_idx, rxs);
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
|
// printk("mactime=%u, len=%d\r\n", *((u32 *)&rx_skb->data[28]), rxdesc->len); //+++
|
rxs->mactime = *((u32 *)&rx_skb->data[28]);
|
#endif
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
chan = sc->hw->conf.channel;
|
#else
|
chan = sc->hw->conf.chandef.chan;
|
#endif
|
rxs->band = chan->band;
|
rxs->freq = chan->center_freq;
|
rxs->antenna = 1;
|
is_beacon = ieee80211_is_beacon(hdr->frame_control);
|
is_probe_resp = ieee80211_is_probe_resp(hdr->frame_control);
|
if (is_beacon) //+++
|
{
|
struct ieee80211_mgmt *mgmt_tmp = NULL;
|
mgmt_tmp = (struct ieee80211_mgmt *)(rx_skb->data + SSV6XXX_RX_DESC_LEN);
|
mgmt_tmp->u.beacon.timestamp = cpu_to_le64(ssv6200_get_systime_us());
|
}
|
if (is_probe_resp)
|
{
|
struct ieee80211_mgmt *mgmt_tmp = NULL;
|
mgmt_tmp = (struct ieee80211_mgmt *)(rx_skb->data + SSV6XXX_RX_DESC_LEN);
|
mgmt_tmp->u.probe_resp.timestamp = cpu_to_le64(ssv6200_get_systime_us());
|
}
|
|
if (rxdesc->rate_idx < SSV62XX_G_RATE_INDEX && rxphypad->RSVD == 0)
|
{
|
if (is_beacon || is_probe_resp)
|
{
|
sta = ssv6xxx_find_sta_by_rx_skb(sc, rx_skb);
|
if(sta)
|
{
|
sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
#ifdef SSV_RSSI_DEBUG
|
printk(KERN_DEBUG "b_beacon %02X:%02X:%02X:%02X:%02X:%02X rssi=%d, snr=%d\n",
|
hdr->addr2[0], hdr->addr2[1],hdr->addr2[2], hdr->addr2[3],
|
hdr->addr2[4], hdr->addr2[5],rxphypad->rpci, rxphypad->snr);
|
#endif
|
#ifdef CONFIG_SSV_SUPPORT_ANDROID
|
if(is_beacon)
|
{
|
mgmt = (struct ieee80211_mgmt *)(rx_skb->data + SSV6XXX_RX_DESC_LEN);
|
p = cfg80211_find_ie(WLAN_EID_DS_PARAMS, mgmt->u.beacon.variable, rx_skb->len);
|
if (p)
|
{
|
u32 beacon_channel = (int)p[2];
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
struct ieee80211_channel *chan;
|
u32 bss_chan_hw_value;
|
chan = sc->hw->conf.channel;
|
bss_chan_hw_value = chan->hw_value;
|
#else
|
u32 bss_chan_hw_value = sc->vif_info[0].vif->bss_conf.chandef.chan->hw_value;
|
#endif
|
if( beacon_channel != bss_chan_hw_value )
|
{
|
diff_channel_cnt++;
|
if(diff_channel_cnt >= 25)
|
{
|
printk(KERN_DEBUG "ssv6xxx_send_deauth_toself by channel change\n");
|
ssv6xxx_send_deauth_toself(sc, sc->vif_info[0].vif->bss_conf.bssid, sc->vif_info[0].vif->addr);
|
diff_channel_cnt = 0;
|
}
|
}
|
else
|
diff_channel_cnt = 0;
|
}
|
}
|
#endif
|
if(sta_priv->beacon_rssi)
|
{
|
sta_priv->beacon_rssi = ((rxphypad->rpci << RSSI_DECIMAL_POINT_SHIFT)
|
+ ((sta_priv->beacon_rssi<<RSSI_SMOOTHING_SHIFT) - sta_priv->beacon_rssi)) >> RSSI_SMOOTHING_SHIFT;
|
rxphypad->rpci = (sta_priv->beacon_rssi >> RSSI_DECIMAL_POINT_SHIFT);
|
}
|
else
|
sta_priv->beacon_rssi = (rxphypad->rpci << RSSI_DECIMAL_POINT_SHIFT);
|
#ifdef SSV_RSSI_DEBUG
|
printk("Beacon smoothing RSSI %d\n",rxphypad->rpci);
|
#endif
|
mitigate_cci(sc, rxphypad->rpci);
|
}
|
#ifdef CONFIG_SSV_RSSI
|
else {
|
mutex_lock(&sc->mutex);
|
list_for_each_entry(p_rssi_res, &rssi_res.rssi_list, rssi_list) {
|
if (!memcmp(p_rssi_res->bssid, hdr->addr2, ETH_ALEN)) {
|
{
|
p_rssi_res->rssi = ((rxphypad->rpci << RSSI_DECIMAL_POINT_SHIFT)
|
+ ((p_rssi_res->rssi<<RSSI_SMOOTHING_SHIFT) - p_rssi_res->rssi)) >> RSSI_SMOOTHING_SHIFT;
|
rxphypad->rpci = (p_rssi_res->rssi >> RSSI_DECIMAL_POINT_SHIFT);
|
}
|
p_rssi_res->cache_jiffies = jiffies;
|
found = 1;
|
break;
|
}
|
else {
|
if(p_rssi_res->rssi) {
|
if (time_after(jiffies, p_rssi_res->cache_jiffies + msecs_to_jiffies(40000))) {
|
p_rssi_res->timeout = 1;
|
}
|
}
|
}
|
}
|
if (!found) {
|
p_rssi_res = kmalloc(sizeof(struct rssi_res_st), GFP_KERNEL);
|
memcpy(p_rssi_res->bssid, hdr->addr2, ETH_ALEN);
|
p_rssi_res->cache_jiffies = jiffies;
|
p_rssi_res->rssi = (rxphypad->rpci << RSSI_DECIMAL_POINT_SHIFT);
|
p_rssi_res->timeout = 0;
|
INIT_LIST_HEAD(&p_rssi_res->rssi_list);
|
list_add_tail_rcu(&(p_rssi_res->rssi_list), &(rssi_res.rssi_list));
|
}
|
mutex_unlock(&sc->mutex);
|
}
|
#endif
|
if(rxphypad->rpci > 88)
|
rxphypad->rpci = 88;
|
#if 0
|
printk("beacon %02X:%02X:%02X:%02X:%02X:%02X rxphypad-rpci=%d RxResult=%x wsid=%x\n",
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], rxphypad->rpci, rxdesc->RxResult, rxdesc->wsid);
|
#endif
|
}
|
if(sc->sh->cfg.rssi_ctl){
|
rxs->signal = (-rxphypad->rpci) + sc->sh->cfg.rssi_ctl;
|
}
|
else{
|
rxs->signal = (-rxphypad->rpci);
|
}
|
}
|
else if (rxdesc->rate_idx >= SSV62XX_G_RATE_INDEX && rxphy->service == 0)
|
{
|
if (is_beacon || is_probe_resp)
|
{
|
sta = ssv6xxx_find_sta_by_rx_skb(sc, rx_skb);
|
if(sta)
|
{
|
sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
#ifdef SSV_RSSI_DEBUG
|
printk("gn_beacon %02X:%02X:%02X:%02X:%02X:%02X rssi=%d, snr=%d\n",
|
hdr->addr2[0], hdr->addr2[1],hdr->addr2[2], hdr->addr2[3],
|
hdr->addr2[4], hdr->addr2[5],rxphy->rpci, rxphy->snr);
|
#endif
|
if(sta_priv->beacon_rssi)
|
{
|
sta_priv->beacon_rssi = ((rxphy->rpci << RSSI_DECIMAL_POINT_SHIFT)
|
+ ((sta_priv->beacon_rssi<<RSSI_SMOOTHING_SHIFT) - sta_priv->beacon_rssi)) >> RSSI_SMOOTHING_SHIFT;
|
rxphy->rpci = (sta_priv->beacon_rssi >> RSSI_DECIMAL_POINT_SHIFT);
|
}
|
else
|
sta_priv->beacon_rssi = (rxphy->rpci << RSSI_DECIMAL_POINT_SHIFT);
|
#ifdef SSV_RSSI_DEBUG
|
printk("Beacon smoothing RSSI %d\n",rxphy->rpci);
|
#endif
|
}
|
if(rxphy->rpci > 88)
|
rxphy->rpci = 88;
|
#if 0
|
printk("beacon %02X:%02X:%02X:%02X:%02X:%02X rxphy-rpci=%d RxResult=%x wsid=%x\n",
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], rxphy->rpci, rxdesc->RxResult, rxdesc->wsid);
|
#endif
|
}
|
if(sc->sh->cfg.rssi_ctl){
|
rxs->signal = (-rxphy->rpci) + sc->sh->cfg.rssi_ctl;
|
}else{
|
rxs->signal = (-rxphy->rpci);
|
}
|
}
|
else {
|
#ifdef SSV_RSSI_DEBUG
|
printk("########unicast: %d, b_rssi/snr: %d/%d, gn_rssi/snr: %d/%d, rate:%d###############\n",
|
rxdesc->unicast, (-rxphy->rpci), rxphy->snr, (-rxphypad->rpci), rxphypad->snr, rxdesc->rate_idx);
|
printk("RSSI, %d, rate_idx, %d\n", rxs->signal, rxdesc->rate_idx);
|
printk("rxdesc->RxResult = %x,rxdesc->wsid = %d\n",rxdesc->RxResult,rxdesc->wsid);
|
#endif
|
sta = ssv6xxx_find_sta_by_rx_skb(sc, rx_skb);
|
if(sta)
|
{
|
sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
rxs->signal = -(sta_priv->beacon_rssi >> RSSI_DECIMAL_POINT_SHIFT);
|
}
|
#ifdef SSV_RSSI_DEBUG
|
printk("Others signal %d\n",rxs->signal);
|
#endif
|
}
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,7,0)
|
rxs->flag = RX_FLAG_MACTIME_MPDU;
|
#else
|
// rxs->flag = RX_FLAG_MACTIME_START; //+++
|
#endif
|
rxs->rx_flags = 0;
|
#endif
|
#if LINUX_VERSION_CODE >= 0x030400
|
if (rxphy->aggregate)
|
rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
|
#endif
|
sc->hw_mng_used = rxdesc->mng_used;
|
if ( (ieee80211_is_data(fc) || ieee80211_is_data_qos(fc))
|
&& ieee80211_has_protected(fc))
|
{
|
sta = ssv6xxx_find_sta_by_rx_skb(sc, rx_skb);
|
if (sta == NULL)
|
goto drop_rx;
|
sta_priv = (struct ssv_sta_priv_data *)sta->drv_priv;
|
vif = sta_priv->sta_info->vif;
|
if (vif == NULL)
|
goto drop_rx;
|
if (is_broadcast_ether_addr(hdr->addr1))
|
{
|
vif_priv = (struct ssv_vif_priv_data *)vif->drv_priv;
|
rx_hw_dec = vif_priv->has_hw_decrypt;
|
do_sw_dec = vif_priv->need_sw_decrypt;
|
}
|
else
|
{
|
rx_hw_dec = sta_priv->has_hw_decrypt;
|
do_sw_dec = sta_priv->need_sw_decrypt;
|
}
|
#if 0
|
if (rx_count++ < 20)
|
{
|
printk(KERN_ERR "HW DEC (%d - %d) %d %02X:%02X:%02X:%02X:%02X:%02X\n",
|
rx_hw_dec, do_sw_dec, rxdesc->wsid,
|
hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
|
hdr->addr1[3], hdr->addr1[4], hdr->addr1[5]);
|
_ssv6xxx_hexdump("M ", (const u8 *)rx_skb->data, (rx_skb->len > 128) ? 128 : rx_skb->len);
|
}
|
#endif
|
#if 0
|
dev_err(sc->dev, "R %02X:%02X:%02X:%02X:%02X:%02X %d %d\n",
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
|
rx_hw_dec, do_sw_dec);
|
_ssv6xxx_hexdump("R ", (const u8 *)rx_skb->data,
|
(rx_skb->len > 128) ? 128 : rx_skb->len);
|
#endif
|
}
|
if (sc->dbg_rx_frame)
|
{
|
_ssv6xxx_hexdump("================================================================\n"
|
"RX frame", (const u8 *)rx_skb->data, rx_skb->len);
|
}
|
skb_pull(rx_skb, SSV6XXX_RX_DESC_LEN);
|
skb_trim(rx_skb, rx_skb->len-sc->sh->rx_pinfo_pad);
|
#ifdef CONFIG_P2P_NOA
|
if (is_beacon)
|
ssv6xxx_noa_detect(sc, hdr, rx_skb->len);
|
#endif
|
#ifdef USE_LOCAL_CRYPTO
|
if ((rx_hw_dec == false) && (do_sw_dec == true))
|
{
|
#ifndef MULTI_THREAD_ENCRYPT
|
ret = ssv6xxx_skb_decrypt(rx_skb, sta, sc);
|
if (ret < 0)
|
{
|
dev_err(sc->dev, "[Local Crypto]: Fail to decrypt local: %02X:%02X:%02X:%02X:%02X:%02X, ret = %d.\n",
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], ret);
|
goto drop_rx;
|
}
|
#else
|
skb_info->sta = sta;
|
ret = ssv6xxx_skb_pre_decrypt(rx_skb, sta, sc);
|
if (ret == 0)
|
{
|
skb_info->crypt_st = PKT_CRYPT_ST_DEC_PRE;
|
spin_lock_irqsave(&sc->crypt_st_lock, flags);
|
__skb_queue_tail(&sc->preprocess_q, rx_skb);
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
if (sc->max_preprocess_q_len < skb_queue_len(&sc->preprocess_q))
|
sc->max_preprocess_q_len = skb_queue_len(&sc->preprocess_q);
|
#endif
|
spin_unlock_irqrestore(&sc->crypt_st_lock, flags);
|
list_for_each_entry_reverse(ta, &sc->encrypt_task_head, list)
|
{
|
if ((cpu_online(ta->cpu_no)) && (ta->running == 0))
|
{
|
wake_up(&ta->encrypt_wait_q);
|
return;
|
}
|
}
|
return;
|
}
|
else if (ret ==(-EOPNOTSUPP))
|
{
|
ret = ssv6xxx_skb_decrypt(rx_skb, sta, sc);
|
if (ret < 0)
|
{
|
dev_err(sc->dev, "[Local Crypto]: Fail to decrypt local: %02X:%02X:%02X:%02X:%02X:%02X, ret = %d.\n",
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5], ret);
|
goto drop_rx;
|
}
|
}
|
else
|
{
|
printk("[MT-CRYPTO]: Failed to do pre-decrypt (%d)\n", ret);
|
dev_kfree_skb_any(rx_skb);
|
return;
|
}
|
#endif
|
}
|
#endif
|
if (rx_hw_dec || do_sw_dec)
|
{
|
hdr = (struct ieee80211_hdr *)rx_skb->data;
|
rxs = IEEE80211_SKB_RXCB(rx_skb);
|
hdr->frame_control = hdr->frame_control & ~(cpu_to_le16(IEEE80211_FCTL_PROTECTED));
|
rxs->flag |= (RX_FLAG_DECRYPTED|RX_FLAG_IV_STRIPPED);
|
}
|
#if 0
|
if ( is_broadcast_ether_addr(hdr->addr1)
|
&& (ieee80211_is_data_qos(fc) || ieee80211_is_data(fc)))
|
#endif
|
#if 0
|
if (ieee80211_is_probe_req(fc))
|
{
|
#if 0
|
printk(KERN_ERR "RX M: 1 %02X:%02X:%02X:%02X:%02X:%02X (%d - %d - %d)\n",
|
hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
|
hdr->addr1[3], hdr->addr1[4], hdr->addr1[5],
|
(le16_to_cpu(hdr->seq_ctrl) >> 4),
|
rxdesc->wsid, ieee80211_has_protected(fc));
|
#endif
|
printk(KERN_ERR "Probe Req: 2 %02X:%02X:%02X:%02X:%02X:%02X\n",
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
|
#if 0
|
printk(KERN_ERR "RX M: 3 %02X:%02X:%02X:%02X:%02X:%02X\n",
|
hdr->addr3[0], hdr->addr3[1], hdr->addr3[2],
|
hdr->addr3[3], hdr->addr3[4], hdr->addr3[5]);
|
#endif
|
_ssv6xxx_hexdump("RX frame", (const u8 *)rx_skb->data,
|
(rx_skb->len > 128) ? 128 : rx_skb->len);
|
}
|
#endif
|
#if defined(USE_THREAD_RX) && !defined(IRQ_PROC_RX_DATA)
|
local_bh_disable();
|
ieee80211_rx(sc->hw, rx_skb);
|
local_bh_enable();
|
#else
|
ieee80211_rx_irqsafe(sc->hw, rx_skb);
|
#endif
|
return;
|
drop_rx:
|
#if 0
|
dev_err(sc->dev, "D %02X:%02X:%02X:%02X:%02X:%02X\n",
|
hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
|
hdr->addr2[3], hdr->addr2[4], hdr->addr2[5]);
|
#endif
|
dev_kfree_skb_any(rx_skb);
|
}
|
#ifdef IRQ_PROC_RX_DATA
|
static struct sk_buff *_proc_rx_skb (struct ssv_softc *sc, struct sk_buff *rx_skb)
|
{
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(rx_skb->data + SSV6XXX_RX_DESC_LEN);
|
struct ssv6200_rx_desc *rxdesc = (struct ssv6200_rx_desc *)rx_skb->data;
|
if ( ieee80211_is_back(hdr->frame_control)
|
|| (rxdesc->c_type == HOST_EVENT))
|
return rx_skb;
|
_proc_data_rx_skb(sc, rx_skb);
|
return NULL;
|
}
|
#endif
|
void _process_rx_q (struct ssv_softc *sc, struct sk_buff_head *rx_q, spinlock_t *rx_q_lock)
|
{
|
struct sk_buff *skb;
|
struct ieee80211_hdr *hdr;
|
struct ssv6200_rx_desc *rxdesc;
|
unsigned long flags=0;
|
#ifdef USE_FLUSH_RETRY
|
bool has_ba_processed = false;
|
#endif
|
while (1) {
|
if (rx_q_lock != NULL)
|
{
|
spin_lock_irqsave(rx_q_lock, flags);
|
skb = __skb_dequeue(rx_q);
|
}
|
else
|
skb = skb_dequeue(rx_q);
|
if (!skb)
|
{
|
if (rx_q_lock != NULL)
|
spin_unlock_irqrestore(rx_q_lock, flags);
|
break;
|
}
|
sc->rx.rxq_count --;
|
if (rx_q_lock != NULL)
|
spin_unlock_irqrestore(rx_q_lock, flags);
|
rxdesc = (struct ssv6200_rx_desc *)skb->data;
|
if (rxdesc->c_type == HOST_EVENT)
|
{
|
struct cfg_host_event *h_evt = (struct cfg_host_event *)rxdesc;
|
if (h_evt->h_event == SOC_EVT_NO_BA)
|
{
|
ssv6200_ampdu_no_BA_handler(sc->hw, skb);
|
#ifdef USE_FLUSH_RETRY
|
has_ba_processed = true;
|
#endif
|
}
|
else if (h_evt->h_event == SOC_EVT_RC_MPDU_REPORT)
|
{
|
#if 0
|
struct cfg_host_event *host_event;
|
struct firmware_rate_control_report_data *report_data;
|
host_event = (struct cfg_host_event *)rxdesc;
|
report_data = (struct firmware_rate_control_report_data *)&host_event->dat[0];
|
printk("MPDU report get!!wsid[%d]didx[%d]F[%d]S[%d]\n",report_data->wsid,report_data->rates[0].data_rate,report_data->ampdu_len,report_data->ampdu_ack_len);
|
#endif
|
skb_queue_tail(&sc->rc_report_queue, skb);
|
if (sc->rc_sample_sechedule == 0)
|
queue_work(sc->rc_sample_workqueue, &sc->rc_sample_work);
|
}
|
else if (h_evt->h_event == SOC_EVT_SDIO_TEST_COMMAND)
|
{
|
if(h_evt->evt_seq_no == 0)
|
{
|
printk("SOC_EVT_SDIO_TEST_COMMAND\n");
|
sc->sdio_rx_evt_size = h_evt->len;
|
sc->sdio_throughput_timestamp = jiffies;
|
}
|
else
|
{
|
sc->sdio_rx_evt_size += h_evt->len;
|
if (time_after(jiffies, sc->sdio_throughput_timestamp + msecs_to_jiffies(1000)))
|
{
|
printk("data[%ld] SDIO RX throughput %ld Kbps\n",sc->sdio_rx_evt_size,(sc->sdio_rx_evt_size << 3) / jiffies_to_msecs(jiffies - sc->sdio_throughput_timestamp));
|
sc->sdio_throughput_timestamp = jiffies;
|
sc->sdio_rx_evt_size = 0;
|
}
|
}
|
dev_kfree_skb_any(skb);
|
}
|
else if (h_evt->h_event == SOC_EVT_WATCHDOG_TRIGGER)
|
{
|
dev_kfree_skb_any(skb);
|
// if(sc->watchdog_flag != WD_SLEEP) //+++
|
sc->watchdog_flag = WD_KICKED;
|
}
|
else if (h_evt->h_event == SOC_EVT_RESET_HOST)
|
{
|
dev_kfree_skb_any(skb);
|
if ((sc->ap_vif == NULL) || !(sc->sh->cfg.ignore_reset_in_ap))
|
{
|
ssv6xxx_restart_hw(sc);
|
}
|
else
|
{
|
dev_warn(sc->dev, "Reset event ignored.\n");
|
}
|
}
|
#ifdef CONFIG_P2P_NOA
|
else if(h_evt->h_event == SOC_EVT_NOA)
|
{
|
ssv6xxx_process_noa_event(sc, skb);
|
dev_kfree_skb_any(skb);
|
}
|
#endif
|
else if (h_evt->h_event == SOC_EVT_SDIO_TXTPUT_RESULT) {
|
printk("data SDIO TX throughput %d Kbps\n", h_evt->evt_seq_no);
|
dev_kfree_skb_any(skb);
|
}
|
else if (h_evt->h_event == SOC_EVT_TXLOOPBK_RESULT){
|
if (h_evt->evt_seq_no == SSV6XXX_STATE_OK) {
|
printk("FW TX LOOPBACK OK\n");
|
sc->iq_cali_done = IQ_CALI_OK;
|
} else {
|
printk(KERN_ERR "FW TX LOOPBACK FAILED\n");
|
sc->iq_cali_done = IQ_CALI_FAILED;
|
}
|
dev_kfree_skb_any(skb);
|
wake_up_interruptible(&sc->fw_wait_q);
|
}
|
else
|
{
|
dev_warn(sc->dev, "Unkown event %d received\n", h_evt->h_event);
|
dev_kfree_skb_any(skb);
|
}
|
continue;
|
}
|
hdr = (struct ieee80211_hdr *)(skb->data + SSV6XXX_RX_DESC_LEN);
|
if (ieee80211_is_back(hdr->frame_control))
|
{
|
ssv6200_ampdu_BA_handler(sc->hw, skb);
|
#ifdef USE_FLUSH_RETRY
|
has_ba_processed = true;
|
#endif
|
continue;
|
}
|
_proc_data_rx_skb(sc, skb);
|
}
|
#ifdef USE_FLUSH_RETRY
|
if (has_ba_processed)
|
{
|
ssv6xxx_ampdu_postprocess_BA(sc->hw);
|
}
|
#endif
|
}
|
#if !defined(USE_THREAD_RX) || defined(USE_BATCH_RX)
|
int ssv6200_rx(struct sk_buff_head *rx_skb_q, void *args)
|
#else
|
int ssv6200_rx(struct sk_buff *rx_skb, void *args)
|
#endif
|
{
|
struct ssv_softc *sc=args;
|
#ifdef IRQ_PROC_RX_DATA
|
struct sk_buff *skb;
|
skb = _proc_rx_skb(sc, rx_skb);
|
if (skb == NULL)
|
return 0;
|
#endif
|
#if !defined(USE_THREAD_RX) || defined(USE_BATCH_RX)
|
{
|
unsigned long flags;
|
spin_lock_irqsave(&sc->rx_skb_q.lock, flags);
|
while (skb_queue_len(rx_skb_q))
|
__skb_queue_tail(&sc->rx_skb_q, __skb_dequeue(rx_skb_q));
|
spin_unlock_irqrestore(&sc->rx_skb_q.lock, flags);
|
}
|
#else
|
skb_queue_tail(&sc->rx_skb_q, rx_skb);
|
#endif
|
wake_up_interruptible(&sc->rx_wait_q);
|
return 0;
|
}
|
struct ieee80211_sta *ssv6xxx_find_sta_by_rx_skb (struct ssv_softc *sc, struct sk_buff *skb)
|
{
|
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data + SSV6XXX_RX_DESC_LEN);
|
struct ssv6200_rx_desc *rxdesc = (struct ssv6200_rx_desc *)skb->data;;
|
if ((rxdesc->wsid >= 0) && (rxdesc->wsid < SSV_NUM_STA))
|
return sc->sta_info[rxdesc->wsid].sta;
|
else
|
return ssv6xxx_find_sta_by_addr(sc, hdr->addr2);
|
}
|
struct ieee80211_sta *ssv6xxx_find_sta_by_addr (struct ssv_softc *sc, u8 addr[6])
|
{
|
struct ieee80211_sta *sta;
|
int i;
|
for (i = 0; i < SSV6200_MAX_VIF; i++)
|
{
|
if (sc->vif_info[i].vif == NULL)
|
continue;
|
sta = ieee80211_find_sta(sc->vif_info[i].vif, addr);
|
if (sta != NULL)
|
return sta;
|
}
|
return NULL;
|
}
|
void ssv6xxx_foreach_sta (struct ssv_softc *sc, void (*sta_func)(struct ssv_softc *, struct ssv_sta_info *, void *), void *param)
|
{
|
int i;
|
BUG_ON(sta_func == NULL);
|
#if 0
|
for (i = 0; i < SSV6200_MAX_VIF; i++)
|
{
|
struct ssv_vif_priv_data *vif_priv;
|
int j;
|
if (sc->vif_info[i].vif == NULL)
|
continue;
|
vif_priv = (struct ssv_vif_priv_data *)sc->vif[i]->drv_priv;
|
for (j = 0; j < SSV_NUM_STA; j++)
|
{
|
if ((vif_priv->sta_info[j].s_flags & STA_FLAG_VALID) == 0)
|
continue;
|
(*sta_func)(sc, &vif_priv->sta_info[j], param);
|
}
|
}
|
#else
|
for (i = 0; i < SSV_NUM_STA; i++)
|
{
|
if ((sc->sta_info[i].s_flags & STA_FLAG_VALID) == 0)
|
continue;
|
(*sta_func)(sc, &sc->sta_info[i], param);
|
}
|
#endif
|
}
|
void ssv6xxx_foreach_vif_sta (struct ssv_softc *sc,
|
struct ssv_vif_info *vif_info,
|
void (*sta_func)(struct ssv_softc *,
|
struct ssv_vif_info *,
|
struct ssv_sta_info *,
|
void *),
|
void *param)
|
{
|
struct ssv_vif_priv_data *vif_priv;
|
struct ssv_sta_priv_data *sta_priv_iter;
|
BUG_ON(vif_info == NULL);
|
BUG_ON((size_t)vif_info < 0x30000);
|
vif_priv = (struct ssv_vif_priv_data *)vif_info->vif->drv_priv;
|
BUG_ON((size_t)vif_info->vif < 0x30000);
|
BUG_ON((size_t)vif_priv < 0x30000);
|
list_for_each_entry(sta_priv_iter, &vif_priv->sta_list, list)
|
{
|
BUG_ON(sta_priv_iter == NULL);
|
BUG_ON((size_t)sta_priv_iter < 0x30000);
|
BUG_ON(sta_priv_iter->sta_info == NULL);
|
BUG_ON((size_t)sta_priv_iter->sta_info < 0x30000);
|
if ((sta_priv_iter->sta_info->s_flags & STA_FLAG_VALID) == 0)
|
continue;
|
(*sta_func)(sc, vif_info, sta_priv_iter->sta_info, param);
|
}
|
}
|
#ifdef CONFIG_SSV6XXX_DEBUGFS
|
ssize_t ssv6xxx_tx_queue_status_dump (struct ssv_softc *sc, char *status_buf,
|
ssize_t length)
|
{
|
ssize_t buf_size = length;
|
ssize_t prt_size;
|
prt_size = snprintf(status_buf, buf_size, "\nSMAC driver queue status:.\n");
|
status_buf += prt_size;
|
buf_size -= prt_size;
|
#ifdef MULTI_THREAD_ENCRYPT
|
prt_size = snprintf(status_buf, buf_size, "\tCrypto pre-process queue: %d.\n",
|
skb_queue_len(&sc->preprocess_q));
|
status_buf += prt_size;
|
buf_size -= prt_size;
|
prt_size = snprintf(status_buf, buf_size, "\tMax pre-process queue: %d.\n",
|
sc->max_preprocess_q_len);
|
status_buf += prt_size;
|
buf_size -= prt_size;
|
prt_size = snprintf(status_buf, buf_size, "\tCrypto process queue: %d.\n",
|
skb_queue_len(&sc->crypted_q));
|
status_buf += prt_size;
|
buf_size -= prt_size;
|
prt_size = snprintf(status_buf, buf_size, "\tMax process queue: %d.\n",
|
sc->max_crypted_q_len);
|
status_buf += prt_size;
|
buf_size -= prt_size;
|
#endif
|
prt_size = snprintf(status_buf, buf_size, "\tTX queue: %d\n",
|
skb_queue_len(&sc->tx_skb_q));
|
status_buf += prt_size;
|
buf_size -= prt_size;
|
prt_size = snprintf(status_buf, buf_size, "\tMax TX queue: %d\n",
|
sc->max_tx_skb_q_len);
|
status_buf += prt_size;
|
buf_size -= prt_size;
|
return (length - buf_size);
|
}
|
#endif
|