/*
* 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