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