diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/staging/rtl8192e | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/staging/rtl8192e')
57 files changed, 27110 insertions, 0 deletions
diff --git a/drivers/staging/rtl8192e/Kconfig b/drivers/staging/rtl8192e/Kconfig new file mode 100644 index 0000000000..e06c189b4c --- /dev/null +++ b/drivers/staging/rtl8192e/Kconfig @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: GPL-2.0 +config RTLLIB + tristate "Support for rtllib wireless devices" + depends on WLAN && m + select LIB80211 + select CRC32 + help + If you have a wireless card that uses rtllib, say + Y. Currently the only card is the rtl8192e. + + If unsure, say N. + + This driver adds support for rtllib wireless cards. + Only the rtl8192e is supported as of now. + +if RTLLIB + +config RTLLIB_CRYPTO_CCMP + tristate "Support for rtllib CCMP crypto" + depends on RTLLIB + select CRYPTO + select CRYPTO_AES + select CRYPTO_CCM + default y + help + CCMP crypto driver for rtllib. + + If you enabled RTLLIB, you want this. + Adds support for the CCM mode Protocol crypto driver for + use in wireless cards (including rtllib cards). + +config RTLLIB_CRYPTO_TKIP + tristate "Support for rtllib TKIP crypto" + depends on RTLLIB + select CRYPTO + select CRYPTO_LIB_ARC4 + select CRYPTO_MICHAEL_MIC + default y + help + TKIP crypto driver for rtllib. + + If you enabled RTLLIB, you want this. + Adds support for the Temporal Key Integrity Protocol for + the IEEE 802.11i standard for use on wireless cards. + +config RTLLIB_CRYPTO_WEP + tristate "Support for rtllib WEP crypto" + select CRYPTO_LIB_ARC4 + depends on RTLLIB + default y + help + WEP crypto driver for rtllib. + + If you enabled RTLLIB, you want this. + Adds support for the (now weak) Wired Equivalent Privacy + (WEP) crypto protocol for wireless cards. + NOTE: This protocol is now considered insecure. + +source "drivers/staging/rtl8192e/rtl8192e/Kconfig" + +endif diff --git a/drivers/staging/rtl8192e/Makefile b/drivers/staging/rtl8192e/Makefile new file mode 100644 index 0000000000..6af5199388 --- /dev/null +++ b/drivers/staging/rtl8192e/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0 +rtllib-objs := \ + dot11d.o \ + rtllib_module.o \ + rtllib_rx.o \ + rtllib_tx.o \ + rtllib_wx.o \ + rtllib_softmac.o \ + rtllib_softmac_wx.o \ + rtl819x_BAProc.o \ + rtl819x_HTProc.o \ + rtl819x_TSProc.o + +obj-$(CONFIG_RTLLIB) += rtllib.o + +obj-$(CONFIG_RTLLIB_CRYPTO_CCMP) += rtllib_crypt_ccmp.o +obj-$(CONFIG_RTLLIB_CRYPTO_TKIP) += rtllib_crypt_tkip.o +obj-$(CONFIG_RTLLIB_CRYPTO_WEP) += rtllib_crypt_wep.o + +obj-$(CONFIG_RTL8192E) += rtl8192e/ diff --git a/drivers/staging/rtl8192e/TODO b/drivers/staging/rtl8192e/TODO new file mode 100644 index 0000000000..7221ae65d6 --- /dev/null +++ b/drivers/staging/rtl8192e/TODO @@ -0,0 +1,18 @@ +To-do list: + +* merge into drivers/net/wireless/realtek/rtlwifi/rtl8192* +* clean up function naming +* Correct the coding style according to Linux guidelines; please read the document + at https://www.kernel.org/doc/html/latest/process/coding-style.html. +* Remove unnecessary debugging/printing macros; for those that are still needed + use the proper kernel API (pr_debug(), dev_dbg(), netdev_dbg()). +* Remove dead code such as unusued functions, variables, fields, etc.. +* Use in-kernel API and remove unnecessary wrappers where possible. +* Fix bugs due to code that sleeps in atomic context. +* Remove the HAL layer and migrate its functionality into the relevant parts of + the driver. +* Switch to use LIB80211. +* Switch to use MAC80211. +* Switch to use CFG80211. +* Improve the error handling of various functions, particularly those that use + existing kernel APIs. diff --git a/drivers/staging/rtl8192e/dot11d.c b/drivers/staging/rtl8192e/dot11d.c new file mode 100644 index 0000000000..82c11caeee --- /dev/null +++ b/drivers/staging/rtl8192e/dot11d.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/****************************************************************************** + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + ******************************************************************************/ +#include "dot11d.h" + +struct channel_list { + u8 channel[32]; + u8 len; +}; + +static struct channel_list channel_array[] = { + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 36, 40, 44, 48, 52, 56, 60, 64, + 149, 153, 157, 161, 165}, 24}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 11}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, 56, + 60, 64}, 21}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, + 56, 60, 64}, 22}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, + 56, 60, 64}, 22}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, + 56, 60, 64}, 22}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 36, 40, 44, 48, 52, + 56, 60, 64}, 22}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, 14}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, 13}, + {{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 36, 40, 44, 48, 52, + 56, 60, 64}, 21} +}; + +void dot11d_init(struct rtllib_device *ieee) +{ + struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(ieee); + + dot11d_info->enabled = false; + + dot11d_info->state = DOT11D_STATE_NONE; + dot11d_info->country_len = 0; + memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); + memset(dot11d_info->max_tx_power_list, 0xFF, MAX_CHANNEL_NUMBER + 1); + RESET_CIE_WATCHDOG(ieee); +} +EXPORT_SYMBOL(dot11d_init); + +void dot11d_channel_map(u8 channel_plan, struct rtllib_device *ieee) +{ + int i, max_chan = 14, min_chan = 1; + + ieee->global_domain = false; + + if (channel_array[channel_plan].len != 0) { + memset(GET_DOT11D_INFO(ieee)->channel_map, 0, + sizeof(GET_DOT11D_INFO(ieee)->channel_map)); + for (i = 0; i < channel_array[channel_plan].len; i++) { + if (channel_array[channel_plan].channel[i] < min_chan || + channel_array[channel_plan].channel[i] > max_chan) + break; + GET_DOT11D_INFO(ieee)->channel_map[channel_array + [channel_plan].channel[i]] = 1; + } + } + + switch (channel_plan) { + case COUNTRY_CODE_GLOBAL_DOMAIN: + ieee->global_domain = true; + for (i = 12; i <= 14; i++) + GET_DOT11D_INFO(ieee)->channel_map[i] = 2; + ieee->bss_start_channel = 10; + ieee->ibss_maxjoin_chal = 11; + break; + + case COUNTRY_CODE_WORLD_WIDE_13: + for (i = 12; i <= 13; i++) + GET_DOT11D_INFO(ieee)->channel_map[i] = 2; + ieee->bss_start_channel = 10; + ieee->ibss_maxjoin_chal = 11; + break; + + default: + ieee->bss_start_channel = 1; + ieee->ibss_maxjoin_chal = 14; + break; + } +} +EXPORT_SYMBOL(dot11d_channel_map); + +void dot11d_reset(struct rtllib_device *ieee) +{ + struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(ieee); + u32 i; + + memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); + memset(dot11d_info->max_tx_power_list, 0xFF, MAX_CHANNEL_NUMBER + 1); + for (i = 1; i <= 11; i++) + (dot11d_info->channel_map)[i] = 1; + for (i = 12; i <= 14; i++) + (dot11d_info->channel_map)[i] = 2; + dot11d_info->state = DOT11D_STATE_NONE; + dot11d_info->country_len = 0; + RESET_CIE_WATCHDOG(ieee); +} + +void dot11d_update_country(struct rtllib_device *dev, u8 *address, + u16 country_len, u8 *country) +{ + struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); + u8 i, j, number_of_triples, max_channel_number; + struct chnl_txpow_triple *triple; + + memset(dot11d_info->channel_map, 0, MAX_CHANNEL_NUMBER + 1); + memset(dot11d_info->max_tx_power_list, 0xFF, MAX_CHANNEL_NUMBER + 1); + max_channel_number = 0; + number_of_triples = (country_len - 3) / 3; + triple = (struct chnl_txpow_triple *)(country + 3); + for (i = 0; i < number_of_triples; i++) { + if (max_channel_number >= triple->first_channel) { + netdev_info(dev->dev, + "%s: Invalid country IE, skip it......1\n", + __func__); + return; + } + if (MAX_CHANNEL_NUMBER < (triple->first_channel + + triple->num_channels)) { + netdev_info(dev->dev, + "%s: Invalid country IE, skip it......2\n", + __func__); + return; + } + + for (j = 0; j < triple->num_channels; j++) { + dot11d_info->channel_map[triple->first_channel + j] = 1; + dot11d_info->max_tx_power_list[triple->first_channel + j] = + triple->max_tx_power; + max_channel_number = triple->first_channel + j; + } + + triple = (struct chnl_txpow_triple *)((u8 *)triple + 3); + } + + UPDATE_CIE_SRC(dev, address); + + dot11d_info->country_len = country_len; + memcpy(dot11d_info->country_buffer, country, country_len); + dot11d_info->state = DOT11D_STATE_LEARNED; +} + +void dot11d_scan_complete(struct rtllib_device *dev) +{ + struct rt_dot11d_info *dot11d_info = GET_DOT11D_INFO(dev); + + switch (dot11d_info->state) { + case DOT11D_STATE_LEARNED: + dot11d_info->state = DOT11D_STATE_DONE; + break; + case DOT11D_STATE_DONE: + dot11d_reset(dev); + break; + case DOT11D_STATE_NONE: + break; + } +} diff --git a/drivers/staging/rtl8192e/dot11d.h b/drivers/staging/rtl8192e/dot11d.h new file mode 100644 index 0000000000..6d2b93acfa --- /dev/null +++ b/drivers/staging/rtl8192e/dot11d.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/****************************************************************************** + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + ******************************************************************************/ +#ifndef __INC_DOT11D_H +#define __INC_DOT11D_H + +#include "rtllib.h" + +struct chnl_txpow_triple { + u8 first_channel; + u8 num_channels; + u8 max_tx_power; +}; + +enum dot11d_state { + DOT11D_STATE_NONE = 0, + DOT11D_STATE_LEARNED, + DOT11D_STATE_DONE, +}; + +/** + * struct rt_dot11d_info * @country_len: value greater than 0 if + * @country_buffer contains valid country information element. + * @channel_map: holds channel values + * 0 - invalid, + * 1 - valid (active scan), + * 2 - valid (passive scan) + * @country_src_addr - Source AP of the country IE + */ + +struct rt_dot11d_info { + bool enabled; + + u16 country_len; + u8 country_buffer[MAX_IE_LEN]; + u8 country_src_addr[6]; + u8 country_watchdog; + + u8 channel_map[MAX_CHANNEL_NUMBER + 1]; + u8 max_tx_power_list[MAX_CHANNEL_NUMBER + 1]; + + enum dot11d_state state; +}; + +static inline void copy_mac_addr(unsigned char *des, unsigned char *src) +{ + memcpy(des, src, 6); +} + +#define GET_DOT11D_INFO(__ieee_dev) \ + ((struct rt_dot11d_info *)((__ieee_dev)->dot11d_info)) + +#define IS_DOT11D_ENABLE(__ieee_dev) \ + (GET_DOT11D_INFO(__ieee_dev)->enabled) +#define IS_COUNTRY_IE_VALID(__ieee_dev) \ + (GET_DOT11D_INFO(__ieee_dev)->country_len > 0) + +#define IS_EQUAL_CIE_SRC(__ieee_dev, __address) \ + ether_addr_equal_unaligned( \ + GET_DOT11D_INFO(__ieee_dev)->country_src_addr, __address) +#define UPDATE_CIE_SRC(__ieee_dev, __address) \ + copy_mac_addr(GET_DOT11D_INFO(__ieee_dev)->country_src_addr, __address) + +#define GET_CIE_WATCHDOG(__ieee_dev) \ + (GET_DOT11D_INFO(__ieee_dev)->country_watchdog) +static inline void RESET_CIE_WATCHDOG(struct rtllib_device *__ieee_dev) +{ + GET_CIE_WATCHDOG(__ieee_dev) = 0; +} + +#define UPDATE_CIE_WATCHDOG(__ieee_dev) (++GET_CIE_WATCHDOG(__ieee_dev)) + +void dot11d_init(struct rtllib_device *dev); +void dot11d_channel_map(u8 channel_plan, struct rtllib_device *ieee); +void dot11d_reset(struct rtllib_device *dev); +void dot11d_update_country(struct rtllib_device *dev, u8 *address, + u16 country_len, u8 *country); +void dot11d_scan_complete(struct rtllib_device *dev); + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/Kconfig b/drivers/staging/rtl8192e/rtl8192e/Kconfig new file mode 100644 index 0000000000..f8f774a162 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +config RTL8192E + tristate "RealTek RTL8192E Wireless LAN NIC driver" + depends on PCI && WLAN && RTLLIB + depends on m + select CFG80211 + select WIRELESS_EXT + select WEXT_PRIV + select CRYPTO + select FW_LOADER diff --git a/drivers/staging/rtl8192e/rtl8192e/Makefile b/drivers/staging/rtl8192e/rtl8192e/Makefile new file mode 100644 index 0000000000..a442d79ea7 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +r8192e_pci-objs := \ + r8192E_dev.o \ + r8192E_phy.o \ + r8192E_firmware.o \ + r8192E_cmdpkt.o \ + table.o \ + r8190P_rtl8256.o \ + rtl_cam.o \ + rtl_core.o \ + rtl_dm.o \ + rtl_eeprom.o \ + rtl_ethtool.o \ + rtl_pci.o \ + rtl_pm.o \ + rtl_ps.o \ + rtl_wx.o \ + +obj-$(CONFIG_RTL8192E) += r8192e_pci.o diff --git a/drivers/staging/rtl8192e/rtl8192e/r8190P_def.h b/drivers/staging/rtl8192e/rtl8192e/r8190P_def.h new file mode 100644 index 0000000000..c229fd244a --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8190P_def.h @@ -0,0 +1,276 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef R8190P_DEF_H +#define R8190P_DEF_H + +#include <linux/types.h> + +#define MAX_SILENT_RESET_RX_SLOT_NUM 10 + +#define RX_MPDU_QUEUE 0 + +enum rtl819x_loopback { + RTL819X_NO_LOOPBACK = 0, + RTL819X_MAC_LOOPBACK = 1, + RTL819X_DMA_LOOPBACK = 2, + RTL819X_CCK_LOOPBACK = 3, +}; + +#define DESC90_RATE1M 0x00 +#define DESC90_RATE2M 0x01 +#define DESC90_RATE5_5M 0x02 +#define DESC90_RATE11M 0x03 +#define DESC90_RATE6M 0x04 +#define DESC90_RATE9M 0x05 +#define DESC90_RATE12M 0x06 +#define DESC90_RATE18M 0x07 +#define DESC90_RATE24M 0x08 +#define DESC90_RATE36M 0x09 +#define DESC90_RATE48M 0x0a +#define DESC90_RATE54M 0x0b +#define DESC90_RATEMCS0 0x00 +#define DESC90_RATEMCS1 0x01 +#define DESC90_RATEMCS2 0x02 +#define DESC90_RATEMCS3 0x03 +#define DESC90_RATEMCS4 0x04 +#define DESC90_RATEMCS5 0x05 +#define DESC90_RATEMCS6 0x06 +#define DESC90_RATEMCS7 0x07 +#define DESC90_RATEMCS8 0x08 +#define DESC90_RATEMCS9 0x09 +#define DESC90_RATEMCS10 0x0a +#define DESC90_RATEMCS11 0x0b +#define DESC90_RATEMCS12 0x0c +#define DESC90_RATEMCS13 0x0d +#define DESC90_RATEMCS14 0x0e +#define DESC90_RATEMCS15 0x0f +#define DESC90_RATEMCS32 0x20 + +#define SHORT_SLOT_TIME 9 +#define NON_SHORT_SLOT_TIME 20 + +#define RX_SMOOTH 20 + +#define QSLT_BK 0x1 +#define QSLT_BE 0x0 +#define QSLT_VI 0x4 +#define QSLT_VO 0x6 +#define QSLT_BEACON 0x10 +#define QSLT_HIGH 0x11 +#define QSLT_MGNT 0x12 +#define QSLT_CMD 0x13 + +#define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x007 +#define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x0aa +#define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x024 +#define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x007 +#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x10 +#define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x4 +#define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0xd + +#define APPLIED_RESERVED_QUEUE_IN_FW 0x80000000 +#define RSVD_FW_QUEUE_PAGE_BK_SHIFT 0x00 +#define RSVD_FW_QUEUE_PAGE_BE_SHIFT 0x08 +#define RSVD_FW_QUEUE_PAGE_VI_SHIFT 0x10 +#define RSVD_FW_QUEUE_PAGE_VO_SHIFT 0x18 +#define RSVD_FW_QUEUE_PAGE_MGNT_SHIFT 0x10 +#define RSVD_FW_QUEUE_PAGE_BCN_SHIFT 0x00 +#define RSVD_FW_QUEUE_PAGE_PUB_SHIFT 0x08 + +#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0 +#define HAL_PRIME_CHNL_OFFSET_LOWER 1 +#define HAL_PRIME_CHNL_OFFSET_UPPER 2 + +enum version_8190_loopback { + VERSION_8190_BD = 0x3, + VERSION_8190_BE +}; + +#define IC_VersionCut_C 0x2 +#define IC_VersionCut_D 0x3 +#define IC_VersionCut_E 0x4 + +enum rf_optype { + RF_OP_By_SW_3wire = 0, + RF_OP_By_FW, + RF_OP_MAX +}; + +struct bb_reg_definition { + u32 rfintfs; + u32 rfintfo; + u32 rfintfe; + u32 rf3wireOffset; + u32 rfHSSIPara2; + u32 rfLSSIReadBack; + u32 rfLSSIReadBackPi; +}; + +struct tx_fwinfo_8190pci { + u8 TxRate:7; + u8 CtsEnable:1; + u8 RtsRate:7; + u8 RtsEnable:1; + u8 TxHT:1; + u8 Short:1; + u8 TxBandwidth:1; + u8 TxSubCarrier:2; + u8 STBC:2; + u8 AllowAggregation:1; + u8 RtsHT:1; + u8 RtsShort:1; + u8 RtsBandwidth:1; + u8 RtsSubcarrier:2; + u8 RtsSTBC:2; + u8 EnableCPUDur:1; + + u32 RxMF:2; + u32 RxAMD:3; + u32 TxPerPktInfoFeedback:1; + u32 Reserved1:2; + u32 TxAGCOffset:4; + u32 TxAGCSign:1; + u32 RAW_TXD:1; + u32 Retry_Limit:4; + u32 Reserved2:1; + u32 PacketID:13; +}; + +struct phy_ofdm_rx_status_rxsc_sgien_exintfflag { + u8 reserved:4; + u8 rxsc:2; + u8 sgi_en:1; + u8 ex_intf_flag:1; +}; + +struct phy_sts_ofdm_819xpci { + u8 trsw_gain_X[4]; + u8 pwdb_all; + u8 cfosho_X[4]; + u8 cfotail_X[4]; + u8 rxevm_X[2]; + u8 rxsnr_X[4]; + u8 pdsnr_X[2]; + u8 csi_current_X[2]; + u8 csi_target_X[2]; + u8 sigevm; + u8 max_ex_pwr; + u8 sgi_en; + u8 rxsc_sgien_exflg; +}; + +struct phy_sts_cck_819xpci { + u8 adc_pwdb_X[4]; + u8 sq_rpt; + u8 cck_agc_rpt; +}; + +#define PHY_RSSI_SLID_WIN_MAX 100 +#define PHY_Beacon_RSSI_SLID_WIN_MAX 10 + +struct tx_desc { + u16 PktSize; + u8 Offset; + u8 Reserved1:3; + u8 CmdInit:1; + u8 LastSeg:1; + u8 FirstSeg:1; + u8 LINIP:1; + u8 OWN:1; + + u8 TxFWInfoSize; + u8 RATid:3; + u8 DISFB:1; + u8 USERATE:1; + u8 MOREFRAG:1; + u8 NoEnc:1; + u8 PIFS:1; + u8 QueueSelect:5; + u8 NoACM:1; + u8 Resv:2; + u8 SecCAMID:5; + u8 SecDescAssign:1; + u8 SecType:2; + + u16 TxBufferSize; + u8 PktId:7; + u8 Resv1:1; + u8 Reserved2; + + u32 TxBuffAddr; + + u32 NextDescAddress; + + u32 Reserved5; + u32 Reserved6; + u32 Reserved7; +}; + +struct tx_desc_cmd { + u16 PktSize; + u8 Reserved1; + u8 CmdType:3; + u8 CmdInit:1; + u8 LastSeg:1; + u8 FirstSeg:1; + u8 LINIP:1; + u8 OWN:1; + + u16 ElementReport; + u16 Reserved2; + + u16 TxBufferSize; + u16 Reserved3; + + u32 TxBuffAddr; + u32 NextDescAddress; + u32 Reserved4; + u32 Reserved5; + u32 Reserved6; +}; + +struct rx_desc { + u16 Length:14; + u16 CRC32:1; + u16 ICV:1; + u8 RxDrvInfoSize; + u8 Shift:2; + u8 PHYStatus:1; + u8 SWDec:1; + u8 LastSeg:1; + u8 FirstSeg:1; + u8 EOR:1; + u8 OWN:1; + + u32 Reserved2; + + u32 Reserved3; + + u32 BufferAddress; +}; + +struct rx_fwinfo { + u16 Reserved1:12; + u16 PartAggr:1; + u16 FirstAGGR:1; + u16 Reserved2:2; + + u8 RxRate:7; + u8 RxHT:1; + + u8 BW:1; + u8 SPLCP:1; + u8 Reserved3:2; + u8 PAM:1; + u8 Mcast:1; + u8 Bcast:1; + u8 Reserved4:1; + + u32 TSFL; +}; + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c new file mode 100644 index 0000000000..7061f1cf4d --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "r8192E_phyreg.h" +#include "r8192E_phy.h" +#include "r8190P_rtl8256.h" + +void rtl92e_set_bandwidth(struct net_device *dev, + enum ht_channel_width bandwidth) +{ + u8 eRFPath; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->card_8192_version != VERSION_8190_BD && + priv->card_8192_version != VERSION_8190_BE) { + netdev_warn(dev, "%s(): Unknown HW version.\n", __func__); + return; + } + + for (eRFPath = 0; eRFPath < priv->num_total_rf_path; eRFPath++) { + switch (bandwidth) { + case HT_CHANNEL_WIDTH_20: + rtl92e_set_rf_reg(dev, (enum rf90_radio_path)eRFPath, + 0x0b, bMask12Bits, 0x100); + rtl92e_set_rf_reg(dev, (enum rf90_radio_path)eRFPath, + 0x2c, bMask12Bits, 0x3d7); + rtl92e_set_rf_reg(dev, (enum rf90_radio_path)eRFPath, + 0x0e, bMask12Bits, 0x021); + break; + case HT_CHANNEL_WIDTH_20_40: + rtl92e_set_rf_reg(dev, (enum rf90_radio_path)eRFPath, + 0x0b, bMask12Bits, 0x300); + rtl92e_set_rf_reg(dev, (enum rf90_radio_path)eRFPath, + 0x2c, bMask12Bits, 0x3ff); + rtl92e_set_rf_reg(dev, (enum rf90_radio_path)eRFPath, + 0x0e, bMask12Bits, 0x0e1); + break; + default: + netdev_err(dev, "%s(): Unknown bandwidth: %#X\n", + __func__, bandwidth); + break; + } + } +} + +bool rtl92e_config_rf(struct net_device *dev) +{ + u32 u4RegValue = 0; + u8 eRFPath; + bool rtStatus = true; + struct bb_reg_definition *pPhyReg; + struct r8192_priv *priv = rtllib_priv(dev); + u32 RegOffSetToBeCheck = 0x3; + u32 RegValueToBeCheck = 0x7f1; + u32 RF3_Final_Value = 0; + u8 ConstRetryTimes = 5, RetryTimes = 5; + u8 ret = 0; + + priv->num_total_rf_path = RTL819X_TOTAL_RF_PATH; + + for (eRFPath = (enum rf90_radio_path)RF90_PATH_A; + eRFPath < priv->num_total_rf_path; eRFPath++) { + pPhyReg = &priv->phy_reg_def[eRFPath]; + + switch (eRFPath) { + case RF90_PATH_A: + u4RegValue = rtl92e_get_bb_reg(dev, pPhyReg->rfintfs, + bRFSI_RFENV); + break; + case RF90_PATH_B: + u4RegValue = rtl92e_get_bb_reg(dev, pPhyReg->rfintfs, + bRFSI_RFENV << 16); + break; + } + + rtl92e_set_bb_reg(dev, pPhyReg->rfintfe, bRFSI_RFENV << 16, 0x1); + + rtl92e_set_bb_reg(dev, pPhyReg->rfintfo, bRFSI_RFENV, 0x1); + + rtl92e_set_bb_reg(dev, pPhyReg->rfHSSIPara2, + b3WireAddressLength, 0x0); + rtl92e_set_bb_reg(dev, pPhyReg->rfHSSIPara2, + b3WireDataLength, 0x0); + + rtl92e_set_rf_reg(dev, (enum rf90_radio_path)eRFPath, 0x0, + bMask12Bits, 0xbf); + + rtStatus = rtl92e_check_bb_and_rf(dev, HW90_BLOCK_RF, + (enum rf90_radio_path)eRFPath); + if (!rtStatus) { + netdev_err(dev, "%s(): Failed to check RF Path %d.\n", + __func__, eRFPath); + goto fail; + } + + RetryTimes = ConstRetryTimes; + RF3_Final_Value = 0; + while (RF3_Final_Value != RegValueToBeCheck && + RetryTimes != 0) { + ret = rtl92e_config_rf_path(dev, + (enum rf90_radio_path)eRFPath); + RF3_Final_Value = rtl92e_get_rf_reg(dev, + (enum rf90_radio_path)eRFPath, + RegOffSetToBeCheck, + bMask12Bits); + RetryTimes--; + } + + switch (eRFPath) { + case RF90_PATH_A: + rtl92e_set_bb_reg(dev, pPhyReg->rfintfs, bRFSI_RFENV, + u4RegValue); + break; + case RF90_PATH_B: + rtl92e_set_bb_reg(dev, pPhyReg->rfintfs, + bRFSI_RFENV << 16, u4RegValue); + break; + } + + if (ret) { + netdev_err(dev, + "%s(): Failed to initialize RF Path %d.\n", + __func__, eRFPath); + goto fail; + } + } + return true; + +fail: + return false; +} + +void rtl92e_set_cck_tx_power(struct net_device *dev, u8 powerlevel) +{ + u32 TxAGC = 0; + struct r8192_priv *priv = rtllib_priv(dev); + + TxAGC = powerlevel; + if (priv->dynamic_tx_low_pwr) { + if (priv->customer_id == RT_CID_819X_NETCORE) + TxAGC = 0x22; + else + TxAGC += priv->cck_pwr_enl; + } + if (TxAGC > 0x24) + TxAGC = 0x24; + rtl92e_set_bb_reg(dev, rTxAGC_CCK_Mcs32, bTxAGCRateCCK, TxAGC); +} + +void rtl92e_set_ofdm_tx_power(struct net_device *dev, u8 powerlevel) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 writeVal, powerBase0, powerBase1, writeVal_tmp; + u8 index = 0; + u16 RegOffset[6] = {0xe00, 0xe04, 0xe10, 0xe14, 0xe18, 0xe1c}; + u8 byte0, byte1, byte2, byte3; + + powerBase0 = powerlevel + priv->legacy_ht_tx_pwr_diff; + powerBase0 = (powerBase0 << 24) | (powerBase0 << 16) | + (powerBase0 << 8) | powerBase0; + powerBase1 = powerlevel; + powerBase1 = (powerBase1 << 24) | (powerBase1 << 16) | + (powerBase1 << 8) | powerBase1; + + for (index = 0; index < 6; index++) { + writeVal = (u32)(priv->mcs_tx_pwr_level_org_offset[index] + + ((index < 2) ? powerBase0 : powerBase1)); + byte0 = writeVal & 0x7f; + byte1 = (writeVal & 0x7f00) >> 8; + byte2 = (writeVal & 0x7f0000) >> 16; + byte3 = (writeVal & 0x7f000000) >> 24; + if (byte0 > 0x24) + byte0 = 0x24; + if (byte1 > 0x24) + byte1 = 0x24; + if (byte2 > 0x24) + byte2 = 0x24; + if (byte3 > 0x24) + byte3 = 0x24; + + if (index == 3) { + writeVal_tmp = (byte3 << 24) | (byte2 << 16) | + (byte1 << 8) | byte0; + priv->pwr_track = writeVal_tmp; + } + + if (priv->dynamic_tx_high_pwr) + writeVal = 0x03030303; + else + writeVal = (byte3 << 24) | (byte2 << 16) | + (byte1 << 8) | byte0; + rtl92e_set_bb_reg(dev, RegOffset[index], 0x7f7f7f7f, writeVal); + } +} diff --git a/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h new file mode 100644 index 0000000000..3c52e2b430 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8190P_rtl8256.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef RTL8225H +#define RTL8225H + +#define RTL819X_TOTAL_RF_PATH 2 +void rtl92e_set_bandwidth(struct net_device *dev, + enum ht_channel_width bandwidth); +bool rtl92e_config_rf(struct net_device *dev); +void rtl92e_set_cck_tx_power(struct net_device *dev, u8 powerlevel); +void rtl92e_set_ofdm_tx_power(struct net_device *dev, u8 powerlevel); + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c new file mode 100644 index 0000000000..eba8364d0f --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "r8192E_hw.h" +#include "r8192E_cmdpkt.h" + +bool rtl92e_send_cmd_pkt(struct net_device *dev, u32 type, const void *data, + u32 len) +{ + bool rt_status = true; + struct r8192_priv *priv = rtllib_priv(dev); + u16 frag_length = 0, frag_offset = 0; + struct sk_buff *skb; + unsigned char *seg_ptr; + struct cb_desc *tcb_desc; + u8 bLastIniPkt; + + struct tx_fwinfo_8190pci *pTxFwInfo = NULL; + + do { + if ((len - frag_offset) > CMDPACKET_FRAG_SIZE) { + frag_length = CMDPACKET_FRAG_SIZE; + bLastIniPkt = 0; + + } else { + frag_length = (u16)(len - frag_offset); + bLastIniPkt = 1; + } + + if (type == DESC_PACKET_TYPE_NORMAL) + skb = dev_alloc_skb(frag_length + + priv->rtllib->tx_headroom + 4); + else + skb = dev_alloc_skb(frag_length + 4); + + if (!skb) { + rt_status = false; + goto Failed; + } + + memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev)); + tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE); + tcb_desc->queue_index = TXCMD_QUEUE; + tcb_desc->bCmdOrInit = type; + tcb_desc->bLastIniPkt = bLastIniPkt; + + if (type == DESC_PACKET_TYPE_NORMAL) { + tcb_desc->pkt_size = frag_length; + + seg_ptr = skb_put(skb, priv->rtllib->tx_headroom); + pTxFwInfo = (struct tx_fwinfo_8190pci *)seg_ptr; + memset(pTxFwInfo, 0, sizeof(struct tx_fwinfo_8190pci)); + memset(pTxFwInfo, 0x12, 8); + } else { + tcb_desc->txbuf_size = frag_length; + } + + skb_put_data(skb, data, frag_length); + + if (type == DESC_PACKET_TYPE_INIT && + (!priv->rtllib->check_nic_enough_desc(dev, TXCMD_QUEUE) || + (!skb_queue_empty(&priv->rtllib->skb_waitQ[TXCMD_QUEUE])) || + (priv->rtllib->queue_stop))) { + skb_queue_tail(&priv->rtllib->skb_waitQ[TXCMD_QUEUE], + skb); + } else { + priv->rtllib->softmac_hard_start_xmit(skb, dev); + } + + data += frag_length; + frag_offset += frag_length; + + } while (frag_offset < len); + + rtl92e_writeb(dev, TP_POLL, TP_POLL_CQ); +Failed: + return rt_status; +} diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h new file mode 100644 index 0000000000..c63909199e --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_cmdpkt.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef R819XUSB_CMDPKT_H +#define R819XUSB_CMDPKT_H + +bool rtl92e_send_cmd_pkt(struct net_device *dev, u32 type, const void *data, + u32 len); +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c new file mode 100644 index 0000000000..58e90b7772 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c @@ -0,0 +1,2010 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "r8192E_phy.h" +#include "r8192E_phyreg.h" +#include "r8190P_rtl8256.h" +#include "r8192E_cmdpkt.h" +#include "rtl_dm.h" +#include "rtl_wx.h" + +static int WDCAPARA_ADD[] = {EDCAPARA_BE, EDCAPARA_BK, EDCAPARA_VI, + EDCAPARA_VO}; + +void rtl92e_start_beacon(struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + struct rtllib_network *net = &priv->rtllib->current_network; + u16 BcnTimeCfg = 0; + u16 BcnCW = 6; + u16 BcnIFS = 0xf; + + rtl92e_irq_disable(dev); + + rtl92e_writew(dev, ATIMWND, 2); + + rtl92e_writew(dev, BCN_INTERVAL, net->beacon_interval); + rtl92e_writew(dev, BCN_DRV_EARLY_INT, 10); + rtl92e_writew(dev, BCN_DMATIME, 256); + + rtl92e_writeb(dev, BCN_ERR_THRESH, 100); + + BcnTimeCfg |= BcnCW << BCN_TCFG_CW_SHIFT; + BcnTimeCfg |= BcnIFS << BCN_TCFG_IFS; + rtl92e_writew(dev, BCN_TCFG, BcnTimeCfg); + rtl92e_irq_enable(dev); +} + +static void _rtl92e_update_msr(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 msr; + + msr = rtl92e_readb(dev, MSR); + msr &= ~MSR_LINK_MASK; + + switch (priv->rtllib->iw_mode) { + case IW_MODE_INFRA: + if (priv->rtllib->link_state == MAC80211_LINKED) + msr |= MSR_LINK_MANAGED; + break; + case IW_MODE_ADHOC: + if (priv->rtllib->link_state == MAC80211_LINKED) + msr |= MSR_LINK_ADHOC; + break; + default: + break; + } + + rtl92e_writeb(dev, MSR, msr); +} + +void rtl92e_set_reg(struct net_device *dev, u8 variable, u8 *val) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + switch (variable) { + case HW_VAR_BSSID: + /* BSSIDR 2 byte alignment */ + rtl92e_writew(dev, BSSIDR, *(u16 *)val); + rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(val + 2)); + break; + + case HW_VAR_MEDIA_STATUS: + { + enum rt_op_mode OpMode = *((enum rt_op_mode *)(val)); + u8 btMsr = rtl92e_readb(dev, MSR); + + btMsr &= 0xfc; + + switch (OpMode) { + case RT_OP_MODE_INFRASTRUCTURE: + btMsr |= MSR_INFRA; + break; + + case RT_OP_MODE_IBSS: + btMsr |= MSR_ADHOC; + break; + + case RT_OP_MODE_AP: + btMsr |= MSR_AP; + break; + + default: + btMsr |= MSR_NOLINK; + break; + } + + rtl92e_writeb(dev, MSR, btMsr); + } + break; + + case HW_VAR_CECHK_BSSID: + { + u32 RegRCR, Type; + + Type = val[0]; + RegRCR = rtl92e_readl(dev, RCR); + priv->receive_config = RegRCR; + + if (Type) + RegRCR |= (RCR_CBSSID); + else + RegRCR &= (~RCR_CBSSID); + + rtl92e_writel(dev, RCR, RegRCR); + priv->receive_config = RegRCR; + } + break; + + case HW_VAR_SLOT_TIME: + + priv->slot_time = val[0]; + rtl92e_writeb(dev, SLOT_TIME, val[0]); + + break; + + case HW_VAR_ACK_PREAMBLE: + { + u32 regTmp; + + priv->short_preamble = (bool)*val; + regTmp = priv->basic_rate; + if (priv->short_preamble) + regTmp |= BRSR_AckShortPmb; + rtl92e_writel(dev, RRSR, regTmp); + break; + } + + case HW_VAR_CPU_RST: + rtl92e_writel(dev, CPU_GEN, ((u32 *)(val))[0]); + break; + + case HW_VAR_AC_PARAM: + { + u8 pAcParam = *val; + u32 eACI = pAcParam; + u8 u1bAIFS; + u32 u4bAcParam; + u8 mode = priv->rtllib->mode; + struct rtllib_qos_parameters *qop = + &priv->rtllib->current_network.qos_data.parameters; + + u1bAIFS = qop->aifs[pAcParam] * + ((mode & (WIRELESS_MODE_G | WIRELESS_MODE_N_24G)) ? 9 : 20) + aSifsTime; + + rtl92e_dm_init_edca_turbo(dev); + + u4bAcParam = (le16_to_cpu(qop->tx_op_limit[pAcParam]) << + AC_PARAM_TXOP_LIMIT_OFFSET) | + ((le16_to_cpu(qop->cw_max[pAcParam])) << + AC_PARAM_ECW_MAX_OFFSET) | + ((le16_to_cpu(qop->cw_min[pAcParam])) << + AC_PARAM_ECW_MIN_OFFSET) | + (((u32)u1bAIFS) << AC_PARAM_AIFS_OFFSET); + + switch (eACI) { + case AC1_BK: + rtl92e_writel(dev, EDCAPARA_BK, u4bAcParam); + break; + + case AC0_BE: + rtl92e_writel(dev, EDCAPARA_BE, u4bAcParam); + break; + + case AC2_VI: + rtl92e_writel(dev, EDCAPARA_VI, u4bAcParam); + break; + + case AC3_VO: + rtl92e_writel(dev, EDCAPARA_VO, u4bAcParam); + break; + + default: + netdev_info(dev, "SetHwReg8185(): invalid ACI: %d !\n", + eACI); + break; + } + priv->rtllib->SetHwRegHandler(dev, HW_VAR_ACM_CTRL, + &pAcParam); + break; + } + + case HW_VAR_ACM_CTRL: + { + struct rtllib_qos_parameters *qos_parameters = + &priv->rtllib->current_network.qos_data.parameters; + u8 pAcParam = *val; + u32 eACI = pAcParam; + union aci_aifsn *pAciAifsn = (union aci_aifsn *)& + (qos_parameters->aifs[0]); + u8 acm = pAciAifsn->f.acm; + u8 AcmCtrl = rtl92e_readb(dev, ACM_HW_CTRL); + + if (acm) { + switch (eACI) { + case AC0_BE: + AcmCtrl |= ACM_HW_BEQ_EN; + break; + + case AC2_VI: + AcmCtrl |= ACM_HW_VIQ_EN; + break; + + case AC3_VO: + AcmCtrl |= ACM_HW_VOQ_EN; + break; + } + } else { + switch (eACI) { + case AC0_BE: + AcmCtrl &= (~ACM_HW_BEQ_EN); + break; + + case AC2_VI: + AcmCtrl &= (~ACM_HW_VIQ_EN); + break; + + case AC3_VO: + AcmCtrl &= (~ACM_HW_BEQ_EN); + break; + + default: + break; + } + } + rtl92e_writeb(dev, ACM_HW_CTRL, AcmCtrl); + break; + } + + case HW_VAR_SIFS: + rtl92e_writeb(dev, SIFS, val[0]); + rtl92e_writeb(dev, SIFS + 1, val[0]); + break; + + case HW_VAR_RF_TIMING: + { + u8 Rf_Timing = *val; + + rtl92e_writeb(dev, rFPGA0_RFTiming1, Rf_Timing); + break; + } + + default: + break; + } +} + +static void _rtl92e_read_eeprom_info(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + const u8 bMac_Tmp_Addr[ETH_ALEN] = {0x00, 0xe0, 0x4c, 0x00, 0x00, 0x01}; + u8 tempval; + u8 ICVer8192, ICVer8256; + u16 i, usValue, IC_Version; + u16 EEPROMId; + + EEPROMId = rtl92e_eeprom_read(dev, 0); + if (EEPROMId != RTL8190_EEPROM_ID) { + netdev_err(dev, "%s(): Invalid EEPROM ID: %x\n", __func__, + EEPROMId); + priv->autoload_fail_flag = true; + } else { + priv->autoload_fail_flag = false; + } + + if (!priv->autoload_fail_flag) { + priv->eeprom_vid = rtl92e_eeprom_read(dev, EEPROM_VID >> 1); + priv->eeprom_did = rtl92e_eeprom_read(dev, EEPROM_DID >> 1); + + usValue = rtl92e_eeprom_read(dev, + (EEPROM_Customer_ID >> 1)) >> 8; + priv->eeprom_customer_id = usValue & 0xff; + usValue = rtl92e_eeprom_read(dev, + EEPROM_ICVersion_ChannelPlan >> 1); + priv->eeprom_chnl_plan = usValue & 0xff; + IC_Version = (usValue & 0xff00) >> 8; + + ICVer8192 = IC_Version & 0xf; + ICVer8256 = (IC_Version & 0xf0) >> 4; + if (ICVer8192 == 0x2) { + if (ICVer8256 == 0x5) + priv->card_8192_version = VERSION_8190_BE; + } + switch (priv->card_8192_version) { + case VERSION_8190_BD: + case VERSION_8190_BE: + break; + default: + priv->card_8192_version = VERSION_8190_BD; + break; + } + } else { + priv->card_8192_version = VERSION_8190_BD; + priv->eeprom_vid = 0; + priv->eeprom_did = 0; + priv->eeprom_customer_id = 0; + priv->eeprom_chnl_plan = 0; + } + + if (!priv->autoload_fail_flag) { + u8 addr[ETH_ALEN]; + + for (i = 0; i < 6; i += 2) { + usValue = rtl92e_eeprom_read(dev, + (EEPROM_NODE_ADDRESS_BYTE_0 + i) >> 1); + *(u16 *)(&addr[i]) = usValue; + } + eth_hw_addr_set(dev, addr); + } else { + eth_hw_addr_set(dev, bMac_Tmp_Addr); + } + + if (priv->card_8192_version > VERSION_8190_BD) + priv->tx_pwr_data_read_from_eeprom = true; + else + priv->tx_pwr_data_read_from_eeprom = false; + + if (priv->card_8192_version > VERSION_8190_BD) { + if (!priv->autoload_fail_flag) { + tempval = (rtl92e_eeprom_read(dev, + (EEPROM_RFInd_PowerDiff >> 1))) & 0xff; + priv->eeprom_legacy_ht_tx_pwr_diff = tempval & 0xf; + } else { + priv->eeprom_legacy_ht_tx_pwr_diff = 0x04; + } + + if (!priv->autoload_fail_flag) + priv->eeprom_thermal_meter = ((rtl92e_eeprom_read(dev, + (EEPROM_ThermalMeter >> 1))) & + 0xff00) >> 8; + else + priv->eeprom_thermal_meter = EEPROM_Default_ThermalMeter; + priv->tssi_13dBm = priv->eeprom_thermal_meter * 100; + + if (priv->epromtype == EEPROM_93C46) { + if (!priv->autoload_fail_flag) { + usValue = rtl92e_eeprom_read(dev, + EEPROM_TxPwDiff_CrystalCap >> 1); + priv->eeprom_ant_pwr_diff = usValue & 0x0fff; + priv->eeprom_crystal_cap = (usValue & 0xf000) + >> 12; + } else { + priv->eeprom_ant_pwr_diff = + EEPROM_Default_AntTxPowerDiff; + priv->eeprom_crystal_cap = + EEPROM_Default_TxPwDiff_CrystalCap; + } + + for (i = 0; i < 14; i += 2) { + if (!priv->autoload_fail_flag) + usValue = rtl92e_eeprom_read(dev, + (EEPROM_TxPwIndex_CCK + i) >> 1); + else + usValue = EEPROM_Default_TxPower; + *((u16 *)(&priv->eeprom_tx_pwr_level_cck[i])) = + usValue; + } + for (i = 0; i < 14; i += 2) { + if (!priv->autoload_fail_flag) + usValue = rtl92e_eeprom_read(dev, + (EEPROM_TxPwIndex_OFDM_24G + i) >> 1); + else + usValue = EEPROM_Default_TxPower; + *((u16 *)(&priv->eeprom_tx_pwr_level_ofdm24g[i])) + = usValue; + } + } + if (priv->epromtype == EEPROM_93C46) { + for (i = 0; i < 14; i++) { + priv->tx_pwr_level_cck[i] = + priv->eeprom_tx_pwr_level_cck[i]; + priv->tx_pwr_level_ofdm_24g[i] = + priv->eeprom_tx_pwr_level_ofdm24g[i]; + } + priv->legacy_ht_tx_pwr_diff = + priv->eeprom_legacy_ht_tx_pwr_diff; + priv->antenna_tx_pwr_diff[0] = priv->eeprom_ant_pwr_diff & 0xf; + priv->antenna_tx_pwr_diff[1] = (priv->eeprom_ant_pwr_diff & + 0xf0) >> 4; + priv->antenna_tx_pwr_diff[2] = (priv->eeprom_ant_pwr_diff & + 0xf00) >> 8; + priv->crystal_cap = priv->eeprom_crystal_cap; + priv->thermal_meter[0] = priv->eeprom_thermal_meter & 0xf; + priv->thermal_meter[1] = (priv->eeprom_thermal_meter & + 0xf0) >> 4; + } else if (priv->epromtype == EEPROM_93C56) { + priv->legacy_ht_tx_pwr_diff = + priv->eeprom_legacy_ht_tx_pwr_diff; + priv->antenna_tx_pwr_diff[0] = 0; + priv->antenna_tx_pwr_diff[1] = 0; + priv->antenna_tx_pwr_diff[2] = 0; + priv->crystal_cap = priv->eeprom_crystal_cap; + priv->thermal_meter[0] = priv->eeprom_thermal_meter & 0xf; + priv->thermal_meter[1] = (priv->eeprom_thermal_meter & + 0xf0) >> 4; + } + } + + rtl92e_init_adaptive_rate(dev); + + priv->chnl_plan = priv->eeprom_chnl_plan; + + switch (priv->eeprom_customer_id) { + case EEPROM_CID_NetCore: + priv->customer_id = RT_CID_819X_NETCORE; + break; + case EEPROM_CID_TOSHIBA: + priv->customer_id = RT_CID_TOSHIBA; + if (priv->eeprom_chnl_plan & 0x80) + priv->chnl_plan = priv->eeprom_chnl_plan & 0x7f; + else + priv->chnl_plan = 0x0; + break; + } + + if (priv->chnl_plan > CHANNEL_PLAN_LEN - 1) + priv->chnl_plan = 0; + priv->chnl_plan = COUNTRY_CODE_WORLD_WIDE_13; + + if (priv->eeprom_vid == 0x1186 && priv->eeprom_did == 0x3304) + priv->rtllib->bSupportRemoteWakeUp = true; + else + priv->rtllib->bSupportRemoteWakeUp = false; +} + +void rtl92e_get_eeprom_size(struct net_device *dev) +{ + u16 curCR; + struct r8192_priv *priv = rtllib_priv(dev); + + curCR = rtl92e_readw(dev, EPROM_CMD); + priv->epromtype = (curCR & EPROM_CMD_9356SEL) ? EEPROM_93C56 : + EEPROM_93C46; + _rtl92e_read_eeprom_info(dev); +} + +static void _rtl92e_hwconfig(struct net_device *dev) +{ + u32 regRATR = 0, regRRSR = 0; + u8 regBwOpMode = 0, regTmp = 0; + struct r8192_priv *priv = rtllib_priv(dev); + + switch (priv->rtllib->mode) { + case WIRELESS_MODE_B: + regBwOpMode = BW_OPMODE_20MHZ; + regRATR = RATE_ALL_CCK; + regRRSR = RATE_ALL_CCK; + break; + case WIRELESS_MODE_AUTO: + case WIRELESS_MODE_N_24G: + regBwOpMode = BW_OPMODE_20MHZ; + regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG | + RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS; + regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG; + break; + case WIRELESS_MODE_G: + default: + regBwOpMode = BW_OPMODE_20MHZ; + regRATR = RATE_ALL_CCK | RATE_ALL_OFDM_AG; + regRRSR = RATE_ALL_CCK | RATE_ALL_OFDM_AG; + break; + } + + rtl92e_writeb(dev, BW_OPMODE, regBwOpMode); + { + u32 ratr_value; + + ratr_value = regRATR; + ratr_value &= ~(RATE_ALL_OFDM_2SS); + rtl92e_writel(dev, RATR0, ratr_value); + rtl92e_writeb(dev, UFWP, 1); + } + regTmp = rtl92e_readb(dev, 0x313); + regRRSR = ((regTmp) << 24) | (regRRSR & 0x00ffffff); + rtl92e_writel(dev, RRSR, regRRSR); + + rtl92e_writew(dev, RETRY_LIMIT, + priv->short_retry_limit << RETRY_LIMIT_SHORT_SHIFT | + priv->long_retry_limit << RETRY_LIMIT_LONG_SHIFT); +} + +bool rtl92e_start_adapter(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 ulRegRead; + bool rtStatus = true; + u8 tmpvalue; + u8 ICVersion, SwitchingRegulatorOutput; + bool bfirmwareok = true; + u32 tmpRegA, TempCCk; + int i = 0; + u32 retry_times = 0; + + priv->being_init_adapter = true; + +start: + rtl92e_reset_desc_ring(dev); + priv->rf_mode = RF_OP_By_SW_3wire; + if (priv->rst_progress == RESET_TYPE_NORESET) { + rtl92e_writeb(dev, ANAPAR, 0x37); + mdelay(500); + } + priv->fw_info->status = FW_STATUS_0_INIT; + + ulRegRead = rtl92e_readl(dev, CPU_GEN); + if (priv->fw_info->status == FW_STATUS_0_INIT) + ulRegRead |= CPU_GEN_SYSTEM_RESET; + else if (priv->fw_info->status == FW_STATUS_5_READY) + ulRegRead |= CPU_GEN_FIRMWARE_RESET; + else + netdev_err(dev, "%s(): undefined firmware state: %d.\n", + __func__, priv->fw_info->status); + + rtl92e_writel(dev, CPU_GEN, ulRegRead); + + ICVersion = rtl92e_readb(dev, IC_VERRSION); + if (ICVersion >= 0x4) { + SwitchingRegulatorOutput = rtl92e_readb(dev, SWREGULATOR); + if (SwitchingRegulatorOutput != 0xb8) { + rtl92e_writeb(dev, SWREGULATOR, 0xa8); + mdelay(1); + rtl92e_writeb(dev, SWREGULATOR, 0xb8); + } + } + rtStatus = rtl92e_config_bb(dev); + if (!rtStatus) { + netdev_warn(dev, "%s(): Failed to configure BB\n", __func__); + return rtStatus; + } + + priv->loopback_mode = RTL819X_NO_LOOPBACK; + if (priv->rst_progress == RESET_TYPE_NORESET) { + ulRegRead = rtl92e_readl(dev, CPU_GEN); + if (priv->loopback_mode == RTL819X_NO_LOOPBACK) + ulRegRead = (ulRegRead & CPU_GEN_NO_LOOPBACK_MSK) | + CPU_GEN_NO_LOOPBACK_SET; + else if (priv->loopback_mode == RTL819X_MAC_LOOPBACK) + ulRegRead |= CPU_CCK_LOOPBACK; + else + netdev_err(dev, "%s: Invalid loopback mode setting.\n", + __func__); + + rtl92e_writel(dev, CPU_GEN, ulRegRead); + + udelay(500); + } + _rtl92e_hwconfig(dev); + rtl92e_writeb(dev, CMDR, CR_RE | CR_TE); + + rtl92e_writeb(dev, PCIF, ((MXDMA2_NO_LIMIT << MXDMA2_RX_SHIFT) | + (MXDMA2_NO_LIMIT << MXDMA2_TX_SHIFT))); + rtl92e_writel(dev, MAC0, ((u32 *)dev->dev_addr)[0]); + rtl92e_writew(dev, MAC4, ((u16 *)(dev->dev_addr + 4))[0]); + rtl92e_writel(dev, RCR, priv->receive_config); + + rtl92e_writel(dev, RQPN1, NUM_OF_PAGE_IN_FW_QUEUE_BK << + RSVD_FW_QUEUE_PAGE_BK_SHIFT | + NUM_OF_PAGE_IN_FW_QUEUE_BE << + RSVD_FW_QUEUE_PAGE_BE_SHIFT | + NUM_OF_PAGE_IN_FW_QUEUE_VI << + RSVD_FW_QUEUE_PAGE_VI_SHIFT | + NUM_OF_PAGE_IN_FW_QUEUE_VO << + RSVD_FW_QUEUE_PAGE_VO_SHIFT); + rtl92e_writel(dev, RQPN2, NUM_OF_PAGE_IN_FW_QUEUE_MGNT << + RSVD_FW_QUEUE_PAGE_MGNT_SHIFT); + rtl92e_writel(dev, RQPN3, APPLIED_RESERVED_QUEUE_IN_FW | + NUM_OF_PAGE_IN_FW_QUEUE_BCN << + RSVD_FW_QUEUE_PAGE_BCN_SHIFT | + NUM_OF_PAGE_IN_FW_QUEUE_PUB << + RSVD_FW_QUEUE_PAGE_PUB_SHIFT); + + rtl92e_tx_enable(dev); + rtl92e_rx_enable(dev); + ulRegRead = (0xFFF00000 & rtl92e_readl(dev, RRSR)) | + RATE_ALL_OFDM_AG | RATE_ALL_CCK; + rtl92e_writel(dev, RRSR, ulRegRead); + rtl92e_writel(dev, RATR0 + 4 * 7, (RATE_ALL_OFDM_AG | RATE_ALL_CCK)); + + rtl92e_writeb(dev, ACK_TIMEOUT, 0x30); + + if (priv->rst_progress == RESET_TYPE_NORESET) + rtl92e_set_wireless_mode(dev, priv->rtllib->mode); + rtl92e_cam_reset(dev); + { + u8 SECR_value = 0x0; + + SECR_value |= SCR_TxEncEnable; + SECR_value |= SCR_RxDecEnable; + SECR_value |= SCR_NoSKMC; + rtl92e_writeb(dev, SECR, SECR_value); + } + rtl92e_writew(dev, ATIMWND, 2); + rtl92e_writew(dev, BCN_INTERVAL, 100); + + for (i = 0; i < QOS_QUEUE_NUM; i++) + rtl92e_writel(dev, WDCAPARA_ADD[i], 0x005e4332); + + rtl92e_writeb(dev, 0xbe, 0xc0); + + rtl92e_config_mac(dev); + + if (priv->card_8192_version > VERSION_8190_BD) { + rtl92e_get_tx_power(dev); + rtl92e_set_tx_power(dev, priv->chan); + } + + tmpvalue = rtl92e_readb(dev, IC_VERRSION); + priv->ic_cut = tmpvalue; + + bfirmwareok = rtl92e_init_fw(dev); + if (!bfirmwareok) { + if (retry_times < 10) { + retry_times++; + goto start; + } else { + rtStatus = false; + goto end; + } + } + + if (priv->rst_progress == RESET_TYPE_NORESET) { + rtStatus = rtl92e_config_rf(dev); + if (!rtStatus) { + netdev_info(dev, "RF Config failed\n"); + return rtStatus; + } + } + + rtl92e_set_bb_reg(dev, rFPGA0_RFMOD, bCCKEn, 0x1); + rtl92e_set_bb_reg(dev, rFPGA0_RFMOD, bOFDMEn, 0x1); + + rtl92e_writeb(dev, 0x87, 0x0); + + if (priv->rtllib->rf_off_reason > RF_CHANGE_BY_PS) { + rtl92e_set_rf_state(dev, rf_off, priv->rtllib->rf_off_reason); + } else if (priv->rtllib->rf_off_reason >= RF_CHANGE_BY_IPS) { + rtl92e_set_rf_state(dev, rf_off, priv->rtllib->rf_off_reason); + } else { + priv->rtllib->rf_power_state = rf_on; + priv->rtllib->rf_off_reason = 0; + } + + if (priv->rtllib->FwRWRF) + priv->rf_mode = RF_OP_By_FW; + else + priv->rf_mode = RF_OP_By_SW_3wire; + + if (priv->rst_progress == RESET_TYPE_NORESET) { + rtl92e_dm_init_txpower_tracking(dev); + + if (priv->ic_cut >= IC_VersionCut_D) { + tmpRegA = rtl92e_get_bb_reg(dev, rOFDM0_XATxIQImbalance, + bMaskDWord); + rtl92e_get_bb_reg(dev, rOFDM0_XCTxIQImbalance, bMaskDWord); + + for (i = 0; i < TX_BB_GAIN_TABLE_LEN; i++) { + if (tmpRegA == dm_tx_bb_gain[i]) { + priv->rfa_txpowertrackingindex = i; + priv->rfa_txpowertrackingindex_real = i; + priv->rfa_txpowertracking_default = + priv->rfa_txpowertrackingindex; + break; + } + } + + TempCCk = rtl92e_get_bb_reg(dev, rCCK0_TxFilter1, + bMaskByte2); + + for (i = 0; i < CCK_TX_BB_GAIN_TABLE_LEN; i++) { + if (TempCCk == dm_cck_tx_bb_gain[i][0]) { + priv->cck_present_attn_20m_def = i; + break; + } + } + priv->cck_present_attn_40m_def = 0; + priv->cck_present_attn_diff = 0; + priv->cck_present_attn = + priv->cck_present_attn_20m_def; + priv->btxpower_tracking = false; + } + } + rtl92e_irq_enable(dev); +end: + priv->being_init_adapter = false; + return rtStatus; +} + +static void _rtl92e_net_update(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_network *net; + u16 BcnTimeCfg = 0, BcnCW = 6, BcnIFS = 0xf; + u16 rate_config = 0; + + net = &priv->rtllib->current_network; + rtl92e_config_rate(dev, &rate_config); + priv->dot11_current_preamble_mode = PREAMBLE_AUTO; + priv->basic_rate = rate_config &= 0x15f; + rtl92e_writew(dev, BSSIDR, *(u16 *)net->bssid); + rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(net->bssid + 2)); + + if (priv->rtllib->iw_mode == IW_MODE_ADHOC) { + rtl92e_writew(dev, ATIMWND, 2); + rtl92e_writew(dev, BCN_DMATIME, 256); + rtl92e_writew(dev, BCN_INTERVAL, net->beacon_interval); + rtl92e_writew(dev, BCN_DRV_EARLY_INT, 10); + rtl92e_writeb(dev, BCN_ERR_THRESH, 100); + + BcnTimeCfg |= (BcnCW << BCN_TCFG_CW_SHIFT); + BcnTimeCfg |= BcnIFS << BCN_TCFG_IFS; + + rtl92e_writew(dev, BCN_TCFG, BcnTimeCfg); + } +} + +void rtl92e_link_change(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + if (!priv->up) + return; + + if (ieee->link_state == MAC80211_LINKED) { + _rtl92e_net_update(dev); + rtl92e_update_ratr_table(dev); + if ((ieee->pairwise_key_type == KEY_TYPE_WEP40) || + (ieee->pairwise_key_type == KEY_TYPE_WEP104)) + rtl92e_enable_hw_security_config(dev); + } else { + rtl92e_writeb(dev, 0x173, 0); + } + _rtl92e_update_msr(dev); + + if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC) { + u32 reg; + + reg = rtl92e_readl(dev, RCR); + if (priv->rtllib->link_state == MAC80211_LINKED) { + if (ieee->intel_promiscuous_md_info.promiscuous_on) + ; + else + priv->receive_config = reg |= RCR_CBSSID; + } else { + priv->receive_config = reg &= ~RCR_CBSSID; + } + + rtl92e_writel(dev, RCR, reg); + } +} + +void rtl92e_set_monitor_mode(struct net_device *dev, bool bAllowAllDA, + bool WriteIntoReg) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (bAllowAllDA) + priv->receive_config |= RCR_AAP; + else + priv->receive_config &= ~RCR_AAP; + + if (WriteIntoReg) + rtl92e_writel(dev, RCR, priv->receive_config); +} + +static u8 _rtl92e_rate_mgn_to_hw(u8 rate) +{ + u8 ret = DESC90_RATE1M; + + switch (rate) { + case MGN_1M: + ret = DESC90_RATE1M; + break; + case MGN_2M: + ret = DESC90_RATE2M; + break; + case MGN_5_5M: + ret = DESC90_RATE5_5M; + break; + case MGN_11M: + ret = DESC90_RATE11M; + break; + case MGN_6M: + ret = DESC90_RATE6M; + break; + case MGN_9M: + ret = DESC90_RATE9M; + break; + case MGN_12M: + ret = DESC90_RATE12M; + break; + case MGN_18M: + ret = DESC90_RATE18M; + break; + case MGN_24M: + ret = DESC90_RATE24M; + break; + case MGN_36M: + ret = DESC90_RATE36M; + break; + case MGN_48M: + ret = DESC90_RATE48M; + break; + case MGN_54M: + ret = DESC90_RATE54M; + break; + case MGN_MCS0: + ret = DESC90_RATEMCS0; + break; + case MGN_MCS1: + ret = DESC90_RATEMCS1; + break; + case MGN_MCS2: + ret = DESC90_RATEMCS2; + break; + case MGN_MCS3: + ret = DESC90_RATEMCS3; + break; + case MGN_MCS4: + ret = DESC90_RATEMCS4; + break; + case MGN_MCS5: + ret = DESC90_RATEMCS5; + break; + case MGN_MCS6: + ret = DESC90_RATEMCS6; + break; + case MGN_MCS7: + ret = DESC90_RATEMCS7; + break; + case MGN_MCS8: + ret = DESC90_RATEMCS8; + break; + case MGN_MCS9: + ret = DESC90_RATEMCS9; + break; + case MGN_MCS10: + ret = DESC90_RATEMCS10; + break; + case MGN_MCS11: + ret = DESC90_RATEMCS11; + break; + case MGN_MCS12: + ret = DESC90_RATEMCS12; + break; + case MGN_MCS13: + ret = DESC90_RATEMCS13; + break; + case MGN_MCS14: + ret = DESC90_RATEMCS14; + break; + case MGN_MCS15: + ret = DESC90_RATEMCS15; + break; + case (0x80 | 0x20): + ret = DESC90_RATEMCS32; + break; + default: + break; + } + return ret; +} + +static u8 _rtl92e_hw_queue_to_fw_queue(struct net_device *dev, u8 QueueID, + u8 priority) +{ + u8 QueueSelect = 0x0; + + switch (QueueID) { + case BE_QUEUE: + QueueSelect = QSLT_BE; + break; + + case BK_QUEUE: + QueueSelect = QSLT_BK; + break; + + case VO_QUEUE: + QueueSelect = QSLT_VO; + break; + + case VI_QUEUE: + QueueSelect = QSLT_VI; + break; + case MGNT_QUEUE: + QueueSelect = QSLT_MGNT; + break; + case BEACON_QUEUE: + QueueSelect = QSLT_BEACON; + break; + case TXCMD_QUEUE: + QueueSelect = QSLT_CMD; + break; + case HIGH_QUEUE: + QueueSelect = QSLT_HIGH; + break; + default: + netdev_warn(dev, "%s(): Impossible Queue Selection: %d\n", + __func__, QueueID); + break; + } + return QueueSelect; +} + +static u8 _rtl92e_query_is_short(u8 TxHT, u8 TxRate, struct cb_desc *tcb_desc) +{ + u8 tmp_Short; + + tmp_Short = (TxHT == 1) ? ((tcb_desc->bUseShortGI) ? 1 : 0) : + ((tcb_desc->bUseShortPreamble) ? 1 : 0); + if (TxHT == 1 && TxRate != DESC90_RATEMCS15) + tmp_Short = 0; + + return tmp_Short; +} + +void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc, + struct cb_desc *cb_desc, struct sk_buff *skb) +{ + struct r8192_priv *priv = rtllib_priv(dev); + dma_addr_t mapping; + struct tx_fwinfo_8190pci *pTxFwInfo; + + pTxFwInfo = (struct tx_fwinfo_8190pci *)skb->data; + memset(pTxFwInfo, 0, sizeof(struct tx_fwinfo_8190pci)); + pTxFwInfo->TxHT = (cb_desc->data_rate & 0x80) ? 1 : 0; + pTxFwInfo->TxRate = _rtl92e_rate_mgn_to_hw(cb_desc->data_rate); + pTxFwInfo->EnableCPUDur = cb_desc->bTxEnableFwCalcDur; + pTxFwInfo->Short = _rtl92e_query_is_short(pTxFwInfo->TxHT, + pTxFwInfo->TxRate, cb_desc); + + if (cb_desc->bAMPDUEnable) { + pTxFwInfo->AllowAggregation = 1; + pTxFwInfo->RxMF = cb_desc->ampdu_factor; + pTxFwInfo->RxAMD = cb_desc->ampdu_density; + } else { + pTxFwInfo->AllowAggregation = 0; + pTxFwInfo->RxMF = 0; + pTxFwInfo->RxAMD = 0; + } + + pTxFwInfo->RtsEnable = (cb_desc->bRTSEnable) ? 1 : 0; + pTxFwInfo->CtsEnable = (cb_desc->bCTSEnable) ? 1 : 0; + pTxFwInfo->RtsSTBC = (cb_desc->bRTSSTBC) ? 1 : 0; + pTxFwInfo->RtsHT = (cb_desc->rts_rate & 0x80) ? 1 : 0; + pTxFwInfo->RtsRate = _rtl92e_rate_mgn_to_hw(cb_desc->rts_rate); + pTxFwInfo->RtsBandwidth = 0; + pTxFwInfo->RtsSubcarrier = cb_desc->RTSSC; + pTxFwInfo->RtsShort = (pTxFwInfo->RtsHT == 0) ? + (cb_desc->bRTSUseShortPreamble ? 1 : 0) : + (cb_desc->bRTSUseShortGI ? 1 : 0); + if (priv->current_chnl_bw == HT_CHANNEL_WIDTH_20_40) { + if (cb_desc->bPacketBW) { + pTxFwInfo->TxBandwidth = 1; + pTxFwInfo->TxSubCarrier = 0; + } else { + pTxFwInfo->TxBandwidth = 0; + pTxFwInfo->TxSubCarrier = priv->n_cur_40mhz_prime_sc; + } + } else { + pTxFwInfo->TxBandwidth = 0; + pTxFwInfo->TxSubCarrier = 0; + } + + memset((u8 *)pdesc, 0, 12); + + mapping = dma_map_single(&priv->pdev->dev, skb->data, skb->len, + DMA_TO_DEVICE); + if (dma_mapping_error(&priv->pdev->dev, mapping)) { + netdev_err(dev, "%s(): DMA Mapping error\n", __func__); + return; + } + + pdesc->LINIP = 0; + pdesc->CmdInit = 1; + pdesc->Offset = sizeof(struct tx_fwinfo_8190pci) + 8; + pdesc->PktSize = skb->len - sizeof(struct tx_fwinfo_8190pci); + + pdesc->SecCAMID = 0; + pdesc->RATid = cb_desc->ratr_index; + + pdesc->NoEnc = 1; + pdesc->SecType = 0x0; + if (cb_desc->bHwSec) { + static u8 tmp; + + if (!tmp) + tmp = 1; + switch (priv->rtllib->pairwise_key_type) { + case KEY_TYPE_WEP40: + case KEY_TYPE_WEP104: + pdesc->SecType = 0x1; + pdesc->NoEnc = 0; + break; + case KEY_TYPE_TKIP: + pdesc->SecType = 0x2; + pdesc->NoEnc = 0; + break; + case KEY_TYPE_CCMP: + pdesc->SecType = 0x3; + pdesc->NoEnc = 0; + break; + case KEY_TYPE_NA: + pdesc->SecType = 0x0; + pdesc->NoEnc = 1; + break; + } + } + + pdesc->PktId = 0x0; + + pdesc->QueueSelect = _rtl92e_hw_queue_to_fw_queue(dev, + cb_desc->queue_index, + cb_desc->priority); + pdesc->TxFWInfoSize = sizeof(struct tx_fwinfo_8190pci); + + pdesc->DISFB = cb_desc->tx_dis_rate_fallback; + pdesc->USERATE = cb_desc->tx_use_drv_assinged_rate; + + pdesc->FirstSeg = 1; + pdesc->LastSeg = 1; + pdesc->TxBufferSize = skb->len; + + pdesc->TxBuffAddr = mapping; +} + +void rtl92e_fill_tx_cmd_desc(struct net_device *dev, struct tx_desc_cmd *entry, + struct cb_desc *cb_desc, struct sk_buff *skb) +{ + struct r8192_priv *priv = rtllib_priv(dev); + dma_addr_t mapping = dma_map_single(&priv->pdev->dev, skb->data, + skb->len, DMA_TO_DEVICE); + + if (dma_mapping_error(&priv->pdev->dev, mapping)) + netdev_err(dev, "%s(): DMA Mapping error\n", __func__); + memset(entry, 0, 12); + entry->LINIP = cb_desc->bLastIniPkt; + entry->FirstSeg = 1; + entry->LastSeg = 1; + if (cb_desc->bCmdOrInit == DESC_PACKET_TYPE_INIT) { + entry->CmdInit = DESC_PACKET_TYPE_INIT; + } else { + struct tx_desc *entry_tmp = (struct tx_desc *)entry; + + entry_tmp->CmdInit = DESC_PACKET_TYPE_NORMAL; + entry_tmp->Offset = sizeof(struct tx_fwinfo_8190pci) + 8; + entry_tmp->PktSize = cb_desc->pkt_size + entry_tmp->Offset; + entry_tmp->QueueSelect = QSLT_CMD; + entry_tmp->TxFWInfoSize = 0x08; + entry_tmp->RATid = DESC_PACKET_TYPE_INIT; + } + entry->TxBufferSize = skb->len; + entry->TxBuffAddr = mapping; + entry->OWN = 1; +} + +static u8 _rtl92e_rate_hw_to_mgn(bool bIsHT, u8 rate) +{ + u8 ret_rate = 0x02; + + if (!bIsHT) { + switch (rate) { + case DESC90_RATE1M: + ret_rate = MGN_1M; + break; + case DESC90_RATE2M: + ret_rate = MGN_2M; + break; + case DESC90_RATE5_5M: + ret_rate = MGN_5_5M; + break; + case DESC90_RATE11M: + ret_rate = MGN_11M; + break; + case DESC90_RATE6M: + ret_rate = MGN_6M; + break; + case DESC90_RATE9M: + ret_rate = MGN_9M; + break; + case DESC90_RATE12M: + ret_rate = MGN_12M; + break; + case DESC90_RATE18M: + ret_rate = MGN_18M; + break; + case DESC90_RATE24M: + ret_rate = MGN_24M; + break; + case DESC90_RATE36M: + ret_rate = MGN_36M; + break; + case DESC90_RATE48M: + ret_rate = MGN_48M; + break; + case DESC90_RATE54M: + ret_rate = MGN_54M; + break; + } + + } else { + switch (rate) { + case DESC90_RATEMCS0: + ret_rate = MGN_MCS0; + break; + case DESC90_RATEMCS1: + ret_rate = MGN_MCS1; + break; + case DESC90_RATEMCS2: + ret_rate = MGN_MCS2; + break; + case DESC90_RATEMCS3: + ret_rate = MGN_MCS3; + break; + case DESC90_RATEMCS4: + ret_rate = MGN_MCS4; + break; + case DESC90_RATEMCS5: + ret_rate = MGN_MCS5; + break; + case DESC90_RATEMCS6: + ret_rate = MGN_MCS6; + break; + case DESC90_RATEMCS7: + ret_rate = MGN_MCS7; + break; + case DESC90_RATEMCS8: + ret_rate = MGN_MCS8; + break; + case DESC90_RATEMCS9: + ret_rate = MGN_MCS9; + break; + case DESC90_RATEMCS10: + ret_rate = MGN_MCS10; + break; + case DESC90_RATEMCS11: + ret_rate = MGN_MCS11; + break; + case DESC90_RATEMCS12: + ret_rate = MGN_MCS12; + break; + case DESC90_RATEMCS13: + ret_rate = MGN_MCS13; + break; + case DESC90_RATEMCS14: + ret_rate = MGN_MCS14; + break; + case DESC90_RATEMCS15: + ret_rate = MGN_MCS15; + break; + case DESC90_RATEMCS32: + ret_rate = 0x80 | 0x20; + break; + } + } + + return ret_rate; +} + +static long _rtl92e_signal_scale_mapping(struct r8192_priv *priv, long currsig) +{ + long retsig; + + if (currsig >= 61 && currsig <= 100) + retsig = 90 + ((currsig - 60) / 4); + else if (currsig >= 41 && currsig <= 60) + retsig = 78 + ((currsig - 40) / 2); + else if (currsig >= 31 && currsig <= 40) + retsig = 66 + (currsig - 30); + else if (currsig >= 21 && currsig <= 30) + retsig = 54 + (currsig - 20); + else if (currsig >= 5 && currsig <= 20) + retsig = 42 + (((currsig - 5) * 2) / 3); + else if (currsig == 4) + retsig = 36; + else if (currsig == 3) + retsig = 27; + else if (currsig == 2) + retsig = 18; + else if (currsig == 1) + retsig = 9; + else + retsig = currsig; + + return retsig; +} + +#define rx_hal_is_cck_rate(_pdrvinfo)\ + ((_pdrvinfo->RxRate == DESC90_RATE1M ||\ + _pdrvinfo->RxRate == DESC90_RATE2M ||\ + _pdrvinfo->RxRate == DESC90_RATE5_5M ||\ + _pdrvinfo->RxRate == DESC90_RATE11M) &&\ + !_pdrvinfo->RxHT) + +static void _rtl92e_query_rxphystatus( + struct r8192_priv *priv, + struct rtllib_rx_stats *pstats, + struct rx_desc *pdesc, + struct rx_fwinfo *pdrvinfo, + struct rtllib_rx_stats *precord_stats, + bool bpacket_match_bssid, + bool bpacket_toself, + bool bPacketBeacon, + bool bToSelfBA + ) +{ + struct phy_sts_ofdm_819xpci *pofdm_buf; + struct phy_sts_cck_819xpci *pcck_buf; + u8 *prxpkt; + u8 i, max_spatial_stream, tmp_rxevm; + s8 rx_pwr[4], rx_pwr_all = 0; + s8 rx_evmX; + u8 evm, pwdb_all; + u32 RSSI, total_rssi = 0; + u8 is_cck_rate = 0; + u8 rf_rx_num = 0; + static u8 check_reg824; + static u32 reg824_bit9; + + is_cck_rate = rx_hal_is_cck_rate(pdrvinfo); + memset(precord_stats, 0, sizeof(struct rtllib_rx_stats)); + pstats->bPacketMatchBSSID = precord_stats->bPacketMatchBSSID = + bpacket_match_bssid; + pstats->bPacketToSelf = precord_stats->bPacketToSelf = bpacket_toself; + pstats->bIsCCK = precord_stats->bIsCCK = is_cck_rate; + pstats->bPacketBeacon = precord_stats->bPacketBeacon = bPacketBeacon; + pstats->bToSelfBA = precord_stats->bToSelfBA = bToSelfBA; + if (check_reg824 == 0) { + reg824_bit9 = rtl92e_get_bb_reg(priv->rtllib->dev, + rFPGA0_XA_HSSIParameter2, + 0x200); + check_reg824 = 1; + } + + prxpkt = (u8 *)pdrvinfo; + + prxpkt += sizeof(struct rx_fwinfo); + + pcck_buf = (struct phy_sts_cck_819xpci *)prxpkt; + pofdm_buf = (struct phy_sts_ofdm_819xpci *)prxpkt; + + pstats->RxMIMOSignalQuality[0] = -1; + pstats->RxMIMOSignalQuality[1] = -1; + precord_stats->RxMIMOSignalQuality[0] = -1; + precord_stats->RxMIMOSignalQuality[1] = -1; + + if (is_cck_rate) { + u8 report; + + if (!reg824_bit9) { + report = pcck_buf->cck_agc_rpt & 0xc0; + report >>= 6; + switch (report) { + case 0x3: + rx_pwr_all = -35 - (pcck_buf->cck_agc_rpt & + 0x3e); + break; + case 0x2: + rx_pwr_all = -23 - (pcck_buf->cck_agc_rpt & + 0x3e); + break; + case 0x1: + rx_pwr_all = -11 - (pcck_buf->cck_agc_rpt & + 0x3e); + break; + case 0x0: + rx_pwr_all = 8 - (pcck_buf->cck_agc_rpt & 0x3e); + break; + } + } else { + report = pcck_buf->cck_agc_rpt & 0x60; + report >>= 5; + switch (report) { + case 0x3: + rx_pwr_all = -35 - + ((pcck_buf->cck_agc_rpt & + 0x1f) << 1); + break; + case 0x2: + rx_pwr_all = -23 - + ((pcck_buf->cck_agc_rpt & + 0x1f) << 1); + break; + case 0x1: + rx_pwr_all = -11 - + ((pcck_buf->cck_agc_rpt & + 0x1f) << 1); + break; + case 0x0: + rx_pwr_all = -8 - + ((pcck_buf->cck_agc_rpt & + 0x1f) << 1); + break; + } + } + + pwdb_all = rtl92e_rx_db_to_percent(rx_pwr_all); + pstats->RxPWDBAll = precord_stats->RxPWDBAll = pwdb_all; + pstats->RecvSignalPower = rx_pwr_all; + + if (bpacket_match_bssid) { + u8 sq; + + if (pstats->RxPWDBAll > 40) { + sq = 100; + } else { + sq = pcck_buf->sq_rpt; + + if (pcck_buf->sq_rpt > 64) + sq = 0; + else if (pcck_buf->sq_rpt < 20) + sq = 100; + else + sq = ((64 - sq) * 100) / 44; + } + pstats->SignalQuality = sq; + precord_stats->SignalQuality = sq; + pstats->RxMIMOSignalQuality[0] = sq; + precord_stats->RxMIMOSignalQuality[0] = sq; + pstats->RxMIMOSignalQuality[1] = -1; + precord_stats->RxMIMOSignalQuality[1] = -1; + } + } else { + for (i = RF90_PATH_A; i < RF90_PATH_MAX; i++) { + if (priv->brfpath_rxenable[i]) + rf_rx_num++; + + rx_pwr[i] = ((pofdm_buf->trsw_gain_X[i] & 0x3F) * + 2) - 110; + + RSSI = rtl92e_rx_db_to_percent(rx_pwr[i]); + if (priv->brfpath_rxenable[i]) + total_rssi += RSSI; + + if (bpacket_match_bssid) { + pstats->RxMIMOSignalStrength[i] = RSSI; + precord_stats->RxMIMOSignalStrength[i] = RSSI; + } + } + + rx_pwr_all = (((pofdm_buf->pwdb_all) >> 1) & 0x7f) - 106; + pwdb_all = rtl92e_rx_db_to_percent(rx_pwr_all); + + pstats->RxPWDBAll = precord_stats->RxPWDBAll = pwdb_all; + pstats->RxPower = precord_stats->RxPower = rx_pwr_all; + pstats->RecvSignalPower = rx_pwr_all; + if (pdrvinfo->RxHT && pdrvinfo->RxRate >= DESC90_RATEMCS8 && + pdrvinfo->RxRate <= DESC90_RATEMCS15) + max_spatial_stream = 2; + else + max_spatial_stream = 1; + + for (i = 0; i < max_spatial_stream; i++) { + tmp_rxevm = pofdm_buf->rxevm_X[i]; + rx_evmX = (s8)(tmp_rxevm); + + rx_evmX /= 2; + + evm = rtl92e_evm_db_to_percent(rx_evmX); + if (bpacket_match_bssid) { + if (i == 0) { + pstats->SignalQuality = evm & 0xff; + precord_stats->SignalQuality = evm & 0xff; + } + pstats->RxMIMOSignalQuality[i] = evm & 0xff; + precord_stats->RxMIMOSignalQuality[i] = evm & 0xff; + } + } + } + + if (is_cck_rate) { + pstats->SignalStrength = precord_stats->SignalStrength = + _rtl92e_signal_scale_mapping(priv, + (long)pwdb_all); + + } else { + if (rf_rx_num != 0) + pstats->SignalStrength = precord_stats->SignalStrength = + _rtl92e_signal_scale_mapping(priv, + (long)(total_rssi /= rf_rx_num)); + } +} + +static void _rtl92e_process_phyinfo(struct r8192_priv *priv, u8 *buffer, + struct rtllib_rx_stats *prev_st, + struct rtllib_rx_stats *curr_st) +{ + bool bcheck = false; + u8 rfpath; + u32 ij, tmp_val; + static u32 slide_rssi_index, slide_rssi_statistics; + static u32 slide_evm_index, slide_evm_statistics; + static u32 last_rssi, last_evm; + static u32 slide_beacon_adc_pwdb_index; + static u32 slide_beacon_adc_pwdb_statistics; + static u32 last_beacon_adc_pwdb; + struct rtllib_hdr_3addr *hdr; + u16 sc; + unsigned int seq; + + hdr = (struct rtllib_hdr_3addr *)buffer; + sc = le16_to_cpu(hdr->seq_ctl); + seq = WLAN_GET_SEQ_SEQ(sc); + curr_st->Seq_Num = seq; + if (!prev_st->bIsAMPDU) + bcheck = true; + + if (slide_rssi_statistics++ >= PHY_RSSI_SLID_WIN_MAX) { + slide_rssi_statistics = PHY_RSSI_SLID_WIN_MAX; + last_rssi = priv->stats.slide_signal_strength[slide_rssi_index]; + priv->stats.slide_rssi_total -= last_rssi; + } + priv->stats.slide_rssi_total += prev_st->SignalStrength; + + priv->stats.slide_signal_strength[slide_rssi_index++] = + prev_st->SignalStrength; + if (slide_rssi_index >= PHY_RSSI_SLID_WIN_MAX) + slide_rssi_index = 0; + + tmp_val = priv->stats.slide_rssi_total / slide_rssi_statistics; + priv->stats.signal_strength = rtl92e_translate_to_dbm(priv, tmp_val); + curr_st->rssi = priv->stats.signal_strength; + if (!prev_st->bPacketMatchBSSID) { + if (!prev_st->bToSelfBA) + return; + } + + if (!bcheck) + return; + + if (!prev_st->bIsCCK && prev_st->bPacketToSelf) { + for (rfpath = RF90_PATH_A; rfpath < priv->num_total_rf_path; rfpath++) { + if (priv->stats.rx_rssi_percentage[rfpath] == 0) { + priv->stats.rx_rssi_percentage[rfpath] = + prev_st->RxMIMOSignalStrength[rfpath]; + } + if (prev_st->RxMIMOSignalStrength[rfpath] > + priv->stats.rx_rssi_percentage[rfpath]) { + priv->stats.rx_rssi_percentage[rfpath] = + ((priv->stats.rx_rssi_percentage[rfpath] + * (RX_SMOOTH - 1)) + + (prev_st->RxMIMOSignalStrength + [rfpath])) / (RX_SMOOTH); + priv->stats.rx_rssi_percentage[rfpath] = + priv->stats.rx_rssi_percentage[rfpath] + + 1; + } else { + priv->stats.rx_rssi_percentage[rfpath] = + ((priv->stats.rx_rssi_percentage[rfpath] * + (RX_SMOOTH - 1)) + + (prev_st->RxMIMOSignalStrength[rfpath])) / + (RX_SMOOTH); + } + } + } + + if (prev_st->bPacketBeacon) { + if (slide_beacon_adc_pwdb_statistics++ >= + PHY_Beacon_RSSI_SLID_WIN_MAX) { + slide_beacon_adc_pwdb_statistics = + PHY_Beacon_RSSI_SLID_WIN_MAX; + last_beacon_adc_pwdb = priv->stats.slide_beacon_pwdb + [slide_beacon_adc_pwdb_index]; + priv->stats.slide_beacon_total -= last_beacon_adc_pwdb; + } + priv->stats.slide_beacon_total += prev_st->RxPWDBAll; + priv->stats.slide_beacon_pwdb[slide_beacon_adc_pwdb_index] = + prev_st->RxPWDBAll; + slide_beacon_adc_pwdb_index++; + if (slide_beacon_adc_pwdb_index >= PHY_Beacon_RSSI_SLID_WIN_MAX) + slide_beacon_adc_pwdb_index = 0; + prev_st->RxPWDBAll = priv->stats.slide_beacon_total / + slide_beacon_adc_pwdb_statistics; + if (prev_st->RxPWDBAll >= 3) + prev_st->RxPWDBAll -= 3; + } + if (prev_st->bPacketToSelf || prev_st->bPacketBeacon || + prev_st->bToSelfBA) { + if (priv->undecorated_smoothed_pwdb < 0) + priv->undecorated_smoothed_pwdb = prev_st->RxPWDBAll; + if (prev_st->RxPWDBAll > (u32)priv->undecorated_smoothed_pwdb) { + priv->undecorated_smoothed_pwdb = + (((priv->undecorated_smoothed_pwdb) * + (RX_SMOOTH - 1)) + + (prev_st->RxPWDBAll)) / (RX_SMOOTH); + priv->undecorated_smoothed_pwdb = + priv->undecorated_smoothed_pwdb + 1; + } else { + priv->undecorated_smoothed_pwdb = + (((priv->undecorated_smoothed_pwdb) * + (RX_SMOOTH - 1)) + + (prev_st->RxPWDBAll)) / (RX_SMOOTH); + } + rtl92e_update_rx_statistics(priv, prev_st); + } + + if (prev_st->SignalQuality != 0) { + if (prev_st->bPacketToSelf || prev_st->bPacketBeacon || + prev_st->bToSelfBA) { + if (slide_evm_statistics++ >= PHY_RSSI_SLID_WIN_MAX) { + slide_evm_statistics = PHY_RSSI_SLID_WIN_MAX; + last_evm = + priv->stats.slide_evm[slide_evm_index]; + priv->stats.slide_evm_total -= last_evm; + } + + priv->stats.slide_evm_total += prev_st->SignalQuality; + + priv->stats.slide_evm[slide_evm_index++] = + prev_st->SignalQuality; + if (slide_evm_index >= PHY_RSSI_SLID_WIN_MAX) + slide_evm_index = 0; + + tmp_val = priv->stats.slide_evm_total / + slide_evm_statistics; + priv->stats.last_signal_strength_inpercent = tmp_val; + } + + if (prev_st->bPacketToSelf || + prev_st->bPacketBeacon || + prev_st->bToSelfBA) { + for (ij = 0; ij < 2; ij++) { + if (prev_st->RxMIMOSignalQuality[ij] != -1) { + if (priv->stats.rx_evm_percentage[ij] == 0) + priv->stats.rx_evm_percentage[ij] = + prev_st->RxMIMOSignalQuality[ij]; + priv->stats.rx_evm_percentage[ij] = + ((priv->stats.rx_evm_percentage[ij] * + (RX_SMOOTH - 1)) + + (prev_st->RxMIMOSignalQuality[ij])) / + (RX_SMOOTH); + } + } + } + } +} + +static void _rtl92e_translate_rx_signal_stats(struct net_device *dev, + struct sk_buff *skb, + struct rtllib_rx_stats *pstats, + struct rx_desc *pdesc, + struct rx_fwinfo *pdrvinfo) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + bool bpacket_match_bssid, bpacket_toself; + bool bPacketBeacon = false; + struct rtllib_hdr_3addr *hdr; + bool bToSelfBA = false; + static struct rtllib_rx_stats previous_stats; + u16 fc, type; + u8 *tmp_buf; + u8 *praddr; + + tmp_buf = skb->data + pstats->RxDrvInfoSize + pstats->RxBufShift; + + hdr = (struct rtllib_hdr_3addr *)tmp_buf; + fc = le16_to_cpu(hdr->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + praddr = hdr->addr1; + + bpacket_match_bssid = + ((type != RTLLIB_FTYPE_CTL) && + ether_addr_equal(priv->rtllib->current_network.bssid, + (fc & RTLLIB_FCTL_TODS) ? hdr->addr1 : + (fc & RTLLIB_FCTL_FROMDS) ? hdr->addr2 : + hdr->addr3) && + (!pstats->bHwError) && (!pstats->bCRC) && (!pstats->bICV)); + bpacket_toself = bpacket_match_bssid && /* check this */ + ether_addr_equal(praddr, priv->rtllib->dev->dev_addr); + if (WLAN_FC_GET_FRAMETYPE(fc) == RTLLIB_STYPE_BEACON) + bPacketBeacon = true; + _rtl92e_process_phyinfo(priv, tmp_buf, &previous_stats, pstats); + _rtl92e_query_rxphystatus(priv, pstats, pdesc, pdrvinfo, + &previous_stats, bpacket_match_bssid, + bpacket_toself, bPacketBeacon, bToSelfBA); + rtl92e_copy_mpdu_stats(pstats, &previous_stats); +} + +static void _rtl92e_update_received_rate_histogram_stats( + struct net_device *dev, + struct rtllib_rx_stats *pstats) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + u32 rcvType = 1; + u32 rateIndex; + + if (pstats->bCRC) + rcvType = 2; + else if (pstats->bICV) + rcvType = 3; + + switch (pstats->rate) { + case MGN_1M: + rateIndex = 0; + break; + case MGN_2M: + rateIndex = 1; + break; + case MGN_5_5M: + rateIndex = 2; + break; + case MGN_11M: + rateIndex = 3; + break; + case MGN_6M: + rateIndex = 4; + break; + case MGN_9M: + rateIndex = 5; + break; + case MGN_12M: + rateIndex = 6; + break; + case MGN_18M: + rateIndex = 7; + break; + case MGN_24M: + rateIndex = 8; + break; + case MGN_36M: + rateIndex = 9; + break; + case MGN_48M: + rateIndex = 10; + break; + case MGN_54M: + rateIndex = 11; + break; + case MGN_MCS0: + rateIndex = 12; + break; + case MGN_MCS1: + rateIndex = 13; + break; + case MGN_MCS2: + rateIndex = 14; + break; + case MGN_MCS3: + rateIndex = 15; + break; + case MGN_MCS4: + rateIndex = 16; + break; + case MGN_MCS5: + rateIndex = 17; + break; + case MGN_MCS6: + rateIndex = 18; + break; + case MGN_MCS7: + rateIndex = 19; + break; + case MGN_MCS8: + rateIndex = 20; + break; + case MGN_MCS9: + rateIndex = 21; + break; + case MGN_MCS10: + rateIndex = 22; + break; + case MGN_MCS11: + rateIndex = 23; + break; + case MGN_MCS12: + rateIndex = 24; + break; + case MGN_MCS13: + rateIndex = 25; + break; + case MGN_MCS14: + rateIndex = 26; + break; + case MGN_MCS15: + rateIndex = 27; + break; + default: + rateIndex = 28; + break; + } + priv->stats.received_rate_histogram[0][rateIndex]++; + priv->stats.received_rate_histogram[rcvType][rateIndex]++; +} + +bool rtl92e_get_rx_stats(struct net_device *dev, struct rtllib_rx_stats *stats, + struct rx_desc *pdesc, struct sk_buff *skb) +{ + struct rx_fwinfo *pDrvInfo = NULL; + + stats->bICV = pdesc->ICV; + stats->bCRC = pdesc->CRC32; + stats->bHwError = pdesc->CRC32 | pdesc->ICV; + + stats->Length = pdesc->Length; + if (stats->Length < 24) + stats->bHwError |= 1; + + if (stats->bHwError) { + stats->bShift = false; + return false; + } + + stats->RxDrvInfoSize = pdesc->RxDrvInfoSize; + stats->RxBufShift = (pdesc->Shift) & 0x03; + stats->Decrypted = !pdesc->SWDec; + + pDrvInfo = (struct rx_fwinfo *)(skb->data + stats->RxBufShift); + + stats->rate = _rtl92e_rate_hw_to_mgn((bool)pDrvInfo->RxHT, + pDrvInfo->RxRate); + stats->bShortPreamble = pDrvInfo->SPLCP; + + _rtl92e_update_received_rate_histogram_stats(dev, stats); + + stats->bIsAMPDU = (pDrvInfo->PartAggr == 1); + stats->bFirstMPDU = (pDrvInfo->PartAggr == 1) && + (pDrvInfo->FirstAGGR == 1); + + stats->TimeStampLow = pDrvInfo->TSFL; + stats->TimeStampHigh = rtl92e_readl(dev, TSFR + 4); + + rtl92e_update_rx_pkt_timestamp(dev, stats); + + if ((stats->RxBufShift + stats->RxDrvInfoSize) > 0) + stats->bShift = 1; + + stats->RxIs40MHzPacket = pDrvInfo->BW; + + _rtl92e_translate_rx_signal_stats(dev, skb, stats, pdesc, pDrvInfo); + skb_trim(skb, skb->len - S_CRC_LEN); + + + stats->packetlength = stats->Length - 4; + stats->fraglength = stats->packetlength; + stats->fragoffset = 0; + stats->ntotalfrag = 1; + return true; +} + +void rtl92e_stop_adapter(struct net_device *dev, bool reset) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int i; + u8 OpMode; + u8 u1bTmp; + u32 ulRegRead; + + OpMode = RT_OP_MODE_NO_LINK; + priv->rtllib->SetHwRegHandler(dev, HW_VAR_MEDIA_STATUS, &OpMode); + + if (!priv->rtllib->bSupportRemoteWakeUp) { + u1bTmp = 0x0; + rtl92e_writeb(dev, CMDR, u1bTmp); + } + + mdelay(20); + + if (!reset) { + mdelay(150); + + priv->hw_rf_off_action = 2; + + if (!priv->rtllib->bSupportRemoteWakeUp) { + rtl92e_set_rf_off(dev); + ulRegRead = rtl92e_readl(dev, CPU_GEN); + ulRegRead |= CPU_GEN_SYSTEM_RESET; + rtl92e_writel(dev, CPU_GEN, ulRegRead); + } else { + rtl92e_writel(dev, WFCRC0, 0xffffffff); + rtl92e_writel(dev, WFCRC1, 0xffffffff); + rtl92e_writel(dev, WFCRC2, 0xffffffff); + + rtl92e_writeb(dev, PMR, 0x5); + rtl92e_writeb(dev, MAC_BLK_CTRL, 0xa); + } + } + + for (i = 0; i < MAX_QUEUE_SIZE; i++) + skb_queue_purge(&priv->rtllib->skb_waitQ[i]); + + skb_queue_purge(&priv->skb_queue); +} + +void rtl92e_update_ratr_table(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + u8 *pMcsRate = ieee->dot11ht_oper_rate_set; + u32 ratr_value = 0; + u16 rate_config = 0; + u8 rate_index = 0; + + rtl92e_config_rate(dev, &rate_config); + ratr_value = rate_config | *pMcsRate << 12; + switch (ieee->mode) { + case WIRELESS_MODE_B: + ratr_value &= 0x0000000F; + break; + case WIRELESS_MODE_G: + case WIRELESS_MODE_G | WIRELESS_MODE_B: + ratr_value &= 0x00000FF7; + break; + case WIRELESS_MODE_N_24G: + if (ieee->ht_info->peer_mimo_ps == 0) + ratr_value &= 0x0007F007; + else + ratr_value &= 0x000FF007; + break; + default: + break; + } + ratr_value &= 0x0FFFFFFF; + if (ieee->ht_info->cur_tx_bw40mhz && + ieee->ht_info->bCurShortGI40MHz) + ratr_value |= 0x80000000; + else if (!ieee->ht_info->cur_tx_bw40mhz && + ieee->ht_info->bCurShortGI20MHz) + ratr_value |= 0x80000000; + rtl92e_writel(dev, RATR0 + rate_index * 4, ratr_value); + rtl92e_writeb(dev, UFWP, 1); +} + +void +rtl92e_init_variables(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + strscpy(priv->nick, "rtl8192E", sizeof(priv->nick)); + + priv->rtllib->softmac_features = IEEE_SOFTMAC_SCAN | + IEEE_SOFTMAC_ASSOCIATE | IEEE_SOFTMAC_PROBERQ | + IEEE_SOFTMAC_PROBERS | IEEE_SOFTMAC_TX_QUEUE; + + priv->rtllib->tx_headroom = sizeof(struct tx_fwinfo_8190pci); + + priv->short_retry_limit = 0x30; + priv->long_retry_limit = 0x30; + + priv->receive_config = RCR_ADD3 | + RCR_AMF | RCR_ADF | + RCR_AICV | + RCR_AB | RCR_AM | RCR_APM | + RCR_AAP | ((u32)7 << RCR_MXDMA_OFFSET) | + ((u32)7 << RCR_FIFO_OFFSET) | RCR_ONLYERLPKT; + + priv->irq_mask[0] = (u32)(IMR_ROK | IMR_VODOK | IMR_VIDOK | + IMR_BEDOK | IMR_BKDOK | IMR_HCCADOK | + IMR_MGNTDOK | IMR_COMDOK | IMR_HIGHDOK | + IMR_BDOK | IMR_RXCMDOK | IMR_TIMEOUT0 | + IMR_RDU | IMR_RXFOVW | IMR_TXFOVW | + IMR_BcnInt | IMR_TBDOK | IMR_TBDER); + + priv->bfirst_after_down = false; +} + +void rtl92e_enable_irq(struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + + priv->irq_enabled = 1; + + rtl92e_writel(dev, INTA_MASK, priv->irq_mask[0]); +} + +void rtl92e_disable_irq(struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + + rtl92e_writel(dev, INTA_MASK, 0); + + priv->irq_enabled = 0; +} + +void rtl92e_enable_rx(struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + + rtl92e_writel(dev, RDQDA, priv->rx_ring_dma[RX_MPDU_QUEUE]); +} + +static const u32 TX_DESC_BASE[] = { + BKQDA, BEQDA, VIQDA, VOQDA, HCCAQDA, CQDA, MQDA, HQDA, BQDA +}; + +void rtl92e_enable_tx(struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + u32 i; + + for (i = 0; i < MAX_TX_QUEUE_COUNT; i++) + rtl92e_writel(dev, TX_DESC_BASE[i], priv->tx_ring[i].dma); +} + +void rtl92e_ack_irq(struct net_device *dev, u32 *p_inta) +{ + *p_inta = rtl92e_readl(dev, ISR); + rtl92e_writel(dev, ISR, *p_inta); +} + +bool rtl92e_is_rx_stuck(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u16 RegRxCounter = rtl92e_readw(dev, 0x130); + bool bStuck = false; + static u8 rx_chk_cnt; + u32 SlotIndex = 0, TotalRxStuckCount = 0; + u8 i; + u8 SilentResetRxSoltNum = 4; + + rx_chk_cnt++; + if (priv->undecorated_smoothed_pwdb >= (RATE_ADAPTIVE_TH_HIGH + 5)) { + rx_chk_cnt = 0; + } else if ((priv->undecorated_smoothed_pwdb < (RATE_ADAPTIVE_TH_HIGH + 5)) + && (((priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) && + (priv->undecorated_smoothed_pwdb >= RATE_ADAPTIVE_TH_LOW_40M)) + || ((priv->current_chnl_bw == HT_CHANNEL_WIDTH_20) && + (priv->undecorated_smoothed_pwdb >= RATE_ADAPTIVE_TH_LOW_20M)))) { + if (rx_chk_cnt < 2) + return bStuck; + rx_chk_cnt = 0; + } else if ((((priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) && + (priv->undecorated_smoothed_pwdb < RATE_ADAPTIVE_TH_LOW_40M)) || + ((priv->current_chnl_bw == HT_CHANNEL_WIDTH_20) && + (priv->undecorated_smoothed_pwdb < RATE_ADAPTIVE_TH_LOW_20M))) && + priv->undecorated_smoothed_pwdb >= VERY_LOW_RSSI) { + if (rx_chk_cnt < 4) + return bStuck; + rx_chk_cnt = 0; + } else { + if (rx_chk_cnt < 8) + return bStuck; + rx_chk_cnt = 0; + } + + + SlotIndex = (priv->silent_reset_rx_slot_index++) % SilentResetRxSoltNum; + + if (priv->rx_ctr == RegRxCounter) { + priv->silent_reset_rx_stuck_event[SlotIndex] = 1; + + for (i = 0; i < SilentResetRxSoltNum; i++) + TotalRxStuckCount += priv->silent_reset_rx_stuck_event[i]; + + if (TotalRxStuckCount == SilentResetRxSoltNum) { + bStuck = true; + for (i = 0; i < SilentResetRxSoltNum; i++) + TotalRxStuckCount += + priv->silent_reset_rx_stuck_event[i]; + } + } else { + priv->silent_reset_rx_stuck_event[SlotIndex] = 0; + } + + priv->rx_ctr = RegRxCounter; + + return bStuck; +} + +bool rtl92e_is_tx_stuck(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + bool bStuck = false; + u16 RegTxCounter = rtl92e_readw(dev, 0x128); + + if (priv->tx_counter == RegTxCounter) + bStuck = true; + + priv->tx_counter = RegTxCounter; + + return bStuck; +} + +bool rtl92e_get_nmode_support_by_sec(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + if (ieee->rtllib_ap_sec_type && + (ieee->rtllib_ap_sec_type(priv->rtllib) & (SEC_ALG_WEP | + SEC_ALG_TKIP))) { + return false; + } else { + return true; + } +} + +bool rtl92e_is_halfn_supported_by_ap(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + return ieee->bHalfWirelessN24GMode; +} diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h new file mode 100644 index 0000000000..11366fda4e --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _RTL8192E_H +#define _RTL8192E_H + +#include "r8190P_def.h" + +bool rtl92e_is_halfn_supported_by_ap(struct net_device *dev); +bool rtl92e_get_nmode_support_by_sec(struct net_device *dev); +bool rtl92e_is_tx_stuck(struct net_device *dev); +bool rtl92e_is_rx_stuck(struct net_device *dev); +void rtl92e_ack_irq(struct net_device *dev, u32 *p_inta); +void rtl92e_enable_rx(struct net_device *dev); +void rtl92e_enable_tx(struct net_device *dev); +void rtl92e_enable_irq(struct net_device *dev); +void rtl92e_disable_irq(struct net_device *dev); +void rtl92e_init_variables(struct net_device *dev); +void rtl92e_start_beacon(struct net_device *dev); +void rtl92e_set_reg(struct net_device *dev, u8 variable, u8 *val); +void rtl92e_get_eeprom_size(struct net_device *dev); +bool rtl92e_start_adapter(struct net_device *dev); +void rtl92e_link_change(struct net_device *dev); +void rtl92e_set_monitor_mode(struct net_device *dev, bool bAllowAllDA, + bool WriteIntoReg); +void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc, + struct cb_desc *cb_desc, struct sk_buff *skb); +void rtl92e_fill_tx_cmd_desc(struct net_device *dev, struct tx_desc_cmd *entry, + struct cb_desc *cb_desc, struct sk_buff *skb); +bool rtl92e_get_rx_stats(struct net_device *dev, struct rtllib_rx_stats *stats, + struct rx_desc *pdesc, struct sk_buff *skb); +void rtl92e_stop_adapter(struct net_device *dev, bool reset); +void rtl92e_update_ratr_table(struct net_device *dev); +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c new file mode 100644 index 0000000000..ddf998cf20 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "r8192E_hw.h" +#include "table.h" +#include "r8192E_firmware.h" +#include "r8192E_cmdpkt.h" +#include <linux/firmware.h> + +static bool _rtl92e_wait_for_fw(struct net_device *dev, u32 mask, u32 timeout) +{ + unsigned long deadline = jiffies + msecs_to_jiffies(timeout); + + while (time_before(jiffies, deadline)) { + if (rtl92e_readl(dev, CPU_GEN) & mask) + return true; + mdelay(2); + } + return false; +} + +static bool _rtl92e_fw_boot_cpu(struct net_device *dev) +{ + u32 CPU_status = 0; + + if (!_rtl92e_wait_for_fw(dev, CPU_GEN_PUT_CODE_OK, 200)) { + netdev_err(dev, "Firmware download failed.\n"); + return false; + } + netdev_dbg(dev, "Download Firmware: Put code ok!\n"); + + CPU_status = rtl92e_readl(dev, CPU_GEN); + rtl92e_writeb(dev, CPU_GEN, (CPU_status | CPU_GEN_PWR_STB_CPU) & 0xff); + mdelay(1); + + if (!_rtl92e_wait_for_fw(dev, CPU_GEN_BOOT_RDY, 200)) { + netdev_err(dev, "Firmware boot failed.\n"); + return false; + } + + netdev_dbg(dev, "Download Firmware: Boot ready!\n"); + + return true; +} + +static bool _rtl92e_fw_check_ready(struct net_device *dev, + u8 load_fw_status) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_firmware *pfirmware = priv->fw_info; + bool rt_status = true; + + switch (load_fw_status) { + case FW_INIT_STEP0_BOOT: + pfirmware->status = FW_STATUS_1_MOVE_BOOT_CODE; + break; + + case FW_INIT_STEP1_MAIN: + pfirmware->status = FW_STATUS_2_MOVE_MAIN_CODE; + + rt_status = _rtl92e_fw_boot_cpu(dev); + if (rt_status) + pfirmware->status = FW_STATUS_3_TURNON_CPU; + else + netdev_dbg(dev, "_rtl92e_fw_boot_cpu fail!\n"); + + break; + + case FW_INIT_STEP2_DATA: + pfirmware->status = FW_STATUS_4_MOVE_DATA_CODE; + mdelay(1); + + rt_status = _rtl92e_wait_for_fw(dev, CPU_GEN_FIRM_RDY, 20); + if (rt_status) + pfirmware->status = FW_STATUS_5_READY; + break; + default: + rt_status = false; + netdev_dbg(dev, "Unknown firmware status"); + break; + } + + return rt_status; +} + +static bool _rtl92e_fw_prepare(struct net_device *dev, struct rt_fw_blob *blob, + const char *name, u8 padding) +{ + const struct firmware *fw; + int rc, i; + bool ret = true; + + rc = request_firmware(&fw, name, &dev->dev); + if (rc < 0) + return false; + + if (round_up(fw->size, 4) > MAX_FW_SIZE - padding) { + netdev_err(dev, "Firmware image %s too big for the device.\n", + name); + ret = false; + goto out; + } + + if (padding) + memset(blob->data, 0, padding); + if (fw->size % 4) + memset(blob->data + padding + fw->size, 0, 4); + memcpy(blob->data + padding, fw->data, fw->size); + + blob->size = round_up(fw->size, 4) + padding; + + /* Swap endian - firmware is packaged in invalid endiannes*/ + for (i = padding; i < blob->size; i += 4) { + u32 *data = (u32 *)(blob->data + i); + *data = swab32p(data); + } +out: + release_firmware(fw); + return ret; +} + +bool rtl92e_init_fw(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + bool rt_status = true; + + u32 file_length = 0; + u8 *mapped_file = NULL; + u8 i = 0; + enum opt_rst_type rst_opt = OPT_SYSTEM_RESET; + enum firmware_init_step starting_state = FW_INIT_STEP0_BOOT; + + struct rt_firmware *pfirmware = priv->fw_info; + + netdev_dbg(dev, " PlatformInitFirmware()==>\n"); + + if (pfirmware->status == FW_STATUS_0_INIT) { + rst_opt = OPT_SYSTEM_RESET; + starting_state = FW_INIT_STEP0_BOOT; + + } else if (pfirmware->status == FW_STATUS_5_READY) { + rst_opt = OPT_FIRMWARE_RESET; + starting_state = FW_INIT_STEP2_DATA; + } + + for (i = starting_state; i <= FW_INIT_STEP2_DATA; i++) { + if (rst_opt == OPT_SYSTEM_RESET) { + if (pfirmware->blobs[i].size == 0) { + const char *fw_name[3] = { + RTL8192E_BOOT_IMG_FW, + RTL8192E_MAIN_IMG_FW, + RTL8192E_DATA_IMG_FW + }; + int pad = 0; + + if (i == FW_INIT_STEP1_MAIN) + pad = 128; + + if (!_rtl92e_fw_prepare(dev, + &pfirmware->blobs[i], + fw_name[i], + pad)) + goto download_firmware_fail; + } + } + + mapped_file = pfirmware->blobs[i].data; + file_length = pfirmware->blobs[i].size; + + rt_status = rtl92e_send_cmd_pkt(dev, DESC_PACKET_TYPE_INIT, + mapped_file, file_length); + if (!rt_status) + goto download_firmware_fail; + + if (!_rtl92e_fw_check_ready(dev, i)) + goto download_firmware_fail; + } + + netdev_dbg(dev, "Firmware Download Success\n"); + return rt_status; + +download_firmware_fail: + netdev_err(dev, "%s: Failed to initialize firmware.\n", __func__); + return false; +} diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h new file mode 100644 index 0000000000..b9059abc90 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef __INC_FIRMWARE_H +#define __INC_FIRMWARE_H + +#define RTL8192E_BOOT_IMG_FW "RTL8192E/boot.img" +#define RTL8192E_MAIN_IMG_FW "RTL8192E/main.img" +#define RTL8192E_DATA_IMG_FW "RTL8192E/data.img" + +enum firmware_init_step { + FW_INIT_STEP0_BOOT = 0, + FW_INIT_STEP1_MAIN = 1, + FW_INIT_STEP2_DATA = 2, +}; + +enum opt_rst_type { + OPT_SYSTEM_RESET = 0, + OPT_FIRMWARE_RESET = 1, +}; + +enum desc_packet_type { + DESC_PACKET_TYPE_INIT = 0, + DESC_PACKET_TYPE_NORMAL = 1, +}; + +enum firmware_status { + FW_STATUS_0_INIT = 0, + FW_STATUS_1_MOVE_BOOT_CODE = 1, + FW_STATUS_2_MOVE_MAIN_CODE = 2, + FW_STATUS_3_TURNON_CPU = 3, + FW_STATUS_4_MOVE_DATA_CODE = 4, + FW_STATUS_5_READY = 5, +}; + +#define MAX_FW_SIZE 64000 +struct rt_fw_blob { + u16 size; + u8 data[MAX_FW_SIZE]; +}; + +#define FW_BLOBS 3 +struct rt_firmware { + enum firmware_status status; + struct rt_fw_blob blobs[FW_BLOBS]; +}; + +bool rtl92e_init_fw(struct net_device *dev); +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h new file mode 100644 index 0000000000..09f8c76b7e --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_hw.h @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef R8180_HW +#define R8180_HW + +enum baseband_config { + BB_CONFIG_PHY_REG = 0, + BB_CONFIG_AGC_TAB = 1, +}; + +#define RTL8190_EEPROM_ID 0x8129 +#define EEPROM_VID 0x02 +#define EEPROM_DID 0x04 +#define EEPROM_NODE_ADDRESS_BYTE_0 0x0C + +#define EEPROM_Default_ThermalMeter 0x77 +#define EEPROM_Default_AntTxPowerDiff 0x0 +#define EEPROM_Default_TxPwDiff_CrystalCap 0x5 +#define EEPROM_Default_TxPower 0x1010 +#define EEPROM_ICVersion_ChannelPlan 0x7C +#define EEPROM_Customer_ID 0x7B +#define EEPROM_RFInd_PowerDiff 0x28 + +#define EEPROM_ThermalMeter 0x29 +#define EEPROM_TxPwDiff_CrystalCap 0x2A +#define EEPROM_TxPwIndex_CCK 0x2C +#define EEPROM_TxPwIndex_OFDM_24G 0x3A + +#define EEPROM_CID_TOSHIBA 0x4 +#define EEPROM_CID_NetCore 0x5 +enum _RTL8192PCI_HW { + MAC0 = 0x000, + MAC4 = 0x004, + PCIF = 0x009, +#define MXDMA2_NO_LIMIT 0x7 + +#define MXDMA2_RX_SHIFT 4 +#define MXDMA2_TX_SHIFT 0 + PMR = 0x00c, + EPROM_CMD = 0x00e, + +#define EPROM_CMD_9356SEL BIT4 +#define EPROM_CMD_OPERATING_MODE_SHIFT 6 +#define EPROM_CMD_NORMAL 0 +#define EPROM_CMD_PROGRAM 2 +#define EPROM_CS_BIT 3 +#define EPROM_CK_BIT 2 +#define EPROM_W_BIT 1 +#define EPROM_R_BIT 0 + + ANAPAR = 0x17, +#define BB_GLOBAL_RESET_BIT 0x1 + BB_GLOBAL_RESET = 0x020, + BSSIDR = 0x02E, + CMDR = 0x037, +#define CR_RE 0x08 +#define CR_TE 0x04 + SIFS = 0x03E, + RCR = 0x044, +#define RCR_ONLYERLPKT BIT31 +#define RCR_CBSSID BIT23 +#define RCR_ADD3 BIT21 +#define RCR_AMF BIT20 +#define RCR_ADF BIT18 +#define RCR_AICV BIT12 +#define RCR_AB BIT3 +#define RCR_AM BIT2 +#define RCR_APM BIT1 +#define RCR_AAP BIT0 +#define RCR_MXDMA_OFFSET 8 +#define RCR_FIFO_OFFSET 13 + SLOT_TIME = 0x049, + ACK_TIMEOUT = 0x04c, + EDCAPARA_BE = 0x050, + EDCAPARA_BK = 0x054, + EDCAPARA_VO = 0x058, + EDCAPARA_VI = 0x05C, +#define AC_PARAM_TXOP_LIMIT_OFFSET 16 +#define AC_PARAM_ECW_MAX_OFFSET 12 +#define AC_PARAM_ECW_MIN_OFFSET 8 +#define AC_PARAM_AIFS_OFFSET 0 + BCN_TCFG = 0x062, +#define BCN_TCFG_CW_SHIFT 8 +#define BCN_TCFG_IFS 0 + BCN_INTERVAL = 0x070, + ATIMWND = 0x072, + BCN_DRV_EARLY_INT = 0x074, + BCN_DMATIME = 0x076, + BCN_ERR_THRESH = 0x078, + RWCAM = 0x0A0, +#define TOTAL_CAM_ENTRY 32 + WCAMI = 0x0A4, + SECR = 0x0B0, +#define SCR_TxUseDK BIT0 +#define SCR_RxUseDK BIT1 +#define SCR_TxEncEnable BIT2 +#define SCR_RxDecEnable BIT3 +#define SCR_NoSKMC BIT5 + SWREGULATOR = 0x0BD, + INTA_MASK = 0x0f4, +#define IMR_TBDOK BIT27 +#define IMR_TBDER BIT26 +#define IMR_TXFOVW BIT15 +#define IMR_TIMEOUT0 BIT14 +#define IMR_BcnInt BIT13 +#define IMR_RXFOVW BIT12 +#define IMR_RDU BIT11 +#define IMR_RXCMDOK BIT10 +#define IMR_BDOK BIT9 +#define IMR_HIGHDOK BIT8 +#define IMR_COMDOK BIT7 +#define IMR_MGNTDOK BIT6 +#define IMR_HCCADOK BIT5 +#define IMR_BKDOK BIT4 +#define IMR_BEDOK BIT3 +#define IMR_VIDOK BIT2 +#define IMR_VODOK BIT1 +#define IMR_ROK BIT0 + ISR = 0x0f8, + TP_POLL = 0x0fd, +#define TP_POLL_CQ BIT5 + PSR = 0x0ff, + CPU_GEN = 0x100, +#define CPU_CCK_LOOPBACK 0x00030000 +#define CPU_GEN_SYSTEM_RESET 0x00000001 +#define CPU_GEN_FIRMWARE_RESET 0x00000008 +#define CPU_GEN_BOOT_RDY 0x00000010 +#define CPU_GEN_FIRM_RDY 0x00000020 +#define CPU_GEN_PUT_CODE_OK 0x00000080 +#define CPU_GEN_BB_RST 0x00000100 +#define CPU_GEN_PWR_STB_CPU 0x00000004 +#define CPU_GEN_NO_LOOPBACK_MSK 0xFFF8FFFF +#define CPU_GEN_NO_LOOPBACK_SET 0x00080000 + ACM_HW_CTRL = 0x171, +#define ACM_HW_BEQ_EN BIT1 +#define ACM_HW_VIQ_EN BIT2 +#define ACM_HW_VOQ_EN BIT3 + RQPN1 = 0x180, + RQPN2 = 0x184, + RQPN3 = 0x188, + QPNR = 0x1F0, + BQDA = 0x200, + HQDA = 0x204, + CQDA = 0x208, + MQDA = 0x20C, + HCCAQDA = 0x210, + VOQDA = 0x214, + VIQDA = 0x218, + BEQDA = 0x21C, + BKQDA = 0x220, + RDQDA = 0x228, + + WFCRC0 = 0x2f0, + WFCRC1 = 0x2f4, + WFCRC2 = 0x2f8, + + BW_OPMODE = 0x300, +#define BW_OPMODE_20MHZ BIT2 + IC_VERRSION = 0x301, + MSR = 0x303, +#define MSR_LINK_MASK (BIT(1) | BIT(0)) +#define MSR_LINK_MANAGED 2 +#define MSR_LINK_ADHOC 1 +#define MSR_LINK_MASTER 3 + +#define MSR_NOLINK 0x00 +#define MSR_ADHOC 0x01 +#define MSR_INFRA 0x02 +#define MSR_AP 0x03 + + RETRY_LIMIT = 0x304, +#define RETRY_LIMIT_SHORT_SHIFT 8 +#define RETRY_LIMIT_LONG_SHIFT 0 + TSFR = 0x308, + RRSR = 0x310, +#define RRSR_SHORT_OFFSET 23 +#define RRSR_1M BIT0 +#define RRSR_2M BIT1 +#define RRSR_5_5M BIT2 +#define RRSR_11M BIT3 +#define RRSR_6M BIT4 +#define RRSR_9M BIT5 +#define RRSR_12M BIT6 +#define RRSR_18M BIT7 +#define RRSR_24M BIT8 +#define RRSR_36M BIT9 +#define RRSR_48M BIT10 +#define RRSR_54M BIT11 +#define BRSR_AckShortPmb BIT23 + UFWP = 0x318, + RATR0 = 0x320, +#define RATR_1M 0x00000001 +#define RATR_2M 0x00000002 +#define RATR_55M 0x00000004 +#define RATR_11M 0x00000008 +#define RATR_6M 0x00000010 +#define RATR_9M 0x00000020 +#define RATR_12M 0x00000040 +#define RATR_18M 0x00000080 +#define RATR_24M 0x00000100 +#define RATR_36M 0x00000200 +#define RATR_48M 0x00000400 +#define RATR_54M 0x00000800 +#define RATR_MCS0 0x00001000 +#define RATR_MCS1 0x00002000 +#define RATR_MCS2 0x00004000 +#define RATR_MCS3 0x00008000 +#define RATR_MCS4 0x00010000 +#define RATR_MCS5 0x00020000 +#define RATR_MCS6 0x00040000 +#define RATR_MCS7 0x00080000 +#define RATR_MCS8 0x00100000 +#define RATR_MCS9 0x00200000 +#define RATR_MCS10 0x00400000 +#define RATR_MCS11 0x00800000 +#define RATR_MCS12 0x01000000 +#define RATR_MCS13 0x02000000 +#define RATR_MCS14 0x04000000 +#define RATR_MCS15 0x08000000 +#define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | RATR_11M) +#define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | RATR_18M | \ + RATR_24M | RATR_36M | RATR_48M | RATR_54M) +#define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 | \ + RATR_MCS3 | RATR_MCS4 | RATR_MCS5 | \ + RATR_MCS6 | RATR_MCS7) +#define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 | \ + RATR_MCS11 | RATR_MCS12 | RATR_MCS13 | \ + RATR_MCS14|RATR_MCS15) + + DRIVER_RSSI = 0x32c, + MCS_TXAGC = 0x340, + CCK_TXAGC = 0x348, + MAC_BLK_CTRL = 0x403, +}; + +#define GPI 0x108 + +#define ANAPAR_FOR_8192PCIE 0x17 + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c new file mode 100644 index 0000000000..875540a207 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.c @@ -0,0 +1,1121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include <linux/bitops.h> +#include "rtl_core.h" +#include "r8192E_hw.h" +#include "r8192E_phyreg.h" +#include "r8190P_rtl8256.h" +#include "r8192E_phy.h" +#include "rtl_dm.h" + +#include "table.h" + +/*************************Define local function prototype**********************/ + +static u32 _rtl92e_phy_rf_fw_read(struct net_device *dev, + enum rf90_radio_path eRFPath, u32 Offset); +static void _rtl92e_phy_rf_fw_write(struct net_device *dev, + enum rf90_radio_path eRFPath, u32 Offset, + u32 Data); + +static u32 _rtl92e_calculate_bit_shift(u32 dwBitMask) +{ + if (!dwBitMask) + return 32; + return ffs(dwBitMask) - 1; +} + +void rtl92e_set_bb_reg(struct net_device *dev, u32 dwRegAddr, u32 dwBitMask, + u32 dwData) +{ + u32 OriginalValue, BitShift, NewValue; + + if (dwBitMask != bMaskDWord) { + OriginalValue = rtl92e_readl(dev, dwRegAddr); + BitShift = _rtl92e_calculate_bit_shift(dwBitMask); + NewValue = (OriginalValue & ~dwBitMask) | (dwData << BitShift); + rtl92e_writel(dev, dwRegAddr, NewValue); + } else { + rtl92e_writel(dev, dwRegAddr, dwData); + } +} + +u32 rtl92e_get_bb_reg(struct net_device *dev, u32 dwRegAddr, u32 dwBitMask) +{ + u32 OriginalValue, BitShift; + + OriginalValue = rtl92e_readl(dev, dwRegAddr); + BitShift = _rtl92e_calculate_bit_shift(dwBitMask); + + return (OriginalValue & dwBitMask) >> BitShift; +} + +static u32 _rtl92e_phy_rf_read(struct net_device *dev, + enum rf90_radio_path eRFPath, u32 Offset) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 ret = 0; + u32 NewOffset = 0; + struct bb_reg_definition *pPhyReg = &priv->phy_reg_def[eRFPath]; + + Offset &= 0x3f; + + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter4, 0xf00, 0x0); + if (Offset >= 31) { + priv->rf_reg_0value[eRFPath] |= 0x140; + rtl92e_set_bb_reg(dev, pPhyReg->rf3wireOffset, + bMaskDWord, + (priv->rf_reg_0value[eRFPath] << 16)); + NewOffset = Offset - 30; + } else if (Offset >= 16) { + priv->rf_reg_0value[eRFPath] |= 0x100; + priv->rf_reg_0value[eRFPath] &= (~0x40); + rtl92e_set_bb_reg(dev, pPhyReg->rf3wireOffset, + bMaskDWord, + (priv->rf_reg_0value[eRFPath] << 16)); + NewOffset = Offset - 15; + } else { + NewOffset = Offset; + } + rtl92e_set_bb_reg(dev, pPhyReg->rfHSSIPara2, bLSSIReadAddress, + NewOffset); + rtl92e_set_bb_reg(dev, pPhyReg->rfHSSIPara2, bLSSIReadEdge, 0x0); + rtl92e_set_bb_reg(dev, pPhyReg->rfHSSIPara2, bLSSIReadEdge, 0x1); + + mdelay(1); + + ret = rtl92e_get_bb_reg(dev, pPhyReg->rfLSSIReadBack, + bLSSIReadBackData); + + priv->rf_reg_0value[eRFPath] &= 0xebf; + + rtl92e_set_bb_reg(dev, pPhyReg->rf3wireOffset, bMaskDWord, + (priv->rf_reg_0value[eRFPath] << 16)); + + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter4, 0x300, 0x3); + + return ret; +} + +static void _rtl92e_phy_rf_write(struct net_device *dev, + enum rf90_radio_path eRFPath, u32 Offset, + u32 Data) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 DataAndAddr = 0, NewOffset = 0; + struct bb_reg_definition *pPhyReg = &priv->phy_reg_def[eRFPath]; + + Offset &= 0x3f; + + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter4, 0xf00, 0x0); + + if (Offset >= 31) { + priv->rf_reg_0value[eRFPath] |= 0x140; + rtl92e_set_bb_reg(dev, pPhyReg->rf3wireOffset, + bMaskDWord, + (priv->rf_reg_0value[eRFPath] << 16)); + NewOffset = Offset - 30; + } else if (Offset >= 16) { + priv->rf_reg_0value[eRFPath] |= 0x100; + priv->rf_reg_0value[eRFPath] &= (~0x40); + rtl92e_set_bb_reg(dev, pPhyReg->rf3wireOffset, + bMaskDWord, + (priv->rf_reg_0value[eRFPath] << 16)); + NewOffset = Offset - 15; + } else { + NewOffset = Offset; + } + + DataAndAddr = (NewOffset & 0x3f) | (Data << 16); + + rtl92e_set_bb_reg(dev, pPhyReg->rf3wireOffset, bMaskDWord, DataAndAddr); + + if (Offset == 0x0) + priv->rf_reg_0value[eRFPath] = Data; + + if (Offset != 0) { + priv->rf_reg_0value[eRFPath] &= 0xebf; + rtl92e_set_bb_reg(dev, pPhyReg->rf3wireOffset, + bMaskDWord, + (priv->rf_reg_0value[eRFPath] << 16)); + } + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter4, 0x300, 0x3); +} + +void rtl92e_set_rf_reg(struct net_device *dev, enum rf90_radio_path eRFPath, + u32 RegAddr, u32 BitMask, u32 Data) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 Original_Value, BitShift, New_Value; + + if (priv->rtllib->rf_power_state != rf_on && !priv->being_init_adapter) + return; + + if (priv->rf_mode == RF_OP_By_FW) { + if (BitMask != bMask12Bits) { + Original_Value = _rtl92e_phy_rf_fw_read(dev, eRFPath, + RegAddr); + BitShift = _rtl92e_calculate_bit_shift(BitMask); + New_Value = (Original_Value & ~BitMask) | (Data << BitShift); + + _rtl92e_phy_rf_fw_write(dev, eRFPath, RegAddr, + New_Value); + } else { + _rtl92e_phy_rf_fw_write(dev, eRFPath, RegAddr, Data); + } + udelay(200); + } else { + if (BitMask != bMask12Bits) { + Original_Value = _rtl92e_phy_rf_read(dev, eRFPath, + RegAddr); + BitShift = _rtl92e_calculate_bit_shift(BitMask); + New_Value = (Original_Value & ~BitMask) | (Data << BitShift); + + _rtl92e_phy_rf_write(dev, eRFPath, RegAddr, New_Value); + } else { + _rtl92e_phy_rf_write(dev, eRFPath, RegAddr, Data); + } + } +} + +u32 rtl92e_get_rf_reg(struct net_device *dev, enum rf90_radio_path eRFPath, + u32 RegAddr, u32 BitMask) +{ + u32 Original_Value, Readback_Value, BitShift; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->rtllib->rf_power_state != rf_on && !priv->being_init_adapter) + return 0; + mutex_lock(&priv->rf_mutex); + if (priv->rf_mode == RF_OP_By_FW) { + Original_Value = _rtl92e_phy_rf_fw_read(dev, eRFPath, RegAddr); + udelay(200); + } else { + Original_Value = _rtl92e_phy_rf_read(dev, eRFPath, RegAddr); + } + BitShift = _rtl92e_calculate_bit_shift(BitMask); + Readback_Value = (Original_Value & BitMask) >> BitShift; + mutex_unlock(&priv->rf_mutex); + return Readback_Value; +} + +static u32 _rtl92e_phy_rf_fw_read(struct net_device *dev, + enum rf90_radio_path eRFPath, u32 Offset) +{ + u32 Data = 0; + u8 time = 0; + + Data |= ((Offset & 0xFF) << 12); + Data |= ((eRFPath & 0x3) << 20); + Data |= 0x80000000; + while (rtl92e_readl(dev, QPNR) & 0x80000000) { + if (time++ < 100) + udelay(10); + else + break; + } + rtl92e_writel(dev, QPNR, Data); + while (rtl92e_readl(dev, QPNR) & 0x80000000) { + if (time++ < 100) + udelay(10); + else + return 0; + } + return rtl92e_readl(dev, RF_DATA); +} + +static void _rtl92e_phy_rf_fw_write(struct net_device *dev, + enum rf90_radio_path eRFPath, u32 Offset, + u32 Data) +{ + u8 time = 0; + + Data |= ((Offset & 0xFF) << 12); + Data |= ((eRFPath & 0x3) << 20); + Data |= 0x400000; + Data |= 0x80000000; + + while (rtl92e_readl(dev, QPNR) & 0x80000000) { + if (time++ < 100) + udelay(10); + else + break; + } + rtl92e_writel(dev, QPNR, Data); +} + +void rtl92e_config_mac(struct net_device *dev) +{ + u32 dwArrayLen = 0, i = 0; + u32 *pdwArray = NULL; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->tx_pwr_data_read_from_eeprom) { + dwArrayLen = RTL8192E_MACPHY_ARR_PG_LEN; + pdwArray = RTL8192E_MACPHY_ARR_PG; + + } else { + dwArrayLen = RTL8192E_MACPHY_ARR_LEN; + pdwArray = RTL8192E_MACPHY_ARR; + } + for (i = 0; i < dwArrayLen; i += 3) { + if (pdwArray[i] == 0x318) + pdwArray[i + 2] = 0x00000800; + rtl92e_set_bb_reg(dev, pdwArray[i], pdwArray[i + 1], + pdwArray[i + 2]); + } +} + +static void _rtl92e_phy_config_bb(struct net_device *dev, u8 ConfigType) +{ + int i; + u32 *Rtl819XPHY_REGArray_Table = NULL; + u32 *Rtl819XAGCTAB_Array_Table = NULL; + u16 AGCTAB_ArrayLen, PHY_REGArrayLen = 0; + + AGCTAB_ArrayLen = RTL8192E_AGCTAB_ARR_LEN; + Rtl819XAGCTAB_Array_Table = RTL8192E_AGCTAB_ARR; + PHY_REGArrayLen = RTL8192E_PHY_REG_1T2R_ARR_LEN; + Rtl819XPHY_REGArray_Table = RTL8192E_PHY_REG_1T2R_ARR; + + if (ConfigType == BB_CONFIG_PHY_REG) { + for (i = 0; i < PHY_REGArrayLen; i += 2) { + rtl92e_set_bb_reg(dev, Rtl819XPHY_REGArray_Table[i], + bMaskDWord, + Rtl819XPHY_REGArray_Table[i + 1]); + } + } else if (ConfigType == BB_CONFIG_AGC_TAB) { + for (i = 0; i < AGCTAB_ArrayLen; i += 2) { + rtl92e_set_bb_reg(dev, Rtl819XAGCTAB_Array_Table[i], + bMaskDWord, + Rtl819XAGCTAB_Array_Table[i + 1]); + } + } +} + +static void _rtl92e_init_bb_rf_reg_def(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->phy_reg_def[RF90_PATH_A].rfintfs = rFPGA0_XAB_RFInterfaceSW; + priv->phy_reg_def[RF90_PATH_B].rfintfs = rFPGA0_XAB_RFInterfaceSW; + + priv->phy_reg_def[RF90_PATH_A].rfintfo = rFPGA0_XA_RFInterfaceOE; + priv->phy_reg_def[RF90_PATH_B].rfintfo = rFPGA0_XB_RFInterfaceOE; + + priv->phy_reg_def[RF90_PATH_A].rfintfe = rFPGA0_XA_RFInterfaceOE; + priv->phy_reg_def[RF90_PATH_B].rfintfe = rFPGA0_XB_RFInterfaceOE; + + priv->phy_reg_def[RF90_PATH_A].rf3wireOffset = rFPGA0_XA_LSSIParameter; + priv->phy_reg_def[RF90_PATH_B].rf3wireOffset = rFPGA0_XB_LSSIParameter; + + priv->phy_reg_def[RF90_PATH_A].rfHSSIPara2 = rFPGA0_XA_HSSIParameter2; + priv->phy_reg_def[RF90_PATH_B].rfHSSIPara2 = rFPGA0_XB_HSSIParameter2; + + priv->phy_reg_def[RF90_PATH_A].rfLSSIReadBack = rFPGA0_XA_LSSIReadBack; + priv->phy_reg_def[RF90_PATH_B].rfLSSIReadBack = rFPGA0_XB_LSSIReadBack; +} + +bool rtl92e_check_bb_and_rf(struct net_device *dev, enum hw90_block CheckBlock, + enum rf90_radio_path eRFPath) +{ + bool ret = true; + u32 i, CheckTimes = 4, dwRegRead = 0; + u32 WriteAddr[4]; + u32 WriteData[] = {0xfffff027, 0xaa55a02f, 0x00000027, 0x55aa502f}; + + WriteAddr[HW90_BLOCK_MAC] = 0x100; + WriteAddr[HW90_BLOCK_PHY0] = 0x900; + WriteAddr[HW90_BLOCK_PHY1] = 0x800; + WriteAddr[HW90_BLOCK_RF] = 0x3; + + if (CheckBlock == HW90_BLOCK_MAC) { + netdev_warn(dev, "%s(): No checks available for MAC block.\n", + __func__); + return ret; + } + + for (i = 0; i < CheckTimes; i++) { + switch (CheckBlock) { + case HW90_BLOCK_PHY0: + case HW90_BLOCK_PHY1: + rtl92e_writel(dev, WriteAddr[CheckBlock], + WriteData[i]); + dwRegRead = rtl92e_readl(dev, WriteAddr[CheckBlock]); + break; + + case HW90_BLOCK_RF: + WriteData[i] &= 0xfff; + rtl92e_set_rf_reg(dev, eRFPath, + WriteAddr[HW90_BLOCK_RF], + bMask12Bits, WriteData[i]); + mdelay(10); + dwRegRead = rtl92e_get_rf_reg(dev, eRFPath, + WriteAddr[HW90_BLOCK_RF], + bMaskDWord); + mdelay(10); + break; + + default: + ret = false; + break; + } + + if (dwRegRead != WriteData[i]) { + netdev_warn(dev, "%s(): Check failed.\n", __func__); + ret = false; + break; + } + } + + return ret; +} + +static bool _rtl92e_bb_config_para_file(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + bool rtStatus = true; + u8 bRegValue = 0, eCheckItem = 0; + u32 dwRegValue = 0; + + bRegValue = rtl92e_readb(dev, BB_GLOBAL_RESET); + rtl92e_writeb(dev, BB_GLOBAL_RESET, (bRegValue | BB_GLOBAL_RESET_BIT)); + + dwRegValue = rtl92e_readl(dev, CPU_GEN); + rtl92e_writel(dev, CPU_GEN, (dwRegValue & (~CPU_GEN_BB_RST))); + + for (eCheckItem = (enum hw90_block)HW90_BLOCK_PHY0; + eCheckItem <= HW90_BLOCK_PHY1; eCheckItem++) { + rtStatus = rtl92e_check_bb_and_rf(dev, + (enum hw90_block)eCheckItem, + (enum rf90_radio_path)0); + if (!rtStatus) + return rtStatus; + } + rtl92e_set_bb_reg(dev, rFPGA0_RFMOD, bCCKEn | bOFDMEn, 0x0); + _rtl92e_phy_config_bb(dev, BB_CONFIG_PHY_REG); + + dwRegValue = rtl92e_readl(dev, CPU_GEN); + rtl92e_writel(dev, CPU_GEN, (dwRegValue | CPU_GEN_BB_RST)); + + _rtl92e_phy_config_bb(dev, BB_CONFIG_AGC_TAB); + + if (priv->ic_cut > VERSION_8190_BD) { + dwRegValue = 0x0; + rtl92e_set_bb_reg(dev, rFPGA0_TxGainStage, + (bXBTxAGC | bXCTxAGC | bXDTxAGC), dwRegValue); + + dwRegValue = priv->crystal_cap; + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, bXtalCap92x, + dwRegValue); + } + + return rtStatus; +} +bool rtl92e_config_bb(struct net_device *dev) +{ + _rtl92e_init_bb_rf_reg_def(dev); + return _rtl92e_bb_config_para_file(dev); +} + +void rtl92e_get_tx_power(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->mcs_tx_pwr_level_org_offset[0] = + rtl92e_readl(dev, rTxAGC_Rate18_06); + priv->mcs_tx_pwr_level_org_offset[1] = + rtl92e_readl(dev, rTxAGC_Rate54_24); + priv->mcs_tx_pwr_level_org_offset[2] = + rtl92e_readl(dev, rTxAGC_Mcs03_Mcs00); + priv->mcs_tx_pwr_level_org_offset[3] = + rtl92e_readl(dev, rTxAGC_Mcs07_Mcs04); + priv->mcs_tx_pwr_level_org_offset[4] = + rtl92e_readl(dev, rTxAGC_Mcs11_Mcs08); + priv->mcs_tx_pwr_level_org_offset[5] = + rtl92e_readl(dev, rTxAGC_Mcs15_Mcs12); + + priv->def_initial_gain[0] = rtl92e_readb(dev, rOFDM0_XAAGCCore1); + priv->def_initial_gain[1] = rtl92e_readb(dev, rOFDM0_XBAGCCore1); + priv->def_initial_gain[2] = rtl92e_readb(dev, rOFDM0_XCAGCCore1); + priv->def_initial_gain[3] = rtl92e_readb(dev, rOFDM0_XDAGCCore1); + + priv->framesync = rtl92e_readb(dev, rOFDM0_RxDetector3); +} + +void rtl92e_set_tx_power(struct net_device *dev, u8 channel) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 powerlevel = 0, powerlevelOFDM24G = 0; + + if (priv->epromtype == EEPROM_93C46) { + powerlevel = priv->tx_pwr_level_cck[channel - 1]; + powerlevelOFDM24G = priv->tx_pwr_level_ofdm_24g[channel - 1]; + } + + rtl92e_set_cck_tx_power(dev, powerlevel); + rtl92e_set_ofdm_tx_power(dev, powerlevelOFDM24G); +} + +u8 rtl92e_config_rf_path(struct net_device *dev, enum rf90_radio_path eRFPath) +{ + int i; + + switch (eRFPath) { + case RF90_PATH_A: + for (i = 0; i < RTL8192E_RADIO_A_ARR_LEN; i += 2) { + if (RTL8192E_RADIO_A_ARR[i] == 0xfe) { + msleep(100); + continue; + } + rtl92e_set_rf_reg(dev, eRFPath, RTL8192E_RADIO_A_ARR[i], + bMask12Bits, + RTL8192E_RADIO_A_ARR[i + 1]); + } + break; + case RF90_PATH_B: + for (i = 0; i < RTL8192E_RADIO_B_ARR_LEN; i += 2) { + if (RTL8192E_RADIO_B_ARR[i] == 0xfe) { + msleep(100); + continue; + } + rtl92e_set_rf_reg(dev, eRFPath, RTL8192E_RADIO_B_ARR[i], + bMask12Bits, + RTL8192E_RADIO_B_ARR[i + 1]); + } + break; + default: + break; + } + + return 0; +} + +static void _rtl92e_set_tx_power_level(struct net_device *dev, u8 channel) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 powerlevel = priv->tx_pwr_level_cck[channel - 1]; + u8 powerlevelOFDM24G = priv->tx_pwr_level_ofdm_24g[channel - 1]; + + rtl92e_set_cck_tx_power(dev, powerlevel); + rtl92e_set_ofdm_tx_power(dev, powerlevelOFDM24G); +} + +static u8 _rtl92e_phy_set_sw_chnl_cmd_array(struct net_device *dev, + struct sw_chnl_cmd *CmdTable, + u32 CmdTableIdx, u32 CmdTableSz, + enum sw_chnl_cmd_id CmdID, + u32 Para1, u32 Para2, u32 msDelay) +{ + struct sw_chnl_cmd *pCmd; + + if (CmdTable == NULL) { + netdev_err(dev, "%s(): CmdTable cannot be NULL.\n", __func__); + return false; + } + if (CmdTableIdx >= CmdTableSz) { + netdev_err(dev, "%s(): Invalid index requested.\n", __func__); + return false; + } + + pCmd = CmdTable + CmdTableIdx; + pCmd->CmdID = CmdID; + pCmd->Para1 = Para1; + pCmd->Para2 = Para2; + pCmd->msDelay = msDelay; + + return true; +} + +static u8 _rtl92e_phy_switch_channel_step(struct net_device *dev, u8 channel, + u8 *stage, u8 *step, u32 *delay) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + u32 PreCommonCmdCnt; + u32 PostCommonCmdCnt; + u32 RfDependCmdCnt; + struct sw_chnl_cmd *CurrentCmd = NULL; + u8 eRFPath; + + if (!rtllib_legal_channel(priv->rtllib, channel)) { + netdev_err(dev, "Invalid channel requested: %d\n", channel); + return true; + } + + { + PreCommonCmdCnt = 0; + _rtl92e_phy_set_sw_chnl_cmd_array(dev, ieee->PreCommonCmd, + PreCommonCmdCnt++, + MAX_PRECMD_CNT, + CmdID_SetTxPowerLevel, + 0, 0, 0); + _rtl92e_phy_set_sw_chnl_cmd_array(dev, ieee->PreCommonCmd, + PreCommonCmdCnt++, + MAX_PRECMD_CNT, CmdID_End, + 0, 0, 0); + + PostCommonCmdCnt = 0; + + _rtl92e_phy_set_sw_chnl_cmd_array(dev, ieee->PostCommonCmd, + PostCommonCmdCnt++, + MAX_POSTCMD_CNT, CmdID_End, + 0, 0, 0); + + RfDependCmdCnt = 0; + + if (!(channel >= 1 && channel <= 14)) { + netdev_err(dev, + "Invalid channel requested for 8256: %d\n", + channel); + return false; + } + _rtl92e_phy_set_sw_chnl_cmd_array(dev, + ieee->RfDependCmd, + RfDependCmdCnt++, + MAX_RFDEPENDCMD_CNT, + CmdID_RF_WriteReg, + rZebra1_Channel, + channel, 10); + _rtl92e_phy_set_sw_chnl_cmd_array(dev, + ieee->RfDependCmd, + RfDependCmdCnt++, + MAX_RFDEPENDCMD_CNT, + CmdID_End, 0, 0, 0); + + do { + switch (*stage) { + case 0: + CurrentCmd = &ieee->PreCommonCmd[*step]; + break; + case 1: + CurrentCmd = &ieee->RfDependCmd[*step]; + break; + case 2: + CurrentCmd = &ieee->PostCommonCmd[*step]; + break; + } + + if (CurrentCmd && CurrentCmd->CmdID == CmdID_End) { + if ((*stage) == 2) + return true; + (*stage)++; + (*step) = 0; + continue; + } + + if (!CurrentCmd) + continue; + switch (CurrentCmd->CmdID) { + case CmdID_SetTxPowerLevel: + if (priv->ic_cut > VERSION_8190_BD) + _rtl92e_set_tx_power_level(dev, + channel); + break; + case CmdID_WritePortUlong: + rtl92e_writel(dev, CurrentCmd->Para1, + CurrentCmd->Para2); + break; + case CmdID_WritePortUshort: + rtl92e_writew(dev, CurrentCmd->Para1, + CurrentCmd->Para2); + break; + case CmdID_WritePortUchar: + rtl92e_writeb(dev, CurrentCmd->Para1, + CurrentCmd->Para2); + break; + case CmdID_RF_WriteReg: + for (eRFPath = 0; eRFPath < + priv->num_total_rf_path; eRFPath++) + rtl92e_set_rf_reg(dev, + (enum rf90_radio_path)eRFPath, + CurrentCmd->Para1, bMask12Bits, + CurrentCmd->Para2 << 7); + break; + default: + break; + } + + break; + } while (true); + } /*for (Number of RF paths)*/ + + (*delay) = CurrentCmd->msDelay; + (*step)++; + return false; +} + +static void _rtl92e_phy_switch_channel(struct net_device *dev, u8 channel) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 delay = 0; + + while (!_rtl92e_phy_switch_channel_step(dev, channel, + &priv->sw_chnl_stage, + &priv->sw_chnl_step, &delay)) { + if (delay > 0) + msleep(delay); + if (!priv->up) + break; + } +} + +static void _rtl92e_phy_switch_channel_work_item(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + _rtl92e_phy_switch_channel(dev, priv->chan); +} + +u8 rtl92e_set_channel(struct net_device *dev, u8 channel) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (!priv->up) { + netdev_err(dev, "%s(): Driver is not initialized\n", __func__); + return false; + } + if (priv->sw_chnl_in_progress) + return false; + + switch (priv->rtllib->mode) { + case WIRELESS_MODE_B: + if (channel > 14) { + netdev_warn(dev, + "Channel %d not available in 802.11b.\n", + channel); + return false; + } + break; + case WIRELESS_MODE_G: + case WIRELESS_MODE_N_24G: + if (channel > 14) { + netdev_warn(dev, + "Channel %d not available in 802.11g.\n", + channel); + return false; + } + break; + } + + priv->sw_chnl_in_progress = true; + if (channel == 0) + channel = 1; + + priv->chan = channel; + + priv->sw_chnl_stage = 0; + priv->sw_chnl_step = 0; + + if (priv->up) + _rtl92e_phy_switch_channel_work_item(dev); + priv->sw_chnl_in_progress = false; + return true; +} + +static void _rtl92e_cck_tx_power_track_bw_switch_tssi(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + switch (priv->current_chnl_bw) { + case HT_CHANNEL_WIDTH_20: + priv->cck_present_attn = + priv->cck_present_attn_20m_def + + priv->cck_present_attn_diff; + + if (priv->cck_present_attn > + (CCK_TX_BB_GAIN_TABLE_LEN - 1)) + priv->cck_present_attn = + CCK_TX_BB_GAIN_TABLE_LEN - 1; + if (priv->cck_present_attn < 0) + priv->cck_present_attn = 0; + + if (priv->rtllib->current_network.channel == 14 && + !priv->bcck_in_ch14) { + priv->bcck_in_ch14 = true; + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } else if (priv->rtllib->current_network.channel != + 14 && priv->bcck_in_ch14) { + priv->bcck_in_ch14 = false; + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } else { + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } + break; + + case HT_CHANNEL_WIDTH_20_40: + priv->cck_present_attn = + priv->cck_present_attn_40m_def + + priv->cck_present_attn_diff; + + if (priv->cck_present_attn > + (CCK_TX_BB_GAIN_TABLE_LEN - 1)) + priv->cck_present_attn = + CCK_TX_BB_GAIN_TABLE_LEN - 1; + if (priv->cck_present_attn < 0) + priv->cck_present_attn = 0; + + if (priv->rtllib->current_network.channel == 14 && + !priv->bcck_in_ch14) { + priv->bcck_in_ch14 = true; + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } else if (priv->rtllib->current_network.channel != 14 + && priv->bcck_in_ch14) { + priv->bcck_in_ch14 = false; + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } else { + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } + break; + } +} + +static void _rtl92e_cck_tx_power_track_bw_switch_thermal(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->rtllib->current_network.channel == 14 && + !priv->bcck_in_ch14) + priv->bcck_in_ch14 = true; + else if (priv->rtllib->current_network.channel != 14 && + priv->bcck_in_ch14) + priv->bcck_in_ch14 = false; + + switch (priv->current_chnl_bw) { + case HT_CHANNEL_WIDTH_20: + if (priv->rec_cck_20m_idx == 0) + priv->rec_cck_20m_idx = 6; + priv->cck_index = priv->rec_cck_20m_idx; + break; + + case HT_CHANNEL_WIDTH_20_40: + priv->cck_index = priv->rec_cck_40m_idx; + break; + } + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); +} + +static void _rtl92e_cck_tx_power_track_bw_switch(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->ic_cut >= IC_VersionCut_D) + _rtl92e_cck_tx_power_track_bw_switch_tssi(dev); + else + _rtl92e_cck_tx_power_track_bw_switch_thermal(dev); +} + +static void _rtl92e_set_bw_mode_work_item(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 regBwOpMode; + + if (!priv->up) { + netdev_err(dev, "%s(): Driver is not initialized\n", __func__); + return; + } + regBwOpMode = rtl92e_readb(dev, BW_OPMODE); + + switch (priv->current_chnl_bw) { + case HT_CHANNEL_WIDTH_20: + regBwOpMode |= BW_OPMODE_20MHZ; + rtl92e_writeb(dev, BW_OPMODE, regBwOpMode); + break; + + case HT_CHANNEL_WIDTH_20_40: + regBwOpMode &= ~BW_OPMODE_20MHZ; + rtl92e_writeb(dev, BW_OPMODE, regBwOpMode); + break; + + default: + netdev_err(dev, "%s(): unknown Bandwidth: %#X\n", __func__, + priv->current_chnl_bw); + break; + } + + switch (priv->current_chnl_bw) { + case HT_CHANNEL_WIDTH_20: + rtl92e_set_bb_reg(dev, rFPGA0_RFMOD, bRFMOD, 0x0); + rtl92e_set_bb_reg(dev, rFPGA1_RFMOD, bRFMOD, 0x0); + + if (!priv->btxpower_tracking) { + rtl92e_writel(dev, rCCK0_TxFilter1, 0x1a1b0000); + rtl92e_writel(dev, rCCK0_TxFilter2, 0x090e1317); + rtl92e_writel(dev, rCCK0_DebugPort, 0x00000204); + } else { + _rtl92e_cck_tx_power_track_bw_switch(dev); + } + + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, 0x00100000, 1); + + break; + case HT_CHANNEL_WIDTH_20_40: + rtl92e_set_bb_reg(dev, rFPGA0_RFMOD, bRFMOD, 0x1); + rtl92e_set_bb_reg(dev, rFPGA1_RFMOD, bRFMOD, 0x1); + + if (!priv->btxpower_tracking) { + rtl92e_writel(dev, rCCK0_TxFilter1, 0x35360000); + rtl92e_writel(dev, rCCK0_TxFilter2, 0x121c252e); + rtl92e_writel(dev, rCCK0_DebugPort, 0x00000409); + } else { + _rtl92e_cck_tx_power_track_bw_switch(dev); + } + + rtl92e_set_bb_reg(dev, rCCK0_System, bCCKSideBand, + (priv->n_cur_40mhz_prime_sc >> 1)); + rtl92e_set_bb_reg(dev, rOFDM1_LSTF, 0xC00, + priv->n_cur_40mhz_prime_sc); + + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, 0x00100000, 0); + break; + default: + netdev_err(dev, "%s(): unknown Bandwidth: %#X\n", __func__, + priv->current_chnl_bw); + break; + } + + rtl92e_set_bandwidth(dev, priv->current_chnl_bw); + + atomic_dec(&(priv->rtllib->atm_swbw)); + priv->set_bw_mode_in_progress = false; +} + +void rtl92e_set_bw_mode(struct net_device *dev, enum ht_channel_width bandwidth, + enum ht_extchnl_offset Offset) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->set_bw_mode_in_progress) + return; + + atomic_inc(&(priv->rtllib->atm_swbw)); + priv->set_bw_mode_in_progress = true; + + priv->current_chnl_bw = bandwidth; + + if (Offset == HT_EXTCHNL_OFFSET_LOWER) + priv->n_cur_40mhz_prime_sc = HAL_PRIME_CHNL_OFFSET_UPPER; + else if (Offset == HT_EXTCHNL_OFFSET_UPPER) + priv->n_cur_40mhz_prime_sc = HAL_PRIME_CHNL_OFFSET_LOWER; + else + priv->n_cur_40mhz_prime_sc = HAL_PRIME_CHNL_OFFSET_DONT_CARE; + + _rtl92e_set_bw_mode_work_item(dev); +} + +void rtl92e_init_gain(struct net_device *dev, u8 Operation) +{ +#define SCAN_RX_INITIAL_GAIN 0x17 +#define POWER_DETECTION_TH 0x08 + struct r8192_priv *priv = rtllib_priv(dev); + u32 BitMask; + u8 initial_gain; + + if (priv->up) { + switch (Operation) { + case IG_Backup: + initial_gain = SCAN_RX_INITIAL_GAIN; + BitMask = bMaskByte0; + if (dm_digtable.dig_algorithm == + DIG_ALGO_BY_FALSE_ALARM) + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x8); + priv->initgain_backup.xaagccore1 = + rtl92e_get_bb_reg(dev, rOFDM0_XAAGCCore1, + BitMask); + priv->initgain_backup.xbagccore1 = + rtl92e_get_bb_reg(dev, rOFDM0_XBAGCCore1, + BitMask); + priv->initgain_backup.xcagccore1 = + rtl92e_get_bb_reg(dev, rOFDM0_XCAGCCore1, + BitMask); + priv->initgain_backup.xdagccore1 = + rtl92e_get_bb_reg(dev, rOFDM0_XDAGCCore1, + BitMask); + BitMask = bMaskByte2; + priv->initgain_backup.cca = (u8)rtl92e_get_bb_reg(dev, + rCCK0_CCA, BitMask); + + rtl92e_writeb(dev, rOFDM0_XAAGCCore1, initial_gain); + rtl92e_writeb(dev, rOFDM0_XBAGCCore1, initial_gain); + rtl92e_writeb(dev, rOFDM0_XCAGCCore1, initial_gain); + rtl92e_writeb(dev, rOFDM0_XDAGCCore1, initial_gain); + rtl92e_writeb(dev, 0xa0a, POWER_DETECTION_TH); + break; + case IG_Restore: + BitMask = 0x7f; + if (dm_digtable.dig_algorithm == + DIG_ALGO_BY_FALSE_ALARM) + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x8); + + rtl92e_set_bb_reg(dev, rOFDM0_XAAGCCore1, BitMask, + (u32)priv->initgain_backup.xaagccore1); + rtl92e_set_bb_reg(dev, rOFDM0_XBAGCCore1, BitMask, + (u32)priv->initgain_backup.xbagccore1); + rtl92e_set_bb_reg(dev, rOFDM0_XCAGCCore1, BitMask, + (u32)priv->initgain_backup.xcagccore1); + rtl92e_set_bb_reg(dev, rOFDM0_XDAGCCore1, BitMask, + (u32)priv->initgain_backup.xdagccore1); + BitMask = bMaskByte2; + rtl92e_set_bb_reg(dev, rCCK0_CCA, BitMask, + (u32)priv->initgain_backup.cca); + + rtl92e_set_tx_power(dev, + priv->rtllib->current_network.channel); + + if (dm_digtable.dig_algorithm == + DIG_ALGO_BY_FALSE_ALARM) + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x1); + break; + } + } +} + +void rtl92e_set_rf_off(struct net_device *dev) +{ + rtl92e_set_bb_reg(dev, rFPGA0_XA_RFInterfaceOE, BIT4, 0x0); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter4, 0x300, 0x0); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, 0x18, 0x0); + rtl92e_set_bb_reg(dev, rOFDM0_TRxPathEnable, 0xf, 0x0); + rtl92e_set_bb_reg(dev, rOFDM1_TRxPathEnable, 0xf, 0x0); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, 0x60, 0x0); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, 0x4, 0x0); + rtl92e_writeb(dev, ANAPAR_FOR_8192PCIE, 0x07); +} + +static bool _rtl92e_set_rf_power_state(struct net_device *dev, + enum rt_rf_power_state rf_power_state) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + (&priv->rtllib->pwr_save_ctrl); + bool bResult = true; + u8 i = 0, QueueID = 0; + struct rtl8192_tx_ring *ring = NULL; + + if (priv->set_rf_pwr_state_in_progress) + return false; + priv->set_rf_pwr_state_in_progress = true; + + switch (rf_power_state) { + case rf_on: + if ((priv->rtllib->rf_power_state == rf_off) && + RT_IN_PS_LEVEL(psc, RT_RF_OFF_LEVL_HALT_NIC)) { + bool rtstatus; + u32 InitilizeCount = 3; + + do { + InitilizeCount--; + rtstatus = rtl92e_enable_nic(dev); + } while (!rtstatus && (InitilizeCount > 0)); + if (!rtstatus) { + netdev_err(dev, + "%s(): Failed to initialize Adapter.\n", + __func__); + priv->set_rf_pwr_state_in_progress = false; + return false; + } + RT_CLEAR_PS_LEVEL(psc, + RT_RF_OFF_LEVL_HALT_NIC); + } else { + rtl92e_writeb(dev, ANAPAR, 0x37); + mdelay(1); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, + 0x4, 0x1); + priv->hw_rf_off_action = 0; + rtl92e_set_bb_reg(dev, rFPGA0_XA_RFInterfaceOE, + BIT4, 0x1); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter4, + 0x300, 0x3); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, + 0x18, 0x3); + rtl92e_set_bb_reg(dev, rOFDM0_TRxPathEnable, + 0x3, 0x3); + rtl92e_set_bb_reg(dev, rOFDM1_TRxPathEnable, + 0x3, 0x3); + rtl92e_set_bb_reg(dev, rFPGA0_AnalogParameter1, + 0x60, 0x3); + } + break; + case rf_sleep: + if (priv->rtllib->rf_power_state == rf_off) + break; + for (QueueID = 0, i = 0; QueueID < MAX_TX_QUEUE; ) { + ring = &priv->tx_ring[QueueID]; + if (skb_queue_len(&ring->queue) == 0) { + QueueID++; + continue; + } else { + udelay(10); + i++; + } + if (i >= MAX_DOZE_WAITING_TIMES_9x) + break; + } + rtl92e_set_rf_off(dev); + break; + case rf_off: + for (QueueID = 0, i = 0; QueueID < MAX_TX_QUEUE; ) { + ring = &priv->tx_ring[QueueID]; + if (skb_queue_len(&ring->queue) == 0) { + QueueID++; + continue; + } else { + udelay(10); + i++; + } + if (i >= MAX_DOZE_WAITING_TIMES_9x) + break; + } + rtl92e_set_rf_off(dev); + break; + default: + bResult = false; + netdev_warn(dev, + "%s(): Unknown state requested: 0x%X.\n", + __func__, rf_power_state); + break; + } + + if (bResult) + priv->rtllib->rf_power_state = rf_power_state; + + priv->set_rf_pwr_state_in_progress = false; + return bResult; +} + +bool rtl92e_set_rf_power_state(struct net_device *dev, + enum rt_rf_power_state rf_power_state) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + bool bResult = false; + + if (rf_power_state == priv->rtllib->rf_power_state && + priv->hw_rf_off_action == 0) { + return bResult; + } + + bResult = _rtl92e_set_rf_power_state(dev, rf_power_state); + return bResult; +} + +void rtl92e_scan_op_backup(struct net_device *dev, u8 Operation) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->up) { + switch (Operation) { + case SCAN_OPT_BACKUP: + priv->rtllib->init_gain_handler(dev, IG_Backup); + break; + + case SCAN_OPT_RESTORE: + priv->rtllib->init_gain_handler(dev, IG_Restore); + break; + } + } +} diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h new file mode 100644 index 0000000000..6c4c33ded6 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phy.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _R819XU_PHY_H +#define _R819XU_PHY_H + +#define MAX_DOZE_WAITING_TIMES_9x 64 + +enum hw90_block { + HW90_BLOCK_MAC = 0, + HW90_BLOCK_PHY0 = 1, + HW90_BLOCK_PHY1 = 2, + HW90_BLOCK_RF = 3, + HW90_BLOCK_MAXIMUM = 4, +}; + +enum rf90_radio_path { + RF90_PATH_A = 0, + RF90_PATH_B = 1, + RF90_PATH_C = 2, + RF90_PATH_D = 3, + RF90_PATH_MAX +}; + +void rtl92e_set_bb_reg(struct net_device *dev, u32 dwRegAddr, + u32 dwBitMask, u32 dwData); +u32 rtl92e_get_bb_reg(struct net_device *dev, u32 dwRegAddr, u32 dwBitMask); +void rtl92e_set_rf_reg(struct net_device *dev, enum rf90_radio_path eRFPath, + u32 RegAddr, u32 BitMask, u32 Data); +u32 rtl92e_get_rf_reg(struct net_device *dev, enum rf90_radio_path eRFPath, + u32 RegAddr, u32 BitMask); +void rtl92e_config_mac(struct net_device *dev); +bool rtl92e_check_bb_and_rf(struct net_device *dev, + enum hw90_block CheckBlock, + enum rf90_radio_path eRFPath); +bool rtl92e_config_bb(struct net_device *dev); +void rtl92e_get_tx_power(struct net_device *dev); +void rtl92e_set_tx_power(struct net_device *dev, u8 channel); +u8 rtl92e_config_rf_path(struct net_device *dev, enum rf90_radio_path eRFPath); + +u8 rtl92e_set_channel(struct net_device *dev, u8 channel); +void rtl92e_set_bw_mode(struct net_device *dev, + enum ht_channel_width bandwidth, + enum ht_extchnl_offset Offset); +void rtl92e_init_gain(struct net_device *dev, u8 Operation); + +void rtl92e_set_rf_off(struct net_device *dev); + +bool rtl92e_set_rf_power_state(struct net_device *dev, + enum rt_rf_power_state rf_power_state); + +void rtl92e_scan_op_backup(struct net_device *dev, u8 Operation); + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h new file mode 100644 index 0000000000..24fb0ca539 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_phyreg.h @@ -0,0 +1,833 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _R819XU_PHYREG_H +#define _R819XU_PHYREG_H + +#define RF_DATA 0x1d4 + +#define rPMAC_Reset 0x100 +#define rPMAC_TxStart 0x104 +#define rPMAC_TxLegacySIG 0x108 +#define rPMAC_TxHTSIG1 0x10c +#define rPMAC_TxHTSIG2 0x110 +#define rPMAC_PHYDebug 0x114 +#define rPMAC_TxPacketNum 0x118 +#define rPMAC_TxIdle 0x11c +#define rPMAC_TxMACHeader0 0x120 +#define rPMAC_TxMACHeader1 0x124 +#define rPMAC_TxMACHeader2 0x128 +#define rPMAC_TxMACHeader3 0x12c +#define rPMAC_TxMACHeader4 0x130 +#define rPMAC_TxMACHeader5 0x134 +#define rPMAC_TxDataType 0x138 +#define rPMAC_TxRandomSeed 0x13c +#define rPMAC_CCKPLCPPreamble 0x140 +#define rPMAC_CCKPLCPHeader 0x144 +#define rPMAC_CCKCRC16 0x148 +#define rPMAC_OFDMRxCRC32OK 0x170 +#define rPMAC_OFDMRxCRC32Er 0x174 +#define rPMAC_OFDMRxParityEr 0x178 +#define rPMAC_OFDMRxCRC8Er 0x17c +#define rPMAC_CCKCRxRC16Er 0x180 +#define rPMAC_CCKCRxRC32Er 0x184 +#define rPMAC_CCKCRxRC32OK 0x188 +#define rPMAC_TxStatus 0x18c + +#define MCS_TXAGC 0x340 +#define CCK_TXAGC 0x348 + +/* Mac block on/off control register */ +#define rFPGA0_RFMOD 0x800 /* RF mode & CCK TxSC */ +#define rFPGA0_TxInfo 0x804 +#define rFPGA0_PSDFunction 0x808 +#define rFPGA0_TxGainStage 0x80c +#define rFPGA0_RFTiming1 0x810 +#define rFPGA0_RFTiming2 0x814 +#define rFPGA0_XA_HSSIParameter2 0x824 +#define rFPGA0_XB_HSSIParameter2 0x82c +#define rFPGA0_XA_LSSIParameter 0x840 +#define rFPGA0_XB_LSSIParameter 0x844 +#define rFPGA0_RFWakeUpParameter 0x850 +#define rFPGA0_RFSleepUpParameter 0x854 +#define rFPGA0_XA_RFInterfaceOE 0x860 +#define rFPGA0_XB_RFInterfaceOE 0x864 +#define rFPGA0_XAB_RFInterfaceSW 0x870 +#define rFPGA0_AnalogParameter1 0x880 +#define rFPGA0_AnalogParameter2 0x884 +#define rFPGA0_AnalogParameter3 0x888 +#define rFPGA0_AnalogParameter4 0x88c +#define rFPGA0_XA_LSSIReadBack 0x8a0 +#define rFPGA0_XB_LSSIReadBack 0x8a4 +#define rFPGA0_PSDReport 0x8b4 + +/* Page 9 - RF mode & OFDM TxSC */ +#define rFPGA1_RFMOD 0x900 +#define rFPGA1_TxBlock 0x904 +#define rFPGA1_DebugSelect 0x908 +#define rFPGA1_TxInfo 0x90c + +#define rCCK0_System 0xa00 +#define rCCK0_AFESetting 0xa04 +#define rCCK0_CCA 0xa08 +/* AGC default value, saturation level */ +#define rCCK0_RxAGC1 0xa0c +#define rCCK0_RxAGC2 0xa10 /* AGC & DAGC */ +#define rCCK0_RxHP 0xa14 +/* Timing recovery & channel estimation threshold */ +#define rCCK0_DSPParameter1 0xa18 +#define rCCK0_DSPParameter2 0xa1c /* SQ threshold */ +#define rCCK0_TxFilter1 0xa20 +#define rCCK0_TxFilter2 0xa24 +#define rCCK0_DebugPort 0xa28 /* Debug port and TX filter 3 */ +#define rCCK0_FalseAlarmReport 0xa2c +#define rCCK0_TRSSIReport 0xa50 +#define rCCK0_RxReport 0xa54 +#define rCCK0_FACounterLower 0xa5c +#define rCCK0_FACounterUpper 0xa58 + +#define rOFDM0_LSTF 0xc00 +#define rOFDM0_TRxPathEnable 0xc04 +#define rOFDM0_TRMuxPar 0xc08 +#define rOFDM0_TRSWIsolation 0xc0c +#define rOFDM0_RxDetector1 0xc30 /* PD, BW & SBD */ +#define rOFDM0_RxDetector2 0xc34 /* SBD */ +#define rOFDM0_RxDetector3 0xc38 /* Frame Sync */ +/* PD, SBD, Frame Sync & Short-GI */ +#define rOFDM0_RxDetector4 0xc3c +#define rOFDM0_RxDSP 0xc40 /* Rx Sync Path */ +#define rOFDM0_CFOandDAGC 0xc44 /* CFO & DAGC */ +#define rOFDM0_CCADropThreshold 0xc48 +#define rOFDM0_ECCAThreshold 0xc4c /* Energy CCA */ +#define rOFDM0_XAAGCCore1 0xc50 +#define rOFDM0_XBAGCCore1 0xc58 +#define rOFDM0_XCAGCCore1 0xc60 +#define rOFDM0_XDAGCCore1 0xc68 +#define rOFDM0_AGCParameter1 0xc70 +#define rOFDM0_AGCParameter2 0xc74 +#define rOFDM0_AGCRSSITable 0xc78 +#define rOFDM0_HTSTFAGC 0xc7c +#define rOFDM0_XATxIQImbalance 0xc80 +#define rOFDM0_XATxAFE 0xc84 +#define rOFDM0_XCTxIQImbalance 0xc90 +#define rOFDM0_RxHPParameter 0xce0 +#define rOFDM0_TxPseudoNoiseWgt 0xce4 +#define rOFDM0_FrameSync 0xcf0 +#define rOFDM0_DFSReport 0xcf4 +#define rOFDM0_TxCoeff1 0xca4 +#define rOFDM0_TxCoeff2 0xca8 +#define rOFDM0_TxCoeff3 0xcac +#define rOFDM0_TxCoeff4 0xcb0 +#define rOFDM0_TxCoeff5 0xcb4 +#define rOFDM0_TxCoeff6 0xcb8 + +#define rOFDM1_LSTF 0xd00 +#define rOFDM1_TRxPathEnable 0xd04 +#define rOFDM1_CFO 0xd08 +#define rOFDM1_CSI1 0xd10 +#define rOFDM1_SBD 0xd14 +#define rOFDM1_CSI2 0xd18 +#define rOFDM1_CFOTracking 0xd2c +#define rOFDM1_TRxMesaure1 0xd34 +#define rOFDM1_IntfDet 0xd3c +#define rOFDM1_PseudoNoiseStateAB 0xd50 +#define rOFDM1_PseudoNoiseStateCD 0xd54 +#define rOFDM1_RxPseudoNoiseWgt 0xd58 +#define rOFDM_PHYCounter1 0xda0 /* cca, parity fail */ +#define rOFDM_PHYCounter2 0xda4 /* rate illegal, crc8 fail */ +#define rOFDM_PHYCounter3 0xda8 /* MCS not supported */ +#define rOFDM_ShortCFOAB 0xdac +#define rOFDM_ShortCFOCD 0xdb0 +#define rOFDM_LongCFOAB 0xdb4 +#define rOFDM_LongCFOCD 0xdb8 +#define rOFDM_TailCFOAB 0xdbc +#define rOFDM_TailCFOCD 0xdc0 +#define rOFDM_PWMeasure1 0xdc4 +#define rOFDM_PWMeasure2 0xdc8 +#define rOFDM_BWReport 0xdcc +#define rOFDM_AGCReport 0xdd0 +#define rOFDM_RxSNR 0xdd4 +#define rOFDM_RxEVMCSI 0xdd8 +#define rOFDM_SIGReport 0xddc + +#define rTxAGC_Rate18_06 0xe00 +#define rTxAGC_Rate54_24 0xe04 +#define rTxAGC_CCK_Mcs32 0xe08 +#define rTxAGC_Mcs03_Mcs00 0xe10 +#define rTxAGC_Mcs07_Mcs04 0xe14 +#define rTxAGC_Mcs11_Mcs08 0xe18 +#define rTxAGC_Mcs15_Mcs12 0xe1c + +#define rZebra1_HSSIEnable 0x0 +#define rZebra1_TRxEnable1 0x1 +#define rZebra1_TRxEnable2 0x2 +#define rZebra1_AGC 0x4 +#define rZebra1_ChargePump 0x5 +#define rZebra1_Channel 0x7 +#define rZebra1_TxGain 0x8 +#define rZebra1_TxLPF 0x9 +#define rZebra1_RxLPF 0xb +#define rZebra1_RxHPFCorner 0xc + +/* Zebra 4 */ +#define rGlobalCtrl 0 +#define rRTL8256_TxLPF 19 +#define rRTL8256_RxLPF 11 + +/* RTL8258 */ +#define rRTL8258_TxLPF 0x11 +#define rRTL8258_RxLPF 0x13 +#define rRTL8258_RSSILPF 0xa + +/* Bit Mask - Page 1*/ +#define bBBResetB 0x100 +#define bGlobalResetB 0x200 +#define bOFDMTxStart 0x4 +#define bCCKTxStart 0x8 +#define bCRC32Debug 0x100 +#define bPMACLoopback 0x10 +#define bTxLSIG 0xffffff +#define bOFDMTxRate 0xf +#define bOFDMTxReserved 0x10 +#define bOFDMTxLength 0x1ffe0 +#define bOFDMTxParity 0x20000 +#define bTxHTSIG1 0xffffff +#define bTxHTMCSRate 0x7f +#define bTxHTBW 0x80 +#define bTxHTLength 0xffff00 +#define bTxHTSIG2 0xffffff +#define bTxHTSmoothing 0x1 +#define bTxHTSounding 0x2 +#define bTxHTReserved 0x4 +#define bTxHTAggreation 0x8 +#define bTxHTSTBC 0x30 +#define bTxHTAdvanceCoding 0x40 +#define bTxHTShortGI 0x80 +#define bTxHTNumberHT_LTF 0x300 +#define bTxHTCRC8 0x3fc00 +#define bCounterReset 0x10000 +#define bNumOfOFDMTx 0xffff +#define bNumOfCCKTx 0xffff0000 +#define bTxIdleInterval 0xffff +#define bOFDMService 0xffff0000 +#define bTxMACHeader 0xffffffff +#define bTxDataInit 0xff +#define bTxHTMode 0x100 +#define bTxDataType 0x30000 +#define bTxRandomSeed 0xffffffff +#define bCCKTxPreamble 0x1 +#define bCCKTxSFD 0xffff0000 +#define bCCKTxSIG 0xff +#define bCCKTxService 0xff00 +#define bCCKLengthExt 0x8000 +#define bCCKTxLength 0xffff0000 +#define bCCKTxCRC16 0xffff +#define bCCKTxStatus 0x1 +#define bOFDMTxStatus 0x2 +/* Bit Mask - Page 8 */ +#define bRFMOD 0x1 +#define bJapanMode 0x2 +#define bCCKTxSC 0x30 +#define bCCKEn 0x1000000 +#define bOFDMEn 0x2000000 +#define bOFDMRxADCPhase 0x10000 +#define bOFDMTxDACPhase 0x40000 +#define bXATxAGC 0x3f +#define bXBTxAGC 0xf00 +#define bXCTxAGC 0xf000 +#define bXDTxAGC 0xf0000 +#define bPAStart 0xf0000000 +#define bTRStart 0x00f00000 +#define bRFStart 0x0000f000 +#define bBBStart 0x000000f0 +#define bBBCCKStart 0x0000000f +/* Bit Mask - rFPGA0_RFTiming2 */ +#define bPAEnd 0xf +#define bTREnd 0x0f000000 +#define bRFEnd 0x000f0000 +/* T2R */ +#define bCCAMask 0x000000f0 +#define bR2RCCAMask 0x00000f00 +#define bHSSI_R2TDelay 0xf8000000 +#define bHSSI_T2RDelay 0xf80000 +/* Channel gain at continue TX. */ +#define bContTxHSSI 0x400 +#define bIGFromCCK 0x200 +#define bAGCAddress 0x3f +#define bRxHPTx 0x7000 +#define bRxHPT2R 0x38000 +#define bRxHPCCKIni 0xc0000 +#define bAGCTxCode 0xc00000 +#define bAGCRxCode 0x300000 +#define b3WireDataLength 0x800 +#define b3WireAddressLength 0x400 +#define b3WireRFPowerDown 0x1 +/*#define bHWSISelect 0x8 */ +#define b2GPAPEPolarity 0x80000000 +#define bRFSW_TxDefaultAnt 0x3 +#define bRFSW_TxOptionAnt 0x30 +#define bRFSW_RxDefaultAnt 0x300 +#define bRFSW_RxOptionAnt 0x3000 +#define bRFSI_3WireData 0x1 +#define bRFSI_3WireClock 0x2 +#define bRFSI_3WireLoad 0x4 +#define bRFSI_3WireRW 0x8 +/* 3-wire total control */ +#define bRFSI_3Wire 0xf +#define bRFSI_RFENV 0x10 +#define bRFSI_TRSW 0x20 +#define bRFSI_TRSWB 0x40 +#define bRFSI_ANTSW 0x100 +#define bRFSI_ANTSWB 0x200 +#define bRFSI_PAPE 0x400 +#define bBandSelect 0x1 +#define bHTSIG2_GI 0x80 +#define bHTSIG2_Smoothing 0x01 +#define bHTSIG2_Sounding 0x02 +#define bHTSIG2_Aggreaton 0x08 +#define bHTSIG2_STBC 0x30 +#define bHTSIG2_AdvCoding 0x40 +#define bHTSIG2_NumOfHTLTF 0x300 +#define bHTSIG2_CRC8 0x3fc +#define bHTSIG1_MCS 0x7f +#define bHTSIG1_BandWidth 0x80 +#define bHTSIG1_HTLength 0xffff +#define bLSIG_Rate 0xf +#define bLSIG_Reserved 0x10 +#define bLSIG_Length 0x1fffe +#define bLSIG_Parity 0x20 +#define bCCKRxPhase 0x4 +#define bLSSIReadAddress 0x3f000000 /* LSSI "read" address */ +#define bLSSIReadEdge 0x80000000 /* LSSI "read" edge signal */ +#define bLSSIReadBackData 0xfff +#define bLSSIReadOKFlag 0x1000 +#define bCCKSampleRate 0x8 /* 0: 44 MHz, 1: 88MHz */ + +#define bRegulator0Standby 0x1 +#define bRegulatorPLLStandby 0x2 +#define bRegulator1Standby 0x4 +#define bPLLPowerUp 0x8 +#define bDPLLPowerUp 0x10 +#define bDA10PowerUp 0x20 +#define bAD7PowerUp 0x200 +#define bDA6PowerUp 0x2000 +#define bXtalPowerUp 0x4000 +#define b40MDClkPowerUP 0x8000 +#define bDA6DebugMode 0x20000 +#define bDA6Swing 0x380000 +#define bADClkPhase 0x4000000 +#define b80MClkDelay 0x18000000 +#define bAFEWatchDogEnable 0x20000000 +#define bXtalCap 0x0f000000 +#define bXtalCap01 0xc0000000 +#define bXtalCap23 0x3 +#define bXtalCap92x 0x0f000000 +#define bIntDifClkEnable 0x400 +#define bExtSigClkEnable 0x800 +#define bBandgapMbiasPowerUp 0x10000 +#define bAD11SHGain 0xc0000 +#define bAD11InputRange 0x700000 +#define bAD11OPCurrent 0x3800000 +#define bIPathLoopback 0x4000000 +#define bQPathLoopback 0x8000000 +#define bAFELoopback 0x10000000 +#define bDA10Swing 0x7e0 +#define bDA10Reverse 0x800 +#define bDAClkSource 0x1000 +#define bAD7InputRange 0x6000 +#define bAD7Gain 0x38000 +#define bAD7OutputCMMode 0x40000 +#define bAD7InputCMMode 0x380000 +#define bAD7Current 0xc00000 +#define bRegulatorAdjust 0x7000000 +#define bAD11PowerUpAtTx 0x1 +#define bDA10PSAtTx 0x10 +#define bAD11PowerUpAtRx 0x100 +#define bDA10PSAtRx 0x1000 + +#define bCCKRxAGCFormat 0x200 + +#define bPSDFFTSamplepPoint 0xc000 +#define bPSDAverageNum 0x3000 +#define bIQPathControl 0xc00 +#define bPSDFreq 0x3ff +#define bPSDAntennaPath 0x30 +#define bPSDIQSwitch 0x40 +#define bPSDRxTrigger 0x400000 +#define bPSDTxTrigger 0x80000000 +#define bPSDSineToneScale 0x7f000000 +#define bPSDReport 0xffff + +/* Page 8 */ +#define bOFDMTxSC 0x30000000 +#define bCCKTxOn 0x1 +#define bOFDMTxOn 0x2 +/* Reset debug page and also HWord, LWord */ +#define bDebugPage 0xfff +/* Reset debug page and LWord */ +#define bDebugItem 0xff +#define bAntL 0x10 +#define bAntNonHT 0x100 +#define bAntHT1 0x1000 +#define bAntHT2 0x10000 +#define bAntHT1S1 0x100000 +#define bAntNonHTS1 0x1000000 + +/* Page a */ +#define bCCKBBMode 0x3 +#define bCCKTxPowerSaving 0x80 +#define bCCKRxPowerSaving 0x40 +#define bCCKSideBand 0x10 +#define bCCKScramble 0x8 +#define bCCKAntDiversity 0x8000 +#define bCCKCarrierRecovery 0x4000 +#define bCCKTxRate 0x3000 +#define bCCKDCCancel 0x0800 +#define bCCKISICancel 0x0400 +#define bCCKMatchFilter 0x0200 +#define bCCKEqualizer 0x0100 +#define bCCKPreambleDetect 0x800000 +#define bCCKFastFalseCCA 0x400000 +#define bCCKChEstStart 0x300000 +#define bCCKCCACount 0x080000 +#define bCCKcs_lim 0x070000 +#define bCCKBistMode 0x80000000 +#define bCCKCCAMask 0x40000000 +#define bCCKTxDACPhase 0x4 +#define bCCKRxADCPhase 0x20000000 /* r_rx_clk */ +#define bCCKr_cp_mode0 0x0100 +#define bCCKTxDCOffset 0xf0 +#define bCCKRxDCOffset 0xf +#define bCCKCCAMode 0xc000 +#define bCCKFalseCS_lim 0x3f00 +#define bCCKCS_ratio 0xc00000 +#define bCCKCorgBit_sel 0x300000 +#define bCCKPD_lim 0x0f0000 +#define bCCKNewCCA 0x80000000 +#define bCCKRxHPofIG 0x8000 +#define bCCKRxIG 0x7f00 +#define bCCKLNAPolarity 0x800000 +#define bCCKRx1stGain 0x7f0000 +/* CCK Rx Initial gain polarity */ +#define bCCKRFExtend 0x20000000 +#define bCCKRxAGCSatLevel 0x1f000000 +#define bCCKRxAGCSatCount 0xe0 +/* AGCSAmp_dly */ +#define bCCKRxRFSettle 0x1f +#define bCCKFixedRxAGC 0x8000 +/*#define bCCKRxAGCFormat 0x4000 remove to HSSI register 0x824 */ +#define bCCKAntennaPolarity 0x2000 +#define bCCKTxFilterType 0x0c00 +#define bCCKRxAGCReportType 0x0300 +#define bCCKRxDAGCEn 0x80000000 +#define bCCKRxDAGCPeriod 0x20000000 +#define bCCKRxDAGCSatLevel 0x1f000000 +#define bCCKTimingRecovery 0x800000 +#define bCCKTxC0 0x3f0000 +#define bCCKTxC1 0x3f000000 +#define bCCKTxC2 0x3f +#define bCCKTxC3 0x3f00 +#define bCCKTxC4 0x3f0000 +#define bCCKTxC5 0x3f000000 +#define bCCKTxC6 0x3f +#define bCCKTxC7 0x3f00 +#define bCCKDebugPort 0xff0000 +#define bCCKDACDebug 0x0f000000 +#define bCCKFalseAlarmEnable 0x8000 +#define bCCKFalseAlarmRead 0x4000 +#define bCCKTRSSI 0x7f +#define bCCKRxAGCReport 0xfe +#define bCCKRxReport_AntSel 0x80000000 +#define bCCKRxReport_MFOff 0x40000000 +#define bCCKRxRxReport_SQLoss 0x20000000 +#define bCCKRxReport_Pktloss 0x10000000 +#define bCCKRxReport_Lockedbit 0x08000000 +#define bCCKRxReport_RateError 0x04000000 +#define bCCKRxReport_RxRate 0x03000000 +#define bCCKRxFACounterLower 0xff +#define bCCKRxFACounterUpper 0xff000000 +#define bCCKRxHPAGCStart 0xe000 +#define bCCKRxHPAGCFinal 0x1c00 + +#define bCCKRxFalseAlarmEnable 0x8000 +#define bCCKFACounterFreeze 0x4000 + +#define bCCKTxPathSel 0x10000000 +#define bCCKDefaultRxPath 0xc000000 +#define bCCKOptionRxPath 0x3000000 + +/* Page c */ +#define bNumOfSTF 0x3 +#define bShift_L 0xc0 +#define bGI_TH 0xc +#define bRxPathA 0x1 +#define bRxPathB 0x2 +#define bRxPathC 0x4 +#define bRxPathD 0x8 +#define bTxPathA 0x1 +#define bTxPathB 0x2 +#define bTxPathC 0x4 +#define bTxPathD 0x8 +#define bTRSSIFreq 0x200 +#define bADCBackoff 0x3000 +#define bDFIRBackoff 0xc000 +#define bTRSSILatchPhase 0x10000 +#define bRxIDCOffset 0xff +#define bRxQDCOffset 0xff00 +#define bRxDFIRMode 0x1800000 +#define bRxDCNFType 0xe000000 +#define bRXIQImb_A 0x3ff +#define bRXIQImb_B 0xfc00 +#define bRXIQImb_C 0x3f0000 +#define bRXIQImb_D 0xffc00000 +#define bDC_dc_Notch 0x60000 +#define bRxNBINotch 0x1f000000 +#define bPD_TH 0xf +#define bPD_TH_Opt2 0xc000 +#define bPWED_TH 0x700 +#define bIfMF_Win_L 0x800 +#define bPD_Option 0x1000 +#define bMF_Win_L 0xe000 +#define bBW_Search_L 0x30000 +#define bwin_enh_L 0xc0000 +#define bBW_TH 0x700000 +#define bED_TH2 0x3800000 +#define bBW_option 0x4000000 +#define bRatio_TH 0x18000000 +#define bWindow_L 0xe0000000 +#define bSBD_Option 0x1 +#define bFrame_TH 0x1c +#define bFS_Option 0x60 +#define bDC_Slope_check 0x80 +#define bFGuard_Counter_DC_L 0xe00 +#define bFrame_Weight_Short 0x7000 +#define bSub_Tune 0xe00000 +#define bFrame_DC_Length 0xe000000 +#define bSBD_start_offset 0x30000000 +#define bFrame_TH_2 0x7 +#define bFrame_GI2_TH 0x38 +#define bGI2_Sync_en 0x40 +#define bSarch_Short_Early 0x300 +#define bSarch_Short_Late 0xc00 +#define bSarch_GI2_Late 0x70000 +#define bCFOAntSum 0x1 +#define bCFOAcc 0x2 +#define bCFOStartOffset 0xc +#define bCFOLookBack 0x70 +#define bCFOSumWeight 0x80 +#define bDAGCEnable 0x10000 +#define bTXIQImb_A 0x3ff +#define bTXIQImb_B 0xfc00 +#define bTXIQImb_C 0x3f0000 +#define bTXIQImb_D 0xffc00000 +#define bTxIDCOffset 0xff +#define bTxQDCOffset 0xff00 +#define bTxDFIRMode 0x10000 +#define bTxPesudoNoiseOn 0x4000000 +#define bTxPesudoNoise_A 0xff +#define bTxPesudoNoise_B 0xff00 +#define bTxPesudoNoise_C 0xff0000 +#define bTxPesudoNoise_D 0xff000000 +#define bCCADropOption 0x20000 +#define bCCADropThres 0xfff00000 +#define bEDCCA_H 0xf +#define bEDCCA_L 0xf0 +#define bLambda_ED 0x300 +#define bRxInitialGain 0x7f +#define bRxAntDivEn 0x80 +#define bRxAGCAddressForLNA 0x7f00 +#define bRxHighPowerFlow 0x8000 +#define bRxAGCFreezeThres 0xc0000 +#define bRxFreezeStep_AGC1 0x300000 +#define bRxFreezeStep_AGC2 0xc00000 +#define bRxFreezeStep_AGC3 0x3000000 +#define bRxFreezeStep_AGC0 0xc000000 +#define bRxRssi_Cmp_En 0x10000000 +#define bRxQuickAGCEn 0x20000000 +#define bRxAGCFreezeThresMode 0x40000000 +#define bRxOverFlowCheckType 0x80000000 +#define bRxAGCShift 0x7f +#define bTRSW_Tri_Only 0x80 +#define bPowerThres 0x300 +#define bRxAGCEn 0x1 +#define bRxAGCTogetherEn 0x2 +#define bRxAGCMin 0x4 +#define bRxHP_Ini 0x7 +#define bRxHP_TRLNA 0x70 +#define bRxHP_RSSI 0x700 +#define bRxHP_BBP1 0x7000 +#define bRxHP_BBP2 0x70000 +#define bRxHP_BBP3 0x700000 +/* The threshold for high power */ +#define bRSSI_H 0x7f0000 +/* The threshold for ant diversity */ +#define bRSSI_Gen 0x7f000000 +#define bRxSettle_TRSW 0x7 +#define bRxSettle_LNA 0x38 +#define bRxSettle_RSSI 0x1c0 +#define bRxSettle_BBP 0xe00 +#define bRxSettle_RxHP 0x7000 +#define bRxSettle_AntSW_RSSI 0x38000 +#define bRxSettle_AntSW 0xc0000 +#define bRxProcessTime_DAGC 0x300000 +#define bRxSettle_HSSI 0x400000 +#define bRxProcessTime_BBPPW 0x800000 +#define bRxAntennaPowerShift 0x3000000 +#define bRSSITableSelect 0xc000000 +#define bRxHP_Final 0x7000000 +#define bRxHTSettle_BBP 0x7 +#define bRxHTSettle_HSSI 0x8 +#define bRxHTSettle_RxHP 0x70 +#define bRxHTSettle_BBPPW 0x80 +#define bRxHTSettle_Idle 0x300 +#define bRxHTSettle_Reserved 0x1c00 +#define bRxHTRxHPEn 0x8000 +#define bRxHTAGCFreezeThres 0x30000 +#define bRxHTAGCTogetherEn 0x40000 +#define bRxHTAGCMin 0x80000 +#define bRxHTAGCEn 0x100000 +#define bRxHTDAGCEn 0x200000 +#define bRxHTRxHP_BBP 0x1c00000 +#define bRxHTRxHP_Final 0xe0000000 +#define bRxPWRatioTH 0x3 +#define bRxPWRatioEn 0x4 +#define bRxMFHold 0x3800 +#define bRxPD_Delay_TH1 0x38 +#define bRxPD_Delay_TH2 0x1c0 +#define bRxPD_DC_COUNT_MAX 0x600 +/*#define bRxMF_Hold 0x3800*/ +#define bRxPD_Delay_TH 0x8000 +#define bRxProcess_Delay 0xf0000 +#define bRxSearchrange_GI2_Early 0x700000 +#define bRxFrame_Guard_Counter_L 0x3800000 +#define bRxSGI_Guard_L 0xc000000 +#define bRxSGI_Search_L 0x30000000 +#define bRxSGI_TH 0xc0000000 +#define bDFSCnt0 0xff +#define bDFSCnt1 0xff00 +#define bDFSFlag 0xf0000 + +#define bMFWeightSum 0x300000 +#define bMinIdxTH 0x7f000000 + +#define bDAFormat 0x40000 + +#define bTxChEmuEnable 0x01000000 + +#define bTRSWIsolation_A 0x7f +#define bTRSWIsolation_B 0x7f00 +#define bTRSWIsolation_C 0x7f0000 +#define bTRSWIsolation_D 0x7f000000 + +#define bExtLNAGain 0x7c00 + +/* Page d */ +#define bSTBCEn 0x4 +#define bAntennaMapping 0x10 +#define bNss 0x20 +#define bCFOAntSumD 0x200 +#define bPHYCounterReset 0x8000000 +#define bCFOReportGet 0x4000000 +#define bOFDMContinueTx 0x10000000 +#define bOFDMSingleCarrier 0x20000000 +#define bOFDMSingleTone 0x40000000 +/* #define bRxPath1 0x01 + * #define bRxPath2 0x02 + * #define bRxPath3 0x04 + * #define bRxPath4 0x08 + * #define bTxPath1 0x10 + * #define bTxPath2 0x20 + */ +#define bHTDetect 0x100 +#define bCFOEn 0x10000 +#define bCFOValue 0xfff00000 +#define bSigTone_Re 0x3f +#define bSigTone_Im 0x7f00 +#define bCounter_CCA 0xffff +#define bCounter_ParityFail 0xffff0000 +#define bCounter_RateIllegal 0xffff +#define bCounter_CRC8Fail 0xffff0000 +#define bCounter_MCSNoSupport 0xffff +#define bCounter_FastSync 0xffff +#define bShortCFO 0xfff +#define bShortCFOTLength 12 /* total */ +#define bShortCFOFLength 11 /* fraction */ +#define bLongCFO 0x7ff +#define bLongCFOTLength 11 +#define bLongCFOFLength 11 +#define bTailCFO 0x1fff +#define bTailCFOTLength 13 +#define bTailCFOFLength 12 + +#define bmax_en_pwdB 0xffff +#define bCC_power_dB 0xffff0000 +#define bnoise_pwdB 0xffff +#define bPowerMeasTLength 10 +#define bPowerMeasFLength 3 +#define bRx_HT_BW 0x1 +#define bRxSC 0x6 +#define bRx_HT 0x8 + +#define bNB_intf_det_on 0x1 +#define bIntf_win_len_cfg 0x30 +#define bNB_Intf_TH_cfg 0x1c0 + +#define bRFGain 0x3f +#define bTableSel 0x40 +#define bTRSW 0x80 + +#define bRxSNR_A 0xff +#define bRxSNR_B 0xff00 +#define bRxSNR_C 0xff0000 +#define bRxSNR_D 0xff000000 +#define bSNREVMTLength 8 +#define bSNREVMFLength 1 + +#define bCSI1st 0xff +#define bCSI2nd 0xff00 +#define bRxEVM1st 0xff0000 +#define bRxEVM2nd 0xff000000 + +#define bSIGEVM 0xff +#define bPWDB 0xff00 +#define bSGIEN 0x10000 + +#define bSFactorQAM1 0xf +#define bSFactorQAM2 0xf0 +#define bSFactorQAM3 0xf00 +#define bSFactorQAM4 0xf000 +#define bSFactorQAM5 0xf0000 +#define bSFactorQAM6 0xf0000 +#define bSFactorQAM7 0xf00000 +#define bSFactorQAM8 0xf000000 +#define bSFactorQAM9 0xf0000000 +#define bCSIScheme 0x100000 + +#define bNoiseLvlTopSet 0x3 +#define bChSmooth 0x4 +#define bChSmoothCfg1 0x38 +#define bChSmoothCfg2 0x1c0 +#define bChSmoothCfg3 0xe00 +#define bChSmoothCfg4 0x7000 +#define bMRCMode 0x800000 +#define bTHEVMCfg 0x7000000 + +#define bLoopFitType 0x1 +#define bUpdCFO 0x40 +#define bUpdCFOOffData 0x80 +#define bAdvUpdCFO 0x100 +#define bAdvTimeCtrl 0x800 +#define bUpdClko 0x1000 +#define bFC 0x6000 +#define bTrackingMode 0x8000 +#define bPhCmpEnable 0x10000 +#define bUpdClkoLTF 0x20000 +#define bComChCFO 0x40000 +#define bCSIEstiMode 0x80000 +#define bAdvUpdEqz 0x100000 +#define bUChCfg 0x7000000 +#define bUpdEqz 0x8000000 + +/* Page e */ +#define bTxAGCRate18_06 0x7f7f7f7f +#define bTxAGCRate54_24 0x7f7f7f7f +#define bTxAGCRateMCS32 0x7f +#define bTxAGCRateCCK 0x7f00 +#define bTxAGCRateMCS3_MCS0 0x7f7f7f7f +#define bTxAGCRateMCS7_MCS4 0x7f7f7f7f +#define bTxAGCRateMCS11_MCS8 0x7f7f7f7f +#define bTxAGCRateMCS15_MCS12 0x7f7f7f7f + +#define bRxPesudoNoiseOn 0x20000000 /* Rx Pseduo noise */ +#define bRxPesudoNoise_A 0xff +#define bRxPesudoNoise_B 0xff00 +#define bRxPesudoNoise_C 0xff0000 +#define bRxPesudoNoise_D 0xff000000 +#define bPesudoNoiseState_A 0xffff +#define bPesudoNoiseState_B 0xffff0000 +#define bPesudoNoiseState_C 0xffff +#define bPesudoNoiseState_D 0xffff0000 + +/* RF Zebra 1 */ +#define bZebra1_HSSIEnable 0x8 +#define bZebra1_TRxControl 0xc00 +#define bZebra1_TRxGainSetting 0x07f +#define bZebra1_RxCorner 0xc00 +#define bZebra1_TxChargePump 0x38 +#define bZebra1_RxChargePump 0x7 +#define bZebra1_ChannelNum 0xf80 +#define bZebra1_TxLPFBW 0x400 +#define bZebra1_RxLPFBW 0x600 + +/* Zebra4 */ +#define bRTL8256RegModeCtrl1 0x100 +#define bRTL8256RegModeCtrl0 0x40 +#define bRTL8256_TxLPFBW 0x18 +#define bRTL8256_RxLPFBW 0x600 + +/* RTL8258 */ +#define bRTL8258_TxLPFBW 0xc +#define bRTL8258_RxLPFBW 0xc00 +#define bRTL8258_RSSILPFBW 0xc0 + +/* byte enable for sb_write */ +#define bByte0 0x1 +#define bByte1 0x2 +#define bByte2 0x4 +#define bByte3 0x8 +#define bWord0 0x3 +#define bWord1 0xc +#define bDWord 0xf + +/* for PutRegsetting & GetRegSetting BitMask */ +#define bMaskByte0 0xff +#define bMaskByte1 0xff00 +#define bMaskByte2 0xff0000 +#define bMaskByte3 0xff000000 +#define bMaskHWord 0xffff0000 +#define bMaskLWord 0x0000ffff +#define bMaskDWord 0xffffffff + +/* for PutRFRegsetting & GetRFRegSetting BitMask */ +#define bMask12Bits 0xfff + +#define bEnable 0x1 +#define bDisable 0x0 + +#define LeftAntenna 0x0 +#define RightAntenna 0x1 + +#define tCheckTxStatus 500 /* 500 ms */ +#define tUpdateRxCounter 100 /* 100 ms */ + +#define rateCCK 0 +#define rateOFDM 1 +#define rateHT 2 + +#define bPMAC_End 0x1ff /* define Register-End */ +#define bFPGAPHY0_End 0x8ff +#define bFPGAPHY1_End 0x9ff +#define bCCKPHY0_End 0xaff +#define bOFDMPHY0_End 0xcff +#define bOFDMPHY1_End 0xdff + +#define bPMACControl 0x0 +#define bWMACControl 0x1 +#define bWNICControl 0x2 + +#define PathA 0x0 +#define PathB 0x1 +#define PathC 0x2 +#define PathD 0x3 + +#define rRTL8256RxMixerPole 0xb +#define bZebraRxMixerPole 0x6 +#define rRTL8256TxBBOPBias 0x9 +#define bRTL8256TxBBOPBias 0x400 +#define rRTL8256TxBBBW 19 +#define bRTL8256TxBBBW 0x18 + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c new file mode 100644 index 0000000000..944cc73fb2 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "r8192E_phy.h" +#include "r8192E_phyreg.h" +#include "r8190P_rtl8256.h" /* RTL8225 Radio frontend */ +#include "r8192E_cmdpkt.h" + +void rtl92e_cam_reset(struct net_device *dev) +{ + u32 ulcommand = 0; + + ulcommand |= BIT31 | BIT30; + rtl92e_writel(dev, RWCAM, ulcommand); +} + +void rtl92e_enable_hw_security_config(struct net_device *dev) +{ + u8 SECR_value = 0x0; + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + SECR_value = SCR_TxEncEnable | SCR_RxDecEnable; + if (((ieee->pairwise_key_type == KEY_TYPE_WEP40) || + (ieee->pairwise_key_type == KEY_TYPE_WEP104)) && + (priv->rtllib->auth_mode != 2)) { + SECR_value |= SCR_RxUseDK; + SECR_value |= SCR_TxUseDK; + } else if ((ieee->iw_mode == IW_MODE_ADHOC) && + (ieee->pairwise_key_type & (KEY_TYPE_CCMP | + KEY_TYPE_TKIP))) { + SECR_value |= SCR_RxUseDK; + SECR_value |= SCR_TxUseDK; + } + + ieee->hwsec_active = 1; + if ((ieee->ht_info->iot_action & HT_IOT_ACT_PURE_N_MODE) || !hwwep) { + ieee->hwsec_active = 0; + SECR_value &= ~SCR_RxDecEnable; + } + rtl92e_writeb(dev, SECR, SECR_value); +} + +void rtl92e_set_swcam(struct net_device *dev, u8 EntryNo, u8 KeyIndex, + u16 KeyType, const u8 *MacAddr, u32 *KeyContent) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + if (EntryNo >= TOTAL_CAM_ENTRY) + return; + + ieee->swcamtable[EntryNo].bused = true; + ieee->swcamtable[EntryNo].key_index = KeyIndex; + ieee->swcamtable[EntryNo].key_type = KeyType; + memcpy(ieee->swcamtable[EntryNo].macaddr, MacAddr, 6); + ieee->swcamtable[EntryNo].useDK = 0; + memcpy(ieee->swcamtable[EntryNo].key_buf, (u8 *)KeyContent, 16); +} + +void rtl92e_set_key(struct net_device *dev, u8 EntryNo, u8 KeyIndex, + u16 KeyType, const u8 *MacAddr, u8 DefaultKey, + u32 *KeyContent) +{ + u32 TargetCommand = 0; + u32 TargetContent = 0; + u16 usConfig = 0; + u8 i; + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + enum rt_rf_power_state rt_state; + + rt_state = priv->rtllib->rf_power_state; + if (rt_state == rf_off) { + if (priv->rtllib->rf_off_reason > RF_CHANGE_BY_IPS) { + netdev_warn(dev, "%s(): RF is OFF.\n", + __func__); + return; + } + mutex_lock(&priv->rtllib->ips_mutex); + rtl92e_ips_leave(dev); + mutex_unlock(&priv->rtllib->ips_mutex); + } + priv->rtllib->is_set_key = true; + if (EntryNo >= TOTAL_CAM_ENTRY) { + netdev_info(dev, "%s(): Invalid CAM entry\n", __func__); + return; + } + + if (DefaultKey) + usConfig |= BIT15 | (KeyType << 2); + else + usConfig |= BIT15 | (KeyType << 2) | KeyIndex; + + for (i = 0; i < CAM_CONTENT_COUNT; i++) { + TargetCommand = i + CAM_CONTENT_COUNT * EntryNo; + TargetCommand |= BIT31 | BIT16; + + if (i == 0) { + TargetContent = (u32)(*(MacAddr + 0)) << 16 | + (u32)(*(MacAddr + 1)) << 24 | + (u32)usConfig; + + rtl92e_writel(dev, WCAMI, TargetContent); + rtl92e_writel(dev, RWCAM, TargetCommand); + } else if (i == 1) { + TargetContent = (u32)(*(MacAddr + 2)) | + (u32)(*(MacAddr + 3)) << 8 | + (u32)(*(MacAddr + 4)) << 16 | + (u32)(*(MacAddr + 5)) << 24; + rtl92e_writel(dev, WCAMI, TargetContent); + rtl92e_writel(dev, RWCAM, TargetCommand); + } else { + if (KeyContent != NULL) { + rtl92e_writel(dev, WCAMI, + (u32)(*(KeyContent + i - 2))); + rtl92e_writel(dev, RWCAM, TargetCommand); + udelay(100); + } + } + } +} + +void rtl92e_cam_restore(struct net_device *dev) +{ + u8 EntryId = 0; + struct r8192_priv *priv = rtllib_priv(dev); + u8 *MacAddr = priv->rtllib->current_network.bssid; + + static u8 CAM_CONST_ADDR[4][6] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} + }; + static u8 CAM_CONST_BROAD[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + if ((priv->rtllib->pairwise_key_type == KEY_TYPE_WEP40) || + (priv->rtllib->pairwise_key_type == KEY_TYPE_WEP104)) { + for (EntryId = 0; EntryId < 4; EntryId++) { + MacAddr = CAM_CONST_ADDR[EntryId]; + if (priv->rtllib->swcamtable[EntryId].bused) { + rtl92e_set_key(dev, EntryId, EntryId, + priv->rtllib->pairwise_key_type, + MacAddr, 0, + (u32 *)(&priv->rtllib->swcamtable + [EntryId].key_buf[0])); + } + } + + } else if (priv->rtllib->pairwise_key_type == KEY_TYPE_TKIP) { + if (priv->rtllib->iw_mode == IW_MODE_ADHOC) { + rtl92e_set_key(dev, 4, 0, + priv->rtllib->pairwise_key_type, + (const u8 *)dev->dev_addr, 0, + (u32 *)(&priv->rtllib->swcamtable[4].key_buf[0])); + } else { + rtl92e_set_key(dev, 4, 0, + priv->rtllib->pairwise_key_type, + MacAddr, 0, + (u32 *)(&priv->rtllib->swcamtable[4].key_buf[0])); + } + + } else if (priv->rtllib->pairwise_key_type == KEY_TYPE_CCMP) { + if (priv->rtllib->iw_mode == IW_MODE_ADHOC) { + rtl92e_set_key(dev, 4, 0, + priv->rtllib->pairwise_key_type, + (const u8 *)dev->dev_addr, 0, + (u32 *)(&priv->rtllib->swcamtable[4].key_buf[0])); + } else { + rtl92e_set_key(dev, 4, 0, + priv->rtllib->pairwise_key_type, MacAddr, + 0, (u32 *)(&priv->rtllib->swcamtable[4].key_buf[0])); + } + } + + if (priv->rtllib->group_key_type == KEY_TYPE_TKIP) { + MacAddr = CAM_CONST_BROAD; + for (EntryId = 1; EntryId < 4; EntryId++) { + if (priv->rtllib->swcamtable[EntryId].bused) { + rtl92e_set_key(dev, EntryId, EntryId, + priv->rtllib->group_key_type, + MacAddr, 0, + (u32 *)(&priv->rtllib->swcamtable[EntryId].key_buf[0])); + } + } + if (priv->rtllib->iw_mode == IW_MODE_ADHOC) { + if (priv->rtllib->swcamtable[0].bused) { + rtl92e_set_key(dev, 0, 0, + priv->rtllib->group_key_type, + CAM_CONST_ADDR[0], 0, + (u32 *)(&priv->rtllib->swcamtable[0].key_buf[0])); + } else { + netdev_warn(dev, + "%s(): ADHOC TKIP: missing key entry.\n", + __func__); + return; + } + } + } else if (priv->rtllib->group_key_type == KEY_TYPE_CCMP) { + MacAddr = CAM_CONST_BROAD; + for (EntryId = 1; EntryId < 4; EntryId++) { + if (priv->rtllib->swcamtable[EntryId].bused) { + rtl92e_set_key(dev, EntryId, EntryId, + priv->rtllib->group_key_type, + MacAddr, 0, + (u32 *)(&priv->rtllib->swcamtable[EntryId].key_buf[0])); + } + } + + if (priv->rtllib->iw_mode == IW_MODE_ADHOC) { + if (priv->rtllib->swcamtable[0].bused) { + rtl92e_set_key(dev, 0, 0, + priv->rtllib->group_key_type, + CAM_CONST_ADDR[0], 0, + (u32 *)(&priv->rtllib->swcamtable[0].key_buf[0])); + } else { + netdev_warn(dev, + "%s(): ADHOC CCMP: missing key entry.\n", + __func__); + return; + } + } + } +} diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_cam.h b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.h new file mode 100644 index 0000000000..bd33ef1051 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_cam.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _RTL_CAM_H +#define _RTL_CAM_H + +#include <linux/types.h> +struct net_device; + +void rtl92e_cam_reset(struct net_device *dev); +void rtl92e_enable_hw_security_config(struct net_device *dev); +void rtl92e_set_key(struct net_device *dev, u8 EntryNo, u8 KeyIndex, + u16 KeyType, const u8 *MacAddr, u8 DefaultKey, + u32 *KeyContent); +void rtl92e_set_swcam(struct net_device *dev, u8 EntryNo, u8 KeyIndex, + u16 KeyType, const u8 *MacAddr, u32 *KeyContent); +void rtl92e_cam_restore(struct net_device *dev); + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c new file mode 100644 index 0000000000..50eb8f3494 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c @@ -0,0 +1,2277 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include <linux/uaccess.h> +#include <linux/pci.h> +#include <linux/vmalloc.h> +#include <linux/ieee80211.h> +#include "rtl_core.h" +#include "r8192E_phy.h" +#include "r8192E_phyreg.h" +#include "r8190P_rtl8256.h" +#include "r8192E_cmdpkt.h" + +#include "rtl_wx.h" +#include "rtl_dm.h" + +#include "rtl_pm.h" + +int hwwep = 1; +static char *ifname = "wlan%d"; + +static struct pci_device_id rtl8192_pci_id_tbl[] = { + {PCI_DEVICE(0x10ec, 0x8192)}, + {PCI_DEVICE(0x07aa, 0x0044)}, + {PCI_DEVICE(0x07aa, 0x0047)}, + {} +}; + +MODULE_DEVICE_TABLE(pci, rtl8192_pci_id_tbl); + +static int _rtl92e_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id); +static void _rtl92e_pci_disconnect(struct pci_dev *pdev); +static irqreturn_t _rtl92e_irq(int irq, void *netdev); + +static SIMPLE_DEV_PM_OPS(rtl92e_pm_ops, rtl92e_suspend, rtl92e_resume); + +static struct pci_driver rtl8192_pci_driver = { + .name = DRV_NAME, /* Driver name */ + .id_table = rtl8192_pci_id_tbl, /* PCI_ID table */ + .probe = _rtl92e_pci_probe, /* probe fn */ + .remove = _rtl92e_pci_disconnect, /* remove fn */ + .driver.pm = &rtl92e_pm_ops, +}; + +static short _rtl92e_is_tx_queue_empty(struct net_device *dev); +static void _rtl92e_watchdog_wq_cb(void *data); +static void _rtl92e_watchdog_timer_cb(struct timer_list *t); +static void _rtl92e_hard_data_xmit(struct sk_buff *skb, struct net_device *dev, + int rate); +static int _rtl92e_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void _rtl92e_tx_cmd(struct net_device *dev, struct sk_buff *skb); +static short _rtl92e_tx(struct net_device *dev, struct sk_buff *skb); +static short _rtl92e_pci_initdescring(struct net_device *dev); +static void _rtl92e_irq_tx_tasklet(struct tasklet_struct *t); +static void _rtl92e_irq_rx_tasklet(struct tasklet_struct *t); +static void _rtl92e_cancel_deferred_work(struct r8192_priv *priv); +static int _rtl92e_up(struct net_device *dev, bool is_silent_reset); +static int _rtl92e_try_up(struct net_device *dev); +static int _rtl92e_down(struct net_device *dev, bool shutdownrf); +static void _rtl92e_restart(void *data); + +/**************************************************************************** + * -----------------------------IO STUFF------------------------- + ****************************************************************************/ + +u8 rtl92e_readb(struct net_device *dev, int x) +{ + return 0xff & readb((u8 __iomem *)dev->mem_start + x); +} + +u32 rtl92e_readl(struct net_device *dev, int x) +{ + return readl((u8 __iomem *)dev->mem_start + x); +} + +u16 rtl92e_readw(struct net_device *dev, int x) +{ + return readw((u8 __iomem *)dev->mem_start + x); +} + +void rtl92e_writeb(struct net_device *dev, int x, u8 y) +{ + writeb(y, (u8 __iomem *)dev->mem_start + x); + + udelay(20); +} + +void rtl92e_writel(struct net_device *dev, int x, u32 y) +{ + writel(y, (u8 __iomem *)dev->mem_start + x); + + udelay(20); +} + +void rtl92e_writew(struct net_device *dev, int x, u16 y) +{ + writew(y, (u8 __iomem *)dev->mem_start + x); + + udelay(20); +} + +/**************************************************************************** + * -----------------------------GENERAL FUNCTION------------------------- + ****************************************************************************/ +bool rtl92e_set_rf_state(struct net_device *dev, + enum rt_rf_power_state state_to_set, + RT_RF_CHANGE_SOURCE change_source) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + bool action_allowed = false; + bool connect_by_ssid = false; + enum rt_rf_power_state rt_state; + u16 rf_wait_counter = 0; + unsigned long flag; + + while (true) { + spin_lock_irqsave(&priv->rf_ps_lock, flag); + if (priv->rf_change_in_progress) { + spin_unlock_irqrestore(&priv->rf_ps_lock, flag); + + while (priv->rf_change_in_progress) { + rf_wait_counter++; + mdelay(1); + + if (rf_wait_counter > 100) { + netdev_warn(dev, + "%s(): Timeout waiting for RF change.\n", + __func__); + return false; + } + } + } else { + priv->rf_change_in_progress = true; + spin_unlock_irqrestore(&priv->rf_ps_lock, flag); + break; + } + } + + rt_state = priv->rtllib->rf_power_state; + + switch (state_to_set) { + case rf_on: + priv->rtllib->rf_off_reason &= (~change_source); + + if ((change_source == RF_CHANGE_BY_HW) && priv->hw_radio_off) + priv->hw_radio_off = false; + + if (!priv->rtllib->rf_off_reason) { + priv->rtllib->rf_off_reason = 0; + action_allowed = true; + + if (rt_state == rf_off && + change_source >= RF_CHANGE_BY_HW) + connect_by_ssid = true; + } + break; + + case rf_off: + + if ((priv->rtllib->iw_mode == IW_MODE_INFRA) || + (priv->rtllib->iw_mode == IW_MODE_ADHOC)) { + if ((priv->rtllib->rf_off_reason > RF_CHANGE_BY_IPS) || + (change_source > RF_CHANGE_BY_IPS)) { + if (ieee->link_state == MAC80211_LINKED) + priv->blinked_ingpio = true; + else + priv->blinked_ingpio = false; + rtllib_MgntDisconnect(priv->rtllib, + WLAN_REASON_DISASSOC_STA_HAS_LEFT); + } + } + if ((change_source == RF_CHANGE_BY_HW) && !priv->hw_radio_off) + priv->hw_radio_off = true; + priv->rtllib->rf_off_reason |= change_source; + action_allowed = true; + break; + + case rf_sleep: + priv->rtllib->rf_off_reason |= change_source; + action_allowed = true; + break; + + default: + break; + } + + if (action_allowed) { + rtl92e_set_rf_power_state(dev, state_to_set); + if (state_to_set == rf_on) { + if (connect_by_ssid && priv->blinked_ingpio) { + schedule_delayed_work( + &ieee->associate_procedure_wq, 0); + priv->blinked_ingpio = false; + } + } + } + + spin_lock_irqsave(&priv->rf_ps_lock, flag); + priv->rf_change_in_progress = false; + spin_unlock_irqrestore(&priv->rf_ps_lock, flag); + return action_allowed; +} + +static short _rtl92e_check_nic_enough_desc(struct net_device *dev, int prio) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtl8192_tx_ring *ring = &priv->tx_ring[prio]; + + if (ring->entries - skb_queue_len(&ring->queue) >= 2) + return 1; + return 0; +} + +static void _rtl92e_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + schedule_work(&priv->reset_wq); + netdev_info(dev, "TXTIMEOUT"); +} + +void rtl92e_irq_enable(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->irq_enabled = 1; + + rtl92e_enable_irq(dev); +} + +void rtl92e_irq_disable(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + rtl92e_disable_irq(dev); + + priv->irq_enabled = 0; +} + +static void _rtl92e_set_chan(struct net_device *dev, short ch) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->chan = ch; + + if (priv->rf_set_chan) + priv->rf_set_chan(dev, priv->chan); +} + +static void _rtl92e_update_cap(struct net_device *dev, u16 cap) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_network *net = &priv->rtllib->current_network; + bool ShortPreamble; + + if (cap & WLAN_CAPABILITY_SHORT_PREAMBLE) { + if (priv->dot11_current_preamble_mode != PREAMBLE_SHORT) { + ShortPreamble = true; + priv->dot11_current_preamble_mode = PREAMBLE_SHORT; + priv->rtllib->SetHwRegHandler(dev, HW_VAR_ACK_PREAMBLE, + (unsigned char *)&ShortPreamble); + } + } else { + if (priv->dot11_current_preamble_mode != PREAMBLE_LONG) { + ShortPreamble = false; + priv->dot11_current_preamble_mode = PREAMBLE_LONG; + priv->rtllib->SetHwRegHandler(dev, HW_VAR_ACK_PREAMBLE, + (unsigned char *)&ShortPreamble); + } + } + + if (net->mode & (WIRELESS_MODE_G | WIRELESS_MODE_N_24G)) { + u8 slot_time_val; + u8 cur_slot_time = priv->slot_time; + + if ((cap & WLAN_CAPABILITY_SHORT_SLOT_TIME) && + (!priv->rtllib->ht_info->current_rt2rt_long_slot_time)) { + if (cur_slot_time != SHORT_SLOT_TIME) { + slot_time_val = SHORT_SLOT_TIME; + priv->rtllib->SetHwRegHandler(dev, + HW_VAR_SLOT_TIME, &slot_time_val); + } + } else { + if (cur_slot_time != NON_SHORT_SLOT_TIME) { + slot_time_val = NON_SHORT_SLOT_TIME; + priv->rtllib->SetHwRegHandler(dev, + HW_VAR_SLOT_TIME, &slot_time_val); + } + } + } +} + +static const struct rtllib_qos_parameters def_qos_parameters = { + {cpu_to_le16(3), cpu_to_le16(3), cpu_to_le16(3), cpu_to_le16(3)}, + {cpu_to_le16(7), cpu_to_le16(7), cpu_to_le16(7), cpu_to_le16(7)}, + {2, 2, 2, 2}, + {0, 0, 0, 0}, + {0, 0, 0, 0} +}; + +static void _rtl92e_update_beacon(void *data) +{ + struct r8192_priv *priv = container_of(data, struct r8192_priv, update_beacon_wq.work); + struct net_device *dev = priv->rtllib->dev; + struct rtllib_device *ieee = priv->rtllib; + struct rtllib_network *net = &ieee->current_network; + + if (ieee->ht_info->bCurrentHTSupport) + HT_update_self_and_peer_setting(ieee, net); + ieee->ht_info->current_rt2rt_long_slot_time = net->bssht.bd_rt2rt_long_slot_time; + ieee->ht_info->RT2RT_HT_Mode = net->bssht.rt2rt_ht_mode; + _rtl92e_update_cap(dev, net->capability); +} + +static void _rtl92e_qos_activate(void *data) +{ + struct r8192_priv *priv = container_of(data, struct r8192_priv, qos_activate); + struct net_device *dev = priv->rtllib->dev; + int i; + + mutex_lock(&priv->mutex); + if (priv->rtllib->link_state != MAC80211_LINKED) + goto success; + + for (i = 0; i < QOS_QUEUE_NUM; i++) + priv->rtllib->SetHwRegHandler(dev, HW_VAR_AC_PARAM, (u8 *)(&i)); + +success: + mutex_unlock(&priv->mutex); +} + +static int _rtl92e_qos_handle_probe_response(struct r8192_priv *priv, + int active_network, + struct rtllib_network *network) +{ + int ret = 0; + u32 size = sizeof(struct rtllib_qos_parameters); + + if (priv->rtllib->link_state != MAC80211_LINKED) + return ret; + + if (priv->rtllib->iw_mode != IW_MODE_INFRA) + return ret; + + if (network->flags & NETWORK_HAS_QOS_MASK) { + if (active_network && + (network->flags & NETWORK_HAS_QOS_PARAMETERS)) + network->qos_data.active = network->qos_data.supported; + + if ((network->qos_data.active == 1) && (active_network == 1) && + (network->flags & NETWORK_HAS_QOS_PARAMETERS) && + (network->qos_data.old_param_count != + network->qos_data.param_count)) { + network->qos_data.old_param_count = + network->qos_data.param_count; + priv->rtllib->wmm_acm = network->qos_data.wmm_acm; + schedule_work(&priv->qos_activate); + } + } else { + memcpy(&priv->rtllib->current_network.qos_data.parameters, + &def_qos_parameters, size); + + if ((network->qos_data.active == 1) && (active_network == 1)) + schedule_work(&priv->qos_activate); + + network->qos_data.active = 0; + network->qos_data.supported = 0; + } + + return 0; +} + +static int _rtl92e_handle_beacon(struct net_device *dev, + struct rtllib_beacon *beacon, + struct rtllib_network *network) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + _rtl92e_qos_handle_probe_response(priv, 1, network); + + schedule_delayed_work(&priv->update_beacon_wq, 0); + return 0; +} + +static int _rtl92e_qos_assoc_resp(struct r8192_priv *priv, + struct rtllib_network *network) +{ + unsigned long flags; + u32 size = sizeof(struct rtllib_qos_parameters); + int set_qos_param = 0; + + if (!priv || !network) + return 0; + + if (priv->rtllib->link_state != MAC80211_LINKED) + return 0; + + if (priv->rtllib->iw_mode != IW_MODE_INFRA) + return 0; + + spin_lock_irqsave(&priv->rtllib->lock, flags); + if (network->flags & NETWORK_HAS_QOS_PARAMETERS) { + memcpy(&priv->rtllib->current_network.qos_data.parameters, + &network->qos_data.parameters, + sizeof(struct rtllib_qos_parameters)); + priv->rtllib->current_network.qos_data.active = 1; + priv->rtllib->wmm_acm = network->qos_data.wmm_acm; + set_qos_param = 1; + priv->rtllib->current_network.qos_data.old_param_count = + priv->rtllib->current_network.qos_data.param_count; + priv->rtllib->current_network.qos_data.param_count = + network->qos_data.param_count; + } else { + memcpy(&priv->rtllib->current_network.qos_data.parameters, + &def_qos_parameters, size); + priv->rtllib->current_network.qos_data.active = 0; + priv->rtllib->current_network.qos_data.supported = 0; + set_qos_param = 1; + } + + spin_unlock_irqrestore(&priv->rtllib->lock, flags); + + if (set_qos_param == 1) { + rtl92e_dm_init_edca_turbo(priv->rtllib->dev); + schedule_work(&priv->qos_activate); + } + return 0; +} + +static int _rtl92e_handle_assoc_response(struct net_device *dev, + struct rtllib_assoc_response_frame *resp, + struct rtllib_network *network) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + _rtl92e_qos_assoc_resp(priv, network); + return 0; +} + +static void _rtl92e_prepare_beacon(struct tasklet_struct *t) +{ + struct r8192_priv *priv = from_tasklet(priv, t, + irq_prepare_beacon_tasklet); + struct net_device *dev = priv->rtllib->dev; + struct sk_buff *pskb = NULL, *pnewskb = NULL; + struct cb_desc *tcb_desc = NULL; + struct rtl8192_tx_ring *ring = NULL; + struct tx_desc *pdesc = NULL; + + ring = &priv->tx_ring[BEACON_QUEUE]; + pskb = __skb_dequeue(&ring->queue); + kfree_skb(pskb); + + pnewskb = rtllib_get_beacon(priv->rtllib); + if (!pnewskb) + return; + + tcb_desc = (struct cb_desc *)(pnewskb->cb + 8); + tcb_desc->queue_index = BEACON_QUEUE; + tcb_desc->data_rate = 2; + tcb_desc->ratr_index = 7; + tcb_desc->tx_dis_rate_fallback = 1; + tcb_desc->tx_use_drv_assinged_rate = 1; + skb_push(pnewskb, priv->rtllib->tx_headroom); + + pdesc = &ring->desc[0]; + rtl92e_fill_tx_desc(dev, pdesc, tcb_desc, pnewskb); + __skb_queue_tail(&ring->queue, pnewskb); + pdesc->OWN = 1; +} + +static void _rtl92e_stop_beacon(struct net_device *dev) +{ +} + +void rtl92e_config_rate(struct net_device *dev, u16 *rate_config) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_network *net; + u8 i = 0, basic_rate = 0; + + net = &priv->rtllib->current_network; + + for (i = 0; i < net->rates_len; i++) { + basic_rate = net->rates[i] & 0x7f; + switch (basic_rate) { + case MGN_1M: + *rate_config |= RRSR_1M; + break; + case MGN_2M: + *rate_config |= RRSR_2M; + break; + case MGN_5_5M: + *rate_config |= RRSR_5_5M; + break; + case MGN_11M: + *rate_config |= RRSR_11M; + break; + case MGN_6M: + *rate_config |= RRSR_6M; + break; + case MGN_9M: + *rate_config |= RRSR_9M; + break; + case MGN_12M: + *rate_config |= RRSR_12M; + break; + case MGN_18M: + *rate_config |= RRSR_18M; + break; + case MGN_24M: + *rate_config |= RRSR_24M; + break; + case MGN_36M: + *rate_config |= RRSR_36M; + break; + case MGN_48M: + *rate_config |= RRSR_48M; + break; + case MGN_54M: + *rate_config |= RRSR_54M; + break; + } + } + + for (i = 0; i < net->rates_ex_len; i++) { + basic_rate = net->rates_ex[i] & 0x7f; + switch (basic_rate) { + case MGN_1M: + *rate_config |= RRSR_1M; + break; + case MGN_2M: + *rate_config |= RRSR_2M; + break; + case MGN_5_5M: + *rate_config |= RRSR_5_5M; + break; + case MGN_11M: + *rate_config |= RRSR_11M; + break; + case MGN_6M: + *rate_config |= RRSR_6M; + break; + case MGN_9M: + *rate_config |= RRSR_9M; + break; + case MGN_12M: + *rate_config |= RRSR_12M; + break; + case MGN_18M: + *rate_config |= RRSR_18M; + break; + case MGN_24M: + *rate_config |= RRSR_24M; + break; + case MGN_36M: + *rate_config |= RRSR_36M; + break; + case MGN_48M: + *rate_config |= RRSR_48M; + break; + case MGN_54M: + *rate_config |= RRSR_54M; + break; + } + } +} + +static void _rtl92e_refresh_support_rate(struct r8192_priv *priv) +{ + struct rtllib_device *ieee = priv->rtllib; + + if (ieee->mode == WIRELESS_MODE_N_24G) { + memcpy(ieee->reg_dot11ht_oper_rate_set, + ieee->reg_ht_supp_rate_set, 16); + memcpy(ieee->reg_dot11tx_ht_oper_rate_set, + ieee->reg_ht_supp_rate_set, 16); + + } else { + memset(ieee->reg_dot11ht_oper_rate_set, 0, 16); + } +} + +void rtl92e_set_wireless_mode(struct net_device *dev, u8 wireless_mode) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 support_mode = (WIRELESS_MODE_N_24G | WIRELESS_MODE_G | WIRELESS_MODE_B); + + if ((wireless_mode == WIRELESS_MODE_AUTO) || ((wireless_mode & support_mode) == 0)) + wireless_mode = WIRELESS_MODE_N_24G; + + if ((wireless_mode & (WIRELESS_MODE_B | WIRELESS_MODE_G)) == + (WIRELESS_MODE_G | WIRELESS_MODE_B)) + wireless_mode = WIRELESS_MODE_G; + + priv->rtllib->mode = wireless_mode; + + if (wireless_mode == WIRELESS_MODE_N_24G) + priv->rtllib->ht_info->enable_ht = 1; + else + priv->rtllib->ht_info->enable_ht = 0; + + _rtl92e_refresh_support_rate(priv); +} + +static int _rtl92e_sta_up(struct net_device *dev, bool is_silent_reset) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + (&priv->rtllib->pwr_save_ctrl); + bool init_status; + + priv->up = 1; + priv->rtllib->ieee_up = 1; + + priv->up_first_time = 0; + init_status = rtl92e_start_adapter(dev); + if (!init_status) { + netdev_err(dev, "%s(): Initialization failed!\n", __func__); + return -1; + } + + RT_CLEAR_PS_LEVEL(psc, RT_RF_OFF_LEVL_HALT_NIC); + + if (priv->polling_timer_on == 0) + rtl92e_check_rfctrl_gpio_timer(&priv->gpio_polling_timer); + + if (priv->rtllib->link_state != MAC80211_LINKED) + rtllib_softmac_start_protocol(priv->rtllib, 0); + rtllib_reset_queue(priv->rtllib); + _rtl92e_watchdog_timer_cb(&priv->watch_dog_timer); + + if (!netif_queue_stopped(dev)) + netif_start_queue(dev); + else + netif_wake_queue(dev); + + priv->bfirst_after_down = false; + return 0; +} + +static int _rtl92e_sta_down(struct net_device *dev, bool shutdownrf) +{ + struct r8192_priv *priv = rtllib_priv(dev); + unsigned long flags = 0; + u8 rf_in_progress_timeout = 0; + + if (priv->up == 0) + return -1; + + priv->rtllib->rtllib_ips_leave(dev); + + if (priv->rtllib->link_state == MAC80211_LINKED) + rtl92e_leisure_ps_leave(dev); + + priv->up = 0; + priv->rtllib->ieee_up = 0; + priv->bfirst_after_down = true; + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + + priv->rtllib->wpa_ie_len = 0; + kfree(priv->rtllib->wpa_ie); + priv->rtllib->wpa_ie = NULL; + rtl92e_cam_reset(dev); + memset(priv->rtllib->swcamtable, 0, sizeof(struct sw_cam_table) * 32); + rtl92e_irq_disable(dev); + + del_timer_sync(&priv->watch_dog_timer); + _rtl92e_cancel_deferred_work(priv); + cancel_delayed_work(&priv->rtllib->hw_wakeup_wq); + + rtllib_softmac_stop_protocol(priv->rtllib, 0, true); + spin_lock_irqsave(&priv->rf_ps_lock, flags); + while (priv->rf_change_in_progress) { + spin_unlock_irqrestore(&priv->rf_ps_lock, flags); + if (rf_in_progress_timeout > 100) { + spin_lock_irqsave(&priv->rf_ps_lock, flags); + break; + } + mdelay(1); + rf_in_progress_timeout++; + spin_lock_irqsave(&priv->rf_ps_lock, flags); + } + priv->rf_change_in_progress = true; + spin_unlock_irqrestore(&priv->rf_ps_lock, flags); + rtl92e_stop_adapter(dev, false); + spin_lock_irqsave(&priv->rf_ps_lock, flags); + priv->rf_change_in_progress = false; + spin_unlock_irqrestore(&priv->rf_ps_lock, flags); + udelay(100); + memset(&priv->rtllib->current_network, 0, + offsetof(struct rtllib_network, list)); + + return 0; +} + +static void _rtl92e_init_priv_handler(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->rtllib->softmac_hard_start_xmit = _rtl92e_hard_start_xmit; + priv->rtllib->set_chan = _rtl92e_set_chan; + priv->rtllib->link_change = rtl92e_link_change; + priv->rtllib->softmac_data_hard_start_xmit = _rtl92e_hard_data_xmit; + priv->rtllib->check_nic_enough_desc = _rtl92e_check_nic_enough_desc; + priv->rtllib->handle_assoc_response = _rtl92e_handle_assoc_response; + priv->rtllib->handle_beacon = _rtl92e_handle_beacon; + priv->rtllib->set_wireless_mode = rtl92e_set_wireless_mode; + priv->rtllib->leisure_ps_leave = rtl92e_leisure_ps_leave; + priv->rtllib->set_bw_mode_handler = rtl92e_set_bw_mode; + priv->rf_set_chan = rtl92e_set_channel; + + priv->rtllib->start_send_beacons = rtl92e_start_beacon; + priv->rtllib->stop_send_beacons = _rtl92e_stop_beacon; + + priv->rtllib->sta_wake_up = rtl92e_hw_wakeup; + priv->rtllib->enter_sleep_state = rtl92e_enter_sleep; + priv->rtllib->ps_is_queue_empty = _rtl92e_is_tx_queue_empty; + + priv->rtllib->GetNmodeSupportBySecCfg = rtl92e_get_nmode_support_by_sec; + priv->rtllib->GetHalfNmodeSupportByAPsHandler = + rtl92e_is_halfn_supported_by_ap; + + priv->rtllib->SetHwRegHandler = rtl92e_set_reg; + priv->rtllib->AllowAllDestAddrHandler = rtl92e_set_monitor_mode; + priv->rtllib->init_gain_handler = rtl92e_init_gain; + priv->rtllib->rtllib_ips_leave_wq = rtl92e_rtllib_ips_leave_wq; + priv->rtllib->rtllib_ips_leave = rtl92e_rtllib_ips_leave; + priv->rtllib->ScanOperationBackupHandler = rtl92e_scan_op_backup; +} + +static void _rtl92e_init_priv_constant(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + &priv->rtllib->pwr_save_ctrl; + + psc->reg_max_lps_awake_intvl = 5; +} + +static void _rtl92e_init_priv_variable(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 i; + + priv->dot11_current_preamble_mode = PREAMBLE_AUTO; + priv->rtllib->status = 0; + priv->polling_timer_on = 0; + priv->up_first_time = 1; + priv->blinked_ingpio = false; + priv->being_init_adapter = false; + priv->txringcount = 64; + priv->rxbuffersize = 9100; + priv->rxringcount = MAX_RX_COUNT; + priv->irq_enabled = 0; + priv->chan = 1; + priv->rtllib->mode = WIRELESS_MODE_AUTO; + priv->rtllib->iw_mode = IW_MODE_INFRA; + priv->rtllib->net_promiscuous_md = false; + priv->rtllib->intel_promiscuous_md_info.promiscuous_on = false; + priv->rtllib->intel_promiscuous_md_info.fltr_src_sta_frame = + false; + priv->rtllib->ieee_up = 0; + priv->retry_rts = DEFAULT_RETRY_RTS; + priv->retry_data = DEFAULT_RETRY_DATA; + priv->rtllib->rts = DEFAULT_RTS_THRESHOLD; + priv->rtllib->rate = 110; + priv->promisc = (dev->flags & IFF_PROMISC) ? 1 : 0; + priv->bcck_in_ch14 = false; + priv->cck_present_attn = 0; + priv->rfa_txpowertrackingindex = 0; + priv->rfc_txpowertrackingindex = 0; + priv->cck_pwr_enl = 6; + priv->rst_progress = RESET_TYPE_NORESET; + priv->force_reset = false; + memset(priv->rtllib->swcamtable, 0, sizeof(struct sw_cam_table) * 32); + priv->rx_ctr = 0; + priv->rtllib->wx_set_enc = 0; + priv->hw_radio_off = false; + priv->rtllib->rf_off_reason = 0; + priv->rf_change_in_progress = false; + priv->hw_rf_off_action = 0; + priv->set_rf_pwr_state_in_progress = false; + priv->rtllib->pwr_save_ctrl.bLeisurePs = true; + priv->rtllib->LPSDelayCnt = 0; + priv->rtllib->sta_sleep = LPS_IS_WAKE; + priv->rtllib->rf_power_state = rf_on; + + priv->rtllib->current_network.beacon_interval = DEFAULT_BEACONINTERVAL; + priv->rtllib->iw_mode = IW_MODE_INFRA; + priv->rtllib->active_scan = 1; + priv->rtllib->be_scan_inprogress = false; + + priv->rtllib->fts = DEFAULT_FRAG_THRESHOLD; + + priv->fw_info = vzalloc(sizeof(struct rt_firmware)); + if (!priv->fw_info) + netdev_err(dev, + "rtl8192e: Unable to allocate space for firmware\n"); + + skb_queue_head_init(&priv->skb_queue); + + for (i = 0; i < MAX_QUEUE_SIZE; i++) + skb_queue_head_init(&priv->rtllib->skb_waitQ[i]); +} + +static void _rtl92e_init_priv_lock(struct r8192_priv *priv) +{ + spin_lock_init(&priv->tx_lock); + spin_lock_init(&priv->irq_th_lock); + spin_lock_init(&priv->rf_ps_lock); + spin_lock_init(&priv->ps_lock); + mutex_init(&priv->wx_mutex); + mutex_init(&priv->rf_mutex); + mutex_init(&priv->mutex); +} + +static void _rtl92e_init_priv_task(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + INIT_WORK(&priv->reset_wq, (void *)_rtl92e_restart); + INIT_WORK(&priv->rtllib->ips_leave_wq, (void *)rtl92e_ips_leave_wq); + INIT_DELAYED_WORK(&priv->watch_dog_wq, (void *)_rtl92e_watchdog_wq_cb); + INIT_DELAYED_WORK(&priv->txpower_tracking_wq, (void *)rtl92e_dm_txpower_tracking_wq); + INIT_DELAYED_WORK(&priv->rfpath_check_wq, (void *)rtl92e_dm_rf_pathcheck_wq); + INIT_DELAYED_WORK(&priv->update_beacon_wq, (void *)_rtl92e_update_beacon); + INIT_WORK(&priv->qos_activate, (void *)_rtl92e_qos_activate); + INIT_DELAYED_WORK(&priv->rtllib->hw_wakeup_wq, (void *)rtl92e_hw_wakeup_wq); + INIT_DELAYED_WORK(&priv->rtllib->hw_sleep_wq, (void *)rtl92e_hw_sleep_wq); + tasklet_setup(&priv->irq_rx_tasklet, _rtl92e_irq_rx_tasklet); + tasklet_setup(&priv->irq_tx_tasklet, _rtl92e_irq_tx_tasklet); + tasklet_setup(&priv->irq_prepare_beacon_tasklet, + _rtl92e_prepare_beacon); +} + +static short _rtl92e_get_channel_map(struct net_device *dev) +{ + int i; + + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->chnl_plan >= COUNTRY_CODE_MAX) { + netdev_info(dev, + "rtl819x_init:Error channel plan! Set to default.\n"); + priv->chnl_plan = COUNTRY_CODE_FCC; + } + dot11d_init(priv->rtllib); + dot11d_channel_map(priv->chnl_plan, priv->rtllib); + for (i = 1; i <= 11; i++) + (priv->rtllib->active_channel_map)[i] = 1; + (priv->rtllib->active_channel_map)[12] = 2; + (priv->rtllib->active_channel_map)[13] = 2; + + return 0; +} + +static short _rtl92e_init(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + memset(&priv->stats, 0, sizeof(struct rt_stats)); + + _rtl92e_init_priv_handler(dev); + _rtl92e_init_priv_constant(dev); + _rtl92e_init_priv_variable(dev); + _rtl92e_init_priv_lock(priv); + _rtl92e_init_priv_task(dev); + rtl92e_get_eeprom_size(dev); + rtl92e_init_variables(dev); + _rtl92e_get_channel_map(dev); + + rtl92e_dm_init(dev); + + timer_setup(&priv->watch_dog_timer, _rtl92e_watchdog_timer_cb, 0); + + timer_setup(&priv->gpio_polling_timer, rtl92e_check_rfctrl_gpio_timer, + 0); + + rtl92e_irq_disable(dev); + if (request_irq(dev->irq, _rtl92e_irq, IRQF_SHARED, dev->name, dev)) { + netdev_err(dev, "Error allocating IRQ %d", dev->irq); + return -1; + } + + priv->irq = dev->irq; + + if (_rtl92e_pci_initdescring(dev) != 0) { + netdev_err(dev, "Endopoints initialization failed"); + free_irq(dev->irq, dev); + return -1; + } + + return 0; +} + +/*************************************************************************** + * -------------------------------WATCHDOG STUFF--------------------------- + **************************************************************************/ +static short _rtl92e_is_tx_queue_empty(struct net_device *dev) +{ + int i = 0; + struct r8192_priv *priv = rtllib_priv(dev); + + for (i = 0; i <= MGNT_QUEUE; i++) { + if ((i == TXCMD_QUEUE) || (i == HCCA_QUEUE)) + continue; + if (skb_queue_len(&(&priv->tx_ring[i])->queue) > 0) { + netdev_info(dev, "===>tx queue is not empty:%d, %d\n", + i, skb_queue_len(&(&priv->tx_ring[i])->queue)); + return 0; + } + } + return 1; +} + +static enum reset_type _rtl92e_tx_check_stuck(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 QueueID; + bool bCheckFwTxCnt = false; + struct rtl8192_tx_ring *ring = NULL; + struct sk_buff *skb = NULL; + struct cb_desc *tcb_desc = NULL; + unsigned long flags = 0; + + switch (priv->rtllib->ps) { + case RTLLIB_PS_DISABLED: + break; + case (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST): + break; + default: + break; + } + spin_lock_irqsave(&priv->irq_th_lock, flags); + for (QueueID = 0; QueueID < MAX_TX_QUEUE; QueueID++) { + if (QueueID == TXCMD_QUEUE) + continue; + + if (QueueID == BEACON_QUEUE) + continue; + + ring = &priv->tx_ring[QueueID]; + + if (skb_queue_len(&ring->queue) == 0) { + continue; + } else { + skb = __skb_peek(&ring->queue); + tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + tcb_desc->nStuckCount++; + bCheckFwTxCnt = true; + if (tcb_desc->nStuckCount > 1) + netdev_info(dev, + "%s: QueueID=%d tcb_desc->nStuckCount=%d\n", + __func__, QueueID, + tcb_desc->nStuckCount); + } + } + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + + if (bCheckFwTxCnt) { + if (rtl92e_is_tx_stuck(dev)) + return RESET_TYPE_SILENT; + } + + return RESET_TYPE_NORESET; +} + +static enum reset_type _rtl92e_rx_check_stuck(struct net_device *dev) +{ + if (rtl92e_is_rx_stuck(dev)) + return RESET_TYPE_SILENT; + + return RESET_TYPE_NORESET; +} + +static enum reset_type _rtl92e_if_check_reset(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + enum reset_type TxResetType = RESET_TYPE_NORESET; + enum reset_type RxResetType = RESET_TYPE_NORESET; + enum rt_rf_power_state rfState; + + rfState = priv->rtllib->rf_power_state; + + if (rfState == rf_on) + TxResetType = _rtl92e_tx_check_stuck(dev); + + if (rfState == rf_on && + (priv->rtllib->iw_mode == IW_MODE_INFRA) && + (priv->rtllib->link_state == MAC80211_LINKED)) + RxResetType = _rtl92e_rx_check_stuck(dev); + + if (TxResetType == RESET_TYPE_NORMAL || + RxResetType == RESET_TYPE_NORMAL) { + netdev_info(dev, "%s(): TxResetType is %d, RxResetType is %d\n", + __func__, TxResetType, RxResetType); + return RESET_TYPE_NORMAL; + } else if (TxResetType == RESET_TYPE_SILENT || + RxResetType == RESET_TYPE_SILENT) { + netdev_info(dev, "%s(): TxResetType is %d, RxResetType is %d\n", + __func__, TxResetType, RxResetType); + return RESET_TYPE_SILENT; + } else { + return RESET_TYPE_NORESET; + } +} + +static void _rtl92e_if_silent_reset(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 reset_times = 0; + int reset_status = 0; + struct rtllib_device *ieee = priv->rtllib; + unsigned long flag; + + if (priv->rst_progress == RESET_TYPE_NORESET) { + priv->rst_progress = RESET_TYPE_SILENT; + + spin_lock_irqsave(&priv->rf_ps_lock, flag); + if (priv->rf_change_in_progress) { + spin_unlock_irqrestore(&priv->rf_ps_lock, flag); + goto END; + } + priv->rf_change_in_progress = true; + priv->reset_in_progress = true; + spin_unlock_irqrestore(&priv->rf_ps_lock, flag); + +RESET_START: + + mutex_lock(&priv->wx_mutex); + + if (priv->rtllib->link_state == MAC80211_LINKED) + rtl92e_leisure_ps_leave(dev); + + if (priv->up) { + netdev_info(dev, "%s():the driver is not up.\n", + __func__); + mutex_unlock(&priv->wx_mutex); + return; + } + priv->up = 0; + + mdelay(1000); + + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); + + rtl92e_irq_disable(dev); + del_timer_sync(&priv->watch_dog_timer); + _rtl92e_cancel_deferred_work(priv); + rtl92e_dm_deinit(dev); + rtllib_stop_scan_syncro(ieee); + + if (ieee->link_state == MAC80211_LINKED) { + mutex_lock(&ieee->wx_mutex); + netdev_info(dev, "ieee->link_state is MAC80211_LINKED\n"); + rtllib_stop_send_beacons(priv->rtllib); + del_timer_sync(&ieee->associate_timer); + cancel_delayed_work(&ieee->associate_retry_wq); + rtllib_stop_scan(ieee); + netif_carrier_off(dev); + mutex_unlock(&ieee->wx_mutex); + } else { + netdev_info(dev, "ieee->link_state is NOT LINKED\n"); + rtllib_softmac_stop_protocol(priv->rtllib, 0, true); + } + + rtl92e_dm_backup_state(dev); + + mutex_unlock(&priv->wx_mutex); + reset_status = _rtl92e_up(dev, true); + + if (reset_status == -1) { + if (reset_times < 3) { + reset_times++; + goto RESET_START; + } else { + netdev_warn(dev, "%s(): Reset Failed\n", + __func__); + } + } + + ieee->is_silent_reset = 1; + + spin_lock_irqsave(&priv->rf_ps_lock, flag); + priv->rf_change_in_progress = false; + spin_unlock_irqrestore(&priv->rf_ps_lock, flag); + + rtl92e_enable_hw_security_config(dev); + + if (ieee->link_state == MAC80211_LINKED && ieee->iw_mode == + IW_MODE_INFRA) { + ieee->set_chan(ieee->dev, + ieee->current_network.channel); + + schedule_work(&ieee->associate_complete_wq); + + } else if (ieee->link_state == MAC80211_LINKED && ieee->iw_mode == + IW_MODE_ADHOC) { + ieee->set_chan(ieee->dev, + ieee->current_network.channel); + ieee->link_change(ieee->dev); + + notify_wx_assoc_event(ieee); + + rtllib_start_send_beacons(ieee); + + netif_carrier_on(ieee->dev); + } + + rtl92e_cam_restore(dev); + rtl92e_dm_restore_state(dev); +END: + priv->rst_progress = RESET_TYPE_NORESET; + priv->reset_count++; + priv->reset_in_progress = false; + + rtl92e_writeb(dev, UFWP, 1); + } +} + +static void _rtl92e_update_rxcounts(struct r8192_priv *priv, u32 *TotalRxBcnNum, + u32 *TotalRxDataNum) +{ + u16 SlotIndex; + u8 i; + + *TotalRxBcnNum = 0; + *TotalRxDataNum = 0; + + SlotIndex = (priv->rtllib->link_detect_info.SlotIndex++) % + (priv->rtllib->link_detect_info.SlotNum); + priv->rtllib->link_detect_info.RxBcnNum[SlotIndex] = + priv->rtllib->link_detect_info.NumRecvBcnInPeriod; + priv->rtllib->link_detect_info.RxDataNum[SlotIndex] = + priv->rtllib->link_detect_info.NumRecvDataInPeriod; + for (i = 0; i < priv->rtllib->link_detect_info.SlotNum; i++) { + *TotalRxBcnNum += priv->rtllib->link_detect_info.RxBcnNum[i]; + *TotalRxDataNum += priv->rtllib->link_detect_info.RxDataNum[i]; + } +} + +static void _rtl92e_watchdog_wq_cb(void *data) +{ + struct r8192_priv *priv = container_of_dwork_rsl(data, + struct r8192_priv, watch_dog_wq); + struct net_device *dev = priv->rtllib->dev; + struct rtllib_device *ieee = priv->rtllib; + enum reset_type ResetType = RESET_TYPE_NORESET; + static u8 check_reset_cnt; + unsigned long flags; + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + (&priv->rtllib->pwr_save_ctrl); + bool bBusyTraffic = false; + bool bHigherBusyTraffic = false; + bool bHigherBusyRxTraffic = false; + bool bEnterPS = false; + + if (!priv->up || priv->hw_radio_off) + return; + + if (priv->rtllib->link_state >= MAC80211_LINKED) { + if (priv->rtllib->CntAfterLink < 2) + priv->rtllib->CntAfterLink++; + } else { + priv->rtllib->CntAfterLink = 0; + } + + rtl92e_dm_watchdog(dev); + + if (!rtllib_act_scanning(priv->rtllib, false)) { + if ((ieee->iw_mode == IW_MODE_INFRA) && (ieee->link_state == + MAC80211_NOLINK) && + (ieee->rf_power_state == rf_on) && !ieee->is_set_key && + (!ieee->proto_stoppping) && !ieee->wx_set_enc) { + if ((ieee->pwr_save_ctrl.ReturnPoint == + IPS_CALLBACK_NONE) && + (!ieee->net_promiscuous_md)) { + rtl92e_ips_enter(dev); + } + } + } + if ((ieee->link_state == MAC80211_LINKED) && (ieee->iw_mode == + IW_MODE_INFRA) && (!ieee->net_promiscuous_md)) { + if (ieee->link_detect_info.NumRxOkInPeriod > 100 || + ieee->link_detect_info.NumTxOkInPeriod > 100) + bBusyTraffic = true; + + if (ieee->link_detect_info.NumRxOkInPeriod > 4000 || + ieee->link_detect_info.NumTxOkInPeriod > 4000) { + bHigherBusyTraffic = true; + if (ieee->link_detect_info.NumRxOkInPeriod > 5000) + bHigherBusyRxTraffic = true; + else + bHigherBusyRxTraffic = false; + } + + if (((ieee->link_detect_info.NumRxUnicastOkInPeriod + + ieee->link_detect_info.NumTxOkInPeriod) > 8) || + (ieee->link_detect_info.NumRxUnicastOkInPeriod > 2)) + bEnterPS = false; + else + bEnterPS = true; + + if (ieee->current_network.beacon_interval < 95) + bEnterPS = false; + + if (bEnterPS) + rtl92e_leisure_ps_enter(dev); + else + rtl92e_leisure_ps_leave(dev); + + } else { + rtl92e_leisure_ps_leave(dev); + } + + ieee->link_detect_info.NumRxOkInPeriod = 0; + ieee->link_detect_info.NumTxOkInPeriod = 0; + ieee->link_detect_info.NumRxUnicastOkInPeriod = 0; + ieee->link_detect_info.bBusyTraffic = bBusyTraffic; + + ieee->link_detect_info.bHigherBusyTraffic = bHigherBusyTraffic; + ieee->link_detect_info.bHigherBusyRxTraffic = bHigherBusyRxTraffic; + + if (ieee->link_state == MAC80211_LINKED && ieee->iw_mode == IW_MODE_INFRA) { + u32 TotalRxBcnNum = 0; + u32 TotalRxDataNum = 0; + + _rtl92e_update_rxcounts(priv, &TotalRxBcnNum, &TotalRxDataNum); + + if ((TotalRxBcnNum + TotalRxDataNum) == 0) + priv->check_roaming_cnt++; + else + priv->check_roaming_cnt = 0; + + if (priv->check_roaming_cnt > 0) { + if (ieee->rf_power_state == rf_off) + netdev_info(dev, "%s(): RF is off\n", __func__); + + netdev_info(dev, + "===>%s(): AP is power off, chan:%d, connect another one\n", + __func__, priv->chan); + + ieee->link_state = RTLLIB_ASSOCIATING; + + RemovePeerTS(priv->rtllib, + priv->rtllib->current_network.bssid); + ieee->is_roaming = true; + ieee->is_set_key = false; + ieee->link_change(dev); + notify_wx_assoc_event(ieee); + + if (!(ieee->rtllib_ap_sec_type(ieee) & + (SEC_ALG_CCMP | SEC_ALG_TKIP))) + schedule_delayed_work( + &ieee->associate_procedure_wq, 0); + + priv->check_roaming_cnt = 0; + } + ieee->link_detect_info.NumRecvBcnInPeriod = 0; + ieee->link_detect_info.NumRecvDataInPeriod = 0; + } + + spin_lock_irqsave(&priv->tx_lock, flags); + if ((check_reset_cnt++ >= 3) && (!ieee->is_roaming) && + (!priv->rf_change_in_progress) && (!psc->bSwRfProcessing)) { + ResetType = _rtl92e_if_check_reset(dev); + check_reset_cnt = 3; + } + spin_unlock_irqrestore(&priv->tx_lock, flags); + + if (ResetType == RESET_TYPE_NORMAL) { + priv->rst_progress = RESET_TYPE_NORMAL; + return; + } + + if ((priv->force_reset || ResetType == RESET_TYPE_SILENT)) + _rtl92e_if_silent_reset(dev); + priv->force_reset = false; + priv->reset_in_progress = false; +} + +static void _rtl92e_watchdog_timer_cb(struct timer_list *t) +{ + struct r8192_priv *priv = from_timer(priv, t, watch_dog_timer); + + schedule_delayed_work(&priv->watch_dog_wq, 0); + mod_timer(&priv->watch_dog_timer, jiffies + + msecs_to_jiffies(RTLLIB_WATCH_DOG_TIME)); +} + +/**************************************************************************** + * ---------------------------- NIC TX/RX STUFF--------------------------- + ****************************************************************************/ +void rtl92e_rx_enable(struct net_device *dev) +{ + rtl92e_enable_rx(dev); +} + +void rtl92e_tx_enable(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + rtl92e_enable_tx(dev); + + rtllib_reset_queue(priv->rtllib); +} + +static void _rtl92e_free_rx_ring(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int i, rx_queue_idx; + + for (rx_queue_idx = 0; rx_queue_idx < MAX_RX_QUEUE; + rx_queue_idx++) { + for (i = 0; i < priv->rxringcount; i++) { + struct sk_buff *skb = priv->rx_buf[rx_queue_idx][i]; + + if (!skb) + continue; + + dma_unmap_single(&priv->pdev->dev, + *((dma_addr_t *)skb->cb), + priv->rxbuffersize, DMA_FROM_DEVICE); + kfree_skb(skb); + } + + dma_free_coherent(&priv->pdev->dev, + sizeof(*priv->rx_ring[rx_queue_idx]) * priv->rxringcount, + priv->rx_ring[rx_queue_idx], + priv->rx_ring_dma[rx_queue_idx]); + priv->rx_ring[rx_queue_idx] = NULL; + } +} + +static void _rtl92e_free_tx_ring(struct net_device *dev, unsigned int prio) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtl8192_tx_ring *ring = &priv->tx_ring[prio]; + + while (skb_queue_len(&ring->queue)) { + struct tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb = __skb_dequeue(&ring->queue); + + dma_unmap_single(&priv->pdev->dev, entry->TxBuffAddr, + skb->len, DMA_TO_DEVICE); + kfree_skb(skb); + ring->idx = (ring->idx + 1) % ring->entries; + } + + dma_free_coherent(&priv->pdev->dev, + sizeof(*ring->desc) * ring->entries, ring->desc, + ring->dma); + ring->desc = NULL; +} + +static void _rtl92e_hard_data_xmit(struct sk_buff *skb, struct net_device *dev, + int rate) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int ret; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + u8 queue_index = tcb_desc->queue_index; + + if ((priv->rtllib->rf_power_state == rf_off) || !priv->up || + priv->reset_in_progress) { + kfree_skb(skb); + return; + } + + if (queue_index == TXCMD_QUEUE) + netdev_warn(dev, "%s(): queue index == TXCMD_QUEUE\n", + __func__); + + memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev)); + skb_push(skb, priv->rtllib->tx_headroom); + ret = _rtl92e_tx(dev, skb); + + if (queue_index != MGNT_QUEUE) { + priv->rtllib->stats.tx_bytes += (skb->len - + priv->rtllib->tx_headroom); + priv->rtllib->stats.tx_packets++; + } + + if (ret != 0) + kfree_skb(skb); +} + +static int _rtl92e_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int ret; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + u8 queue_index = tcb_desc->queue_index; + + if (queue_index != TXCMD_QUEUE) { + if ((priv->rtllib->rf_power_state == rf_off) || + !priv->up || priv->reset_in_progress) { + kfree_skb(skb); + return 0; + } + } + + memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev)); + if (queue_index == TXCMD_QUEUE) { + _rtl92e_tx_cmd(dev, skb); + return 0; + } + + tcb_desc->ratr_index = 7; + tcb_desc->tx_dis_rate_fallback = 1; + tcb_desc->tx_use_drv_assinged_rate = 1; + tcb_desc->bTxEnableFwCalcDur = 1; + skb_push(skb, priv->rtllib->tx_headroom); + ret = _rtl92e_tx(dev, skb); + if (ret != 0) + kfree_skb(skb); + return ret; +} + +static void _rtl92e_tx_isr(struct net_device *dev, int prio) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + struct rtl8192_tx_ring *ring = &priv->tx_ring[prio]; + + while (skb_queue_len(&ring->queue)) { + struct tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb; + + if (prio != BEACON_QUEUE) { + if (entry->OWN) + return; + ring->idx = (ring->idx + 1) % ring->entries; + } + + skb = __skb_dequeue(&ring->queue); + dma_unmap_single(&priv->pdev->dev, entry->TxBuffAddr, + skb->len, DMA_TO_DEVICE); + + kfree_skb(skb); + } + if (prio != BEACON_QUEUE) + tasklet_schedule(&priv->irq_tx_tasklet); +} + +static void _rtl92e_tx_cmd(struct net_device *dev, struct sk_buff *skb) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtl8192_tx_ring *ring; + struct tx_desc_cmd *entry; + unsigned int idx; + struct cb_desc *tcb_desc; + unsigned long flags; + + spin_lock_irqsave(&priv->irq_th_lock, flags); + ring = &priv->tx_ring[TXCMD_QUEUE]; + + idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; + entry = (struct tx_desc_cmd *)&ring->desc[idx]; + + tcb_desc = (struct cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE); + + rtl92e_fill_tx_cmd_desc(dev, entry, tcb_desc, skb); + + __skb_queue_tail(&ring->queue, skb); + spin_unlock_irqrestore(&priv->irq_th_lock, flags); +} + +static short _rtl92e_tx(struct net_device *dev, struct sk_buff *skb) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtl8192_tx_ring *ring; + unsigned long flags; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + struct tx_desc *pdesc = NULL; + struct rtllib_hdr_1addr *header = NULL; + u8 *pda_addr = NULL; + int idx; + u32 fwinfo_size = 0; + + priv->rtllib->bAwakePktSent = true; + + fwinfo_size = sizeof(struct tx_fwinfo_8190pci); + + header = (struct rtllib_hdr_1addr *)(((u8 *)skb->data) + fwinfo_size); + pda_addr = header->addr1; + + if (!is_broadcast_ether_addr(pda_addr) && !is_multicast_ether_addr(pda_addr)) + priv->stats.txbytesunicast += skb->len - fwinfo_size; + + spin_lock_irqsave(&priv->irq_th_lock, flags); + ring = &priv->tx_ring[tcb_desc->queue_index]; + if (tcb_desc->queue_index != BEACON_QUEUE) + idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; + else + idx = 0; + + pdesc = &ring->desc[idx]; + if ((pdesc->OWN == 1) && (tcb_desc->queue_index != BEACON_QUEUE)) { + netdev_warn(dev, + "No more TX desc@%d, ring->idx = %d, idx = %d, skblen = 0x%x queuelen=%d", + tcb_desc->queue_index, ring->idx, idx, skb->len, + skb_queue_len(&ring->queue)); + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + return skb->len; + } + rtl92e_fill_tx_desc(dev, pdesc, tcb_desc, skb); + __skb_queue_tail(&ring->queue, skb); + pdesc->OWN = 1; + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + netif_trans_update(dev); + + rtl92e_writew(dev, TP_POLL, 0x01 << tcb_desc->queue_index); + return 0; +} + +static short _rtl92e_alloc_rx_ring(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rx_desc *entry = NULL; + int i, rx_queue_idx; + + for (rx_queue_idx = 0; rx_queue_idx < MAX_RX_QUEUE; rx_queue_idx++) { + priv->rx_ring[rx_queue_idx] = dma_alloc_coherent(&priv->pdev->dev, + sizeof(*priv->rx_ring[rx_queue_idx]) * priv->rxringcount, + &priv->rx_ring_dma[rx_queue_idx], + GFP_ATOMIC); + if (!priv->rx_ring[rx_queue_idx] || + (unsigned long)priv->rx_ring[rx_queue_idx] & 0xFF) { + netdev_warn(dev, "Cannot allocate RX ring\n"); + return -ENOMEM; + } + + priv->rx_idx[rx_queue_idx] = 0; + + for (i = 0; i < priv->rxringcount; i++) { + struct sk_buff *skb = dev_alloc_skb(priv->rxbuffersize); + dma_addr_t *mapping; + + entry = &priv->rx_ring[rx_queue_idx][i]; + if (!skb) + return 0; + skb->dev = dev; + priv->rx_buf[rx_queue_idx][i] = skb; + mapping = (dma_addr_t *)skb->cb; + *mapping = dma_map_single(&priv->pdev->dev, + skb_tail_pointer(skb), + priv->rxbuffersize, DMA_FROM_DEVICE); + if (dma_mapping_error(&priv->pdev->dev, *mapping)) { + dev_kfree_skb_any(skb); + return -1; + } + entry->BufferAddress = *mapping; + + entry->Length = priv->rxbuffersize; + entry->OWN = 1; + } + + if (entry) + entry->EOR = 1; + } + return 0; +} + +static int _rtl92e_alloc_tx_ring(struct net_device *dev, unsigned int prio, + unsigned int entries) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct tx_desc *ring; + dma_addr_t dma; + int i; + + ring = dma_alloc_coherent(&priv->pdev->dev, sizeof(*ring) * entries, + &dma, GFP_ATOMIC); + if (!ring || (unsigned long)ring & 0xFF) { + netdev_warn(dev, "Cannot allocate TX ring (prio = %d)\n", prio); + return -ENOMEM; + } + + priv->tx_ring[prio].desc = ring; + priv->tx_ring[prio].dma = dma; + priv->tx_ring[prio].idx = 0; + priv->tx_ring[prio].entries = entries; + skb_queue_head_init(&priv->tx_ring[prio].queue); + + for (i = 0; i < entries; i++) + ring[i].NextDescAddress = + (u32)dma + ((i + 1) % entries) * + sizeof(*ring); + + return 0; +} + +static short _rtl92e_pci_initdescring(struct net_device *dev) +{ + u32 ret; + int i; + struct r8192_priv *priv = rtllib_priv(dev); + + ret = _rtl92e_alloc_rx_ring(dev); + if (ret) + return ret; + + for (i = 0; i < MAX_TX_QUEUE_COUNT; i++) { + ret = _rtl92e_alloc_tx_ring(dev, i, priv->txringcount); + if (ret) + goto err_free_rings; + } + + return 0; + +err_free_rings: + _rtl92e_free_rx_ring(dev); + for (i = 0; i < MAX_TX_QUEUE_COUNT; i++) + if (priv->tx_ring[i].desc) + _rtl92e_free_tx_ring(dev, i); + return 1; +} + +void rtl92e_reset_desc_ring(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int i, rx_queue_idx; + unsigned long flags = 0; + + for (rx_queue_idx = 0; rx_queue_idx < MAX_RX_QUEUE; rx_queue_idx++) { + if (priv->rx_ring[rx_queue_idx]) { + struct rx_desc *entry = NULL; + + for (i = 0; i < priv->rxringcount; i++) { + entry = &priv->rx_ring[rx_queue_idx][i]; + entry->OWN = 1; + } + priv->rx_idx[rx_queue_idx] = 0; + } + } + + spin_lock_irqsave(&priv->irq_th_lock, flags); + for (i = 0; i < MAX_TX_QUEUE_COUNT; i++) { + if (priv->tx_ring[i].desc) { + struct rtl8192_tx_ring *ring = &priv->tx_ring[i]; + + while (skb_queue_len(&ring->queue)) { + struct tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb = + __skb_dequeue(&ring->queue); + + dma_unmap_single(&priv->pdev->dev, + entry->TxBuffAddr, skb->len, + DMA_TO_DEVICE); + kfree_skb(skb); + ring->idx = (ring->idx + 1) % ring->entries; + } + ring->idx = 0; + } + } + spin_unlock_irqrestore(&priv->irq_th_lock, flags); +} + +void rtl92e_update_rx_pkt_timestamp(struct net_device *dev, + struct rtllib_rx_stats *stats) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (stats->bIsAMPDU && !stats->bFirstMPDU) + stats->mac_time = priv->last_rx_desc_tsf; + else + priv->last_rx_desc_tsf = stats->mac_time; +} + +long rtl92e_translate_to_dbm(struct r8192_priv *priv, u8 signal_strength_index) +{ + long signal_power; + + signal_power = (long)((signal_strength_index + 1) >> 1); + signal_power -= 95; + + return signal_power; +} + +void rtl92e_update_rx_statistics(struct r8192_priv *priv, + struct rtllib_rx_stats *pprevious_stats) +{ + int weighting = 0; + + if (priv->stats.recv_signal_power == 0) + priv->stats.recv_signal_power = + pprevious_stats->RecvSignalPower; + + if (pprevious_stats->RecvSignalPower > priv->stats.recv_signal_power) + weighting = 5; + else if (pprevious_stats->RecvSignalPower < + priv->stats.recv_signal_power) + weighting = (-5); + priv->stats.recv_signal_power = (priv->stats.recv_signal_power * 5 + + pprevious_stats->RecvSignalPower + + weighting) / 6; +} + +u8 rtl92e_rx_db_to_percent(s8 antpower) +{ + if ((antpower <= -100) || (antpower >= 20)) + return 0; + else if (antpower >= 0) + return 100; + else + return 100 + antpower; + +} /* QueryRxPwrPercentage */ + +u8 rtl92e_evm_db_to_percent(s8 value) +{ + s8 ret_val = clamp(-value, 0, 33) * 3; + + if (ret_val == 99) + ret_val = 100; + + return ret_val; +} + +void rtl92e_copy_mpdu_stats(struct rtllib_rx_stats *psrc_stats, + struct rtllib_rx_stats *ptarget_stats) +{ + ptarget_stats->bIsAMPDU = psrc_stats->bIsAMPDU; + ptarget_stats->bFirstMPDU = psrc_stats->bFirstMPDU; +} + +static void _rtl92e_rx_normal(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_hdr_1addr *rtllib_hdr = NULL; + bool unicast_packet = false; + u32 skb_len = 0; + int rx_queue_idx = RX_MPDU_QUEUE; + + struct rtllib_rx_stats stats = { + .signal = 0, + .noise = (u8)-98, + .rate = 0, + }; + unsigned int count = priv->rxringcount; + + stats.nic_type = NIC_8192E; + + while (count--) { + struct rx_desc *pdesc = &priv->rx_ring[rx_queue_idx] + [priv->rx_idx[rx_queue_idx]]; + struct sk_buff *skb = priv->rx_buf[rx_queue_idx] + [priv->rx_idx[rx_queue_idx]]; + struct sk_buff *new_skb; + + if (pdesc->OWN) + return; + if (!rtl92e_get_rx_stats(dev, &stats, pdesc, skb)) + goto done; + new_skb = dev_alloc_skb(priv->rxbuffersize); + /* if allocation of new skb failed - drop current packet + * and reuse skb + */ + if (unlikely(!new_skb)) + goto done; + + dma_unmap_single(&priv->pdev->dev, *((dma_addr_t *)skb->cb), + priv->rxbuffersize, DMA_FROM_DEVICE); + + skb_put(skb, pdesc->Length); + skb_reserve(skb, stats.RxDrvInfoSize + + stats.RxBufShift); + skb_trim(skb, skb->len - S_CRC_LEN); + rtllib_hdr = (struct rtllib_hdr_1addr *)skb->data; + if (!is_multicast_ether_addr(rtllib_hdr->addr1)) { + /* unicast packet */ + unicast_packet = true; + } + skb_len = skb->len; + + if (!rtllib_rx(priv->rtllib, skb, &stats)) { + dev_kfree_skb_any(skb); + } else { + if (unicast_packet) + priv->stats.rxbytesunicast += skb_len; + } + + skb = new_skb; + skb->dev = dev; + + priv->rx_buf[rx_queue_idx][priv->rx_idx[rx_queue_idx]] = + skb; + *((dma_addr_t *)skb->cb) = dma_map_single(&priv->pdev->dev, + skb_tail_pointer(skb), + priv->rxbuffersize, DMA_FROM_DEVICE); + if (dma_mapping_error(&priv->pdev->dev, *((dma_addr_t *)skb->cb))) { + dev_kfree_skb_any(skb); + return; + } +done: + pdesc->BufferAddress = *((dma_addr_t *)skb->cb); + pdesc->OWN = 1; + pdesc->Length = priv->rxbuffersize; + if (priv->rx_idx[rx_queue_idx] == priv->rxringcount - 1) + pdesc->EOR = 1; + priv->rx_idx[rx_queue_idx] = (priv->rx_idx[rx_queue_idx] + 1) % + priv->rxringcount; + } +} + +static void _rtl92e_tx_resume(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + struct sk_buff *skb; + int queue_index; + + for (queue_index = BK_QUEUE; + queue_index < MAX_QUEUE_SIZE; queue_index++) { + while ((!skb_queue_empty(&ieee->skb_waitQ[queue_index])) && + (priv->rtllib->check_nic_enough_desc(dev, queue_index) > 0)) { + skb = skb_dequeue(&ieee->skb_waitQ[queue_index]); + ieee->softmac_data_hard_start_xmit(skb, dev, 0); + } + } +} + +static void _rtl92e_irq_tx_tasklet(struct tasklet_struct *t) +{ + struct r8192_priv *priv = from_tasklet(priv, t, irq_tx_tasklet); + + _rtl92e_tx_resume(priv->rtllib->dev); +} + +static void _rtl92e_irq_rx_tasklet(struct tasklet_struct *t) +{ + struct r8192_priv *priv = from_tasklet(priv, t, irq_rx_tasklet); + + _rtl92e_rx_normal(priv->rtllib->dev); + + rtl92e_writel(priv->rtllib->dev, INTA_MASK, + rtl92e_readl(priv->rtllib->dev, INTA_MASK) | IMR_RDU); +} + +/**************************************************************************** + * ---------------------------- NIC START/CLOSE STUFF--------------------------- + ****************************************************************************/ +static void _rtl92e_cancel_deferred_work(struct r8192_priv *priv) +{ + cancel_delayed_work_sync(&priv->watch_dog_wq); + cancel_delayed_work_sync(&priv->update_beacon_wq); + cancel_delayed_work(&priv->rtllib->hw_sleep_wq); + cancel_work_sync(&priv->reset_wq); + cancel_work_sync(&priv->qos_activate); +} + +static int _rtl92e_up(struct net_device *dev, bool is_silent_reset) +{ + if (_rtl92e_sta_up(dev, is_silent_reset) == -1) + return -1; + return 0; +} + +static int _rtl92e_open(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int ret; + + mutex_lock(&priv->wx_mutex); + ret = _rtl92e_try_up(dev); + mutex_unlock(&priv->wx_mutex); + return ret; +} + +static int _rtl92e_try_up(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->up == 1) + return -1; + return _rtl92e_up(dev, false); +} + +static int _rtl92e_close(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int ret; + + if ((rtllib_act_scanning(priv->rtllib, false)) && + !(priv->rtllib->softmac_features & IEEE_SOFTMAC_SCAN)) { + rtllib_stop_scan(priv->rtllib); + } + + mutex_lock(&priv->wx_mutex); + + ret = _rtl92e_down(dev, true); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_down(struct net_device *dev, bool shutdownrf) +{ + if (_rtl92e_sta_down(dev, shutdownrf) == -1) + return -1; + + return 0; +} + +void rtl92e_commit(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->up == 0) + return; + rtllib_softmac_stop_protocol(priv->rtllib, 0, true); + rtl92e_irq_disable(dev); + rtl92e_stop_adapter(dev, true); + _rtl92e_up(dev, false); +} + +static void _rtl92e_restart(void *data) +{ + struct r8192_priv *priv = container_of(data, struct r8192_priv, reset_wq); + struct net_device *dev = priv->rtllib->dev; + + mutex_lock(&priv->wx_mutex); + + rtl92e_commit(dev); + + mutex_unlock(&priv->wx_mutex); +} + +static void _rtl92e_set_multicast(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + short promisc; + + promisc = (dev->flags & IFF_PROMISC) ? 1 : 0; + priv->promisc = promisc; +} + +static int _rtl92e_set_mac_adr(struct net_device *dev, void *mac) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct sockaddr *addr = mac; + + mutex_lock(&priv->wx_mutex); + + eth_hw_addr_set(dev, addr->sa_data); + + schedule_work(&priv->reset_wq); + mutex_unlock(&priv->wx_mutex); + + return 0; +} + +static irqreturn_t _rtl92e_irq(int irq, void *netdev) +{ + struct net_device *dev = netdev; + struct r8192_priv *priv = rtllib_priv(dev); + unsigned long flags; + u32 inta; + + if (priv->irq_enabled == 0) + goto done; + + spin_lock_irqsave(&priv->irq_th_lock, flags); + + rtl92e_ack_irq(dev, &inta); + + if (!inta) { + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + goto done; + } + + if (inta == 0xffff) { + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + goto done; + } + + if (!netif_running(dev)) { + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + goto done; + } + + if (inta & IMR_MGNTDOK) { + _rtl92e_tx_isr(dev, MGNT_QUEUE); + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + if (priv->rtllib->ack_tx_to_ieee) { + if (_rtl92e_is_tx_queue_empty(dev)) { + priv->rtllib->ack_tx_to_ieee = 0; + rtllib_ps_tx_ack(priv->rtllib, 1); + } + } + spin_lock_irqsave(&priv->irq_th_lock, flags); + } + + if (inta & IMR_COMDOK) + _rtl92e_tx_isr(dev, TXCMD_QUEUE); + + if (inta & IMR_HIGHDOK) + _rtl92e_tx_isr(dev, HIGH_QUEUE); + + if (inta & IMR_ROK) + tasklet_schedule(&priv->irq_rx_tasklet); + + if (inta & IMR_BcnInt) + tasklet_schedule(&priv->irq_prepare_beacon_tasklet); + + if (inta & IMR_RDU) { + rtl92e_writel(dev, INTA_MASK, + rtl92e_readl(dev, INTA_MASK) & ~IMR_RDU); + tasklet_schedule(&priv->irq_rx_tasklet); + } + + if (inta & IMR_RXFOVW) + tasklet_schedule(&priv->irq_rx_tasklet); + + if (inta & IMR_BKDOK) { + priv->rtllib->link_detect_info.NumTxOkInPeriod++; + _rtl92e_tx_isr(dev, BK_QUEUE); + } + + if (inta & IMR_BEDOK) { + priv->rtllib->link_detect_info.NumTxOkInPeriod++; + _rtl92e_tx_isr(dev, BE_QUEUE); + } + + if (inta & IMR_VIDOK) { + priv->rtllib->link_detect_info.NumTxOkInPeriod++; + _rtl92e_tx_isr(dev, VI_QUEUE); + } + + if (inta & IMR_VODOK) { + priv->rtllib->link_detect_info.NumTxOkInPeriod++; + _rtl92e_tx_isr(dev, VO_QUEUE); + } + + spin_unlock_irqrestore(&priv->irq_th_lock, flags); + +done: + + return IRQ_HANDLED; +} + +/**************************************************************************** + * ---------------------------- PCI_STUFF--------------------------- + ****************************************************************************/ +static const struct net_device_ops rtl8192_netdev_ops = { + .ndo_open = _rtl92e_open, + .ndo_stop = _rtl92e_close, + .ndo_tx_timeout = _rtl92e_tx_timeout, + .ndo_set_rx_mode = _rtl92e_set_multicast, + .ndo_set_mac_address = _rtl92e_set_mac_adr, + .ndo_validate_addr = eth_validate_addr, + .ndo_start_xmit = rtllib_xmit, +}; + +static int _rtl92e_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long ioaddr = 0; + struct net_device *dev = NULL; + struct r8192_priv *priv = NULL; + unsigned long pmem_start, pmem_len, pmem_flags; + int err = -ENOMEM; + + if (pci_enable_device(pdev)) { + dev_err(&pdev->dev, "Failed to enable PCI device"); + return -EIO; + } + + pci_set_master(pdev); + + if (!dma_set_mask(&pdev->dev, DMA_BIT_MASK(32))) { + if (dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) { + dev_info(&pdev->dev, + "Unable to obtain 32bit DMA for consistent allocations\n"); + goto err_pci_disable; + } + } + dev = alloc_rtllib(sizeof(struct r8192_priv)); + if (!dev) + goto err_pci_disable; + + err = -ENODEV; + + pci_set_drvdata(pdev, dev); + SET_NETDEV_DEV(dev, &pdev->dev); + priv = rtllib_priv(dev); + priv->rtllib = (struct rtllib_device *)netdev_priv_rsl(dev); + priv->pdev = pdev; + priv->rtllib->pdev = pdev; + if ((pdev->subsystem_vendor == PCI_VENDOR_ID_DLINK) && + (pdev->subsystem_device == 0x3304)) + priv->rtllib->bSupportRemoteWakeUp = 1; + else + priv->rtllib->bSupportRemoteWakeUp = 0; + + pmem_start = pci_resource_start(pdev, 1); + pmem_len = pci_resource_len(pdev, 1); + pmem_flags = pci_resource_flags(pdev, 1); + + if (!(pmem_flags & IORESOURCE_MEM)) { + netdev_err(dev, "region #1 not a MMIO resource, aborting"); + goto err_rel_rtllib; + } + + dev_info(&pdev->dev, "Memory mapped space start: 0x%08lx\n", + pmem_start); + if (!request_mem_region(pmem_start, pmem_len, DRV_NAME)) { + netdev_err(dev, "request_mem_region failed!"); + goto err_rel_rtllib; + } + + ioaddr = (unsigned long)ioremap(pmem_start, pmem_len); + if (ioaddr == (unsigned long)NULL) { + netdev_err(dev, "ioremap failed!"); + goto err_rel_mem; + } + + dev->mem_start = ioaddr; + dev->mem_end = ioaddr + pci_resource_len(pdev, 0); + + if (!rtl92e_check_adapter(pdev, dev)) + goto err_unmap; + + dev->irq = pdev->irq; + priv->irq = 0; + + dev->netdev_ops = &rtl8192_netdev_ops; + + dev->wireless_handlers = &r8192_wx_handlers_def; + dev->ethtool_ops = &rtl819x_ethtool_ops; + + dev->type = ARPHRD_ETHER; + dev->watchdog_timeo = HZ * 3; + + if (dev_alloc_name(dev, ifname) < 0) + dev_alloc_name(dev, ifname); + + if (_rtl92e_init(dev) != 0) { + netdev_warn(dev, "Initialization failed"); + goto err_free_irq; + } + + netif_carrier_off(dev); + netif_stop_queue(dev); + + if (register_netdev(dev)) + goto err_free_irq; + + if (priv->polling_timer_on == 0) + rtl92e_check_rfctrl_gpio_timer(&priv->gpio_polling_timer); + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); + priv->irq = 0; +err_unmap: + iounmap((void __iomem *)ioaddr); +err_rel_mem: + release_mem_region(pmem_start, pmem_len); +err_rel_rtllib: + free_rtllib(dev); +err_pci_disable: + pci_disable_device(pdev); + return err; +} + +static void _rtl92e_pci_disconnect(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct r8192_priv *priv; + u32 i; + + if (dev) { + unregister_netdev(dev); + + priv = rtllib_priv(dev); + + del_timer_sync(&priv->gpio_polling_timer); + cancel_delayed_work_sync(&priv->gpio_change_rf_wq); + priv->polling_timer_on = 0; + _rtl92e_down(dev, true); + rtl92e_dm_deinit(dev); + vfree(priv->fw_info); + priv->fw_info = NULL; + _rtl92e_free_rx_ring(dev); + for (i = 0; i < MAX_TX_QUEUE_COUNT; i++) + _rtl92e_free_tx_ring(dev, i); + + if (priv->irq) { + dev_info(&pdev->dev, "Freeing irq %d\n", dev->irq); + free_irq(dev->irq, dev); + priv->irq = 0; + } + + if (dev->mem_start != 0) { + iounmap((void __iomem *)dev->mem_start); + release_mem_region(pci_resource_start(pdev, 1), + pci_resource_len(pdev, 1)); + } + + free_rtllib(dev); + } + + pci_disable_device(pdev); +} + +bool rtl92e_enable_nic(struct net_device *dev) +{ + bool init_status = true; + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + (&priv->rtllib->pwr_save_ctrl); + + if (!priv->up) { + netdev_warn(dev, "%s(): Driver is already down!\n", __func__); + return false; + } + + init_status = rtl92e_start_adapter(dev); + if (!init_status) { + netdev_warn(dev, "%s(): Initialization failed!\n", __func__); + return false; + } + RT_CLEAR_PS_LEVEL(psc, RT_RF_OFF_LEVL_HALT_NIC); + + rtl92e_irq_enable(dev); + return init_status; +} + +module_pci_driver(rtl8192_pci_driver); + +void rtl92e_check_rfctrl_gpio_timer(struct timer_list *t) +{ + struct r8192_priv *priv = from_timer(priv, t, gpio_polling_timer); + + priv->polling_timer_on = 1; + + schedule_delayed_work(&priv->gpio_change_rf_wq, 0); + + mod_timer(&priv->gpio_polling_timer, jiffies + + msecs_to_jiffies(RTLLIB_WATCH_DOG_TIME)); +} + +/*************************************************************************** + * ------------------- module init / exit stubs ---------------- + ***************************************************************************/ +MODULE_DESCRIPTION("Linux driver for Realtek RTL819x WiFi cards"); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(RTL8192E_BOOT_IMG_FW); +MODULE_FIRMWARE(RTL8192E_MAIN_IMG_FW); +MODULE_FIRMWARE(RTL8192E_DATA_IMG_FW); + +module_param(ifname, charp, 0644); +module_param(hwwep, int, 0644); + +MODULE_PARM_DESC(ifname, " Net interface name, wlan%d=default"); +MODULE_PARM_DESC(hwwep, " Try to use hardware WEP support(default use hw. set 0 to use software security)"); diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.h b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h new file mode 100644 index 0000000000..fa82a06678 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.h @@ -0,0 +1,423 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _RTL_CORE_H +#define _RTL_CORE_H + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> +#include <linux/rtnetlink.h> +#include <linux/wireless.h> +#include <linux/timer.h> +#include <linux/proc_fs.h> +#include <linux/if_arp.h> +#include <linux/random.h> +#include <linux/io.h> + +/* Need this defined before including local include files */ +#define DRV_NAME "rtl819xE" + +#include "../rtllib.h" + +#include "../dot11d.h" + +#include "r8192E_firmware.h" +#include "r8192E_hw.h" + +#include "r8190P_def.h" +#include "r8192E_dev.h" + +#include "rtl_eeprom.h" +#include "rtl_ps.h" +#include "rtl_pci.h" +#include "rtl_cam.h" + +#define DRV_COPYRIGHT \ + "Copyright(c) 2008 - 2010 Realsil Semiconductor Corporation" +#define DRV_AUTHOR "<wlanfae@realtek.com>" +#define DRV_VERSION "0014.0401.2010" + +#define TOTAL_CAM_ENTRY 32 +#define CAM_CONTENT_COUNT 8 + +#define HAL_HW_PCI_REVISION_ID_8192PCIE 0x01 +#define HAL_HW_PCI_REVISION_ID_8192SE 0x10 + +#define RTLLIB_WATCH_DOG_TIME 2000 + +#define MAX_DEV_ADDR_SIZE 8 /*support till 64 bit bus width OS*/ +#define MAX_FIRMWARE_INFORMATION_SIZE 32 +#define MAX_802_11_HEADER_LENGTH (40 + MAX_FIRMWARE_INFORMATION_SIZE) +#define ENCRYPTION_MAX_OVERHEAD 128 +#define MAX_FRAGMENT_COUNT 8 +#define MAX_TRANSMIT_BUFFER_SIZE \ + (1600 + (MAX_802_11_HEADER_LENGTH + ENCRYPTION_MAX_OVERHEAD) * \ + MAX_FRAGMENT_COUNT) + +#define CMDPACKET_FRAG_SIZE (4 * (MAX_TRANSMIT_BUFFER_SIZE / 4) - 8) + +#define DEFAULT_FRAG_THRESHOLD 2342U +#define MIN_FRAG_THRESHOLD 256U +#define DEFAULT_BEACONINTERVAL 0x64U + +#define DEFAULT_RETRY_RTS 7 +#define DEFAULT_RETRY_DATA 7 + +#define PHY_RSSI_SLID_WIN_MAX 100 + +#define TX_BB_GAIN_TABLE_LEN 37 +#define CCK_TX_BB_GAIN_TABLE_LEN 23 + +#define CHANNEL_PLAN_LEN 10 +#define S_CRC_LEN 4 + +#define NIC_SEND_HANG_THRESHOLD_NORMAL 4 +#define NIC_SEND_HANG_THRESHOLD_POWERSAVE 8 + +#define MAX_TX_QUEUE 9 + +#define MAX_RX_QUEUE 1 + +#define MAX_RX_COUNT 64 +#define MAX_TX_QUEUE_COUNT 9 + +extern int hwwep; + +enum nic_t { + NIC_UNKNOWN = 0, + NIC_8192E = 1, + NIC_8190P = 2, + NIC_8192SE = 4, + NIC_8192CE = 5, + NIC_8192CU = 6, + NIC_8192DE = 7, + NIC_8192DU = 8, +}; + +enum rt_eeprom_type { + EEPROM_93C46, + EEPROM_93C56, +}; + +enum dcmg_txcmd_op { + TXCMD_TXRA_HISTORY_CTRL = 0xFF900000, + TXCMD_RESET_TX_PKT_BUFF = 0xFF900001, + TXCMD_RESET_RX_PKT_BUFF = 0xFF900002, + TXCMD_SET_TX_DURATION = 0xFF900003, + TXCMD_SET_RX_RSSI = 0xFF900004, + TXCMD_SET_TX_PWR_TRACKING = 0xFF900005, + TXCMD_XXXX_CTRL, +}; + +enum rt_customer_id { + RT_CID_DEFAULT = 0, + RT_CID_TOSHIBA = 9, + RT_CID_819X_NETCORE = 10, +}; + +enum reset_type { + RESET_TYPE_NORESET = 0x00, + RESET_TYPE_NORMAL = 0x01, + RESET_TYPE_SILENT = 0x02 +}; + +struct rt_stats { + unsigned long received_rate_histogram[4][32]; + unsigned long txbytesunicast; + unsigned long rxbytesunicast; + unsigned long txretrycount; + u8 last_packet_rate; + unsigned long slide_signal_strength[100]; + unsigned long slide_evm[100]; + unsigned long slide_rssi_total; + unsigned long slide_evm_total; + long signal_strength; + long last_signal_strength_inpercent; + long recv_signal_power; + u8 rx_rssi_percentage[4]; + u8 rx_evm_percentage[2]; + u32 slide_beacon_pwdb[100]; + u32 slide_beacon_total; + u32 CurrentShowTxate; +}; + +struct init_gain { + u8 xaagccore1; + u8 xbagccore1; + u8 xcagccore1; + u8 xdagccore1; + u8 cca; + +}; + +struct tx_ring { + u32 *desc; + u8 nStuckCount; + struct tx_ring *next; +} __packed; + +struct rtl8192_tx_ring { + struct tx_desc *desc; + dma_addr_t dma; + unsigned int idx; + unsigned int entries; + struct sk_buff_head queue; +}; + +struct r8192_priv { + struct pci_dev *pdev; + struct pci_dev *bridge_pdev; + + bool bfirst_after_down; + bool being_init_adapter; + + int irq; + short irq_enabled; + + short up; + short up_first_time; + struct delayed_work update_beacon_wq; + struct delayed_work watch_dog_wq; + struct delayed_work txpower_tracking_wq; + struct delayed_work rfpath_check_wq; + struct delayed_work gpio_change_rf_wq; + struct rtllib_device *rtllib; + + struct work_struct reset_wq; + + enum rt_customer_id customer_id; + + enum ht_channel_width current_chnl_bw; + struct bb_reg_definition phy_reg_def[4]; + struct rate_adaptive rate_adaptive; + + struct rt_firmware *fw_info; + enum rtl819x_loopback loopback_mode; + + struct timer_list watch_dog_timer; + struct timer_list fsync_timer; + struct timer_list gpio_polling_timer; + + spinlock_t irq_th_lock; + spinlock_t tx_lock; + spinlock_t rf_ps_lock; + spinlock_t ps_lock; + + struct sk_buff_head skb_queue; + + struct tasklet_struct irq_rx_tasklet; + struct tasklet_struct irq_tx_tasklet; + struct tasklet_struct irq_prepare_beacon_tasklet; + + struct mutex wx_mutex; + struct mutex rf_mutex; + struct mutex mutex; + + struct rt_stats stats; + struct iw_statistics wstats; + + u8 (*rf_set_chan)(struct net_device *dev, u8 ch); + + struct rx_desc *rx_ring[MAX_RX_QUEUE]; + struct sk_buff *rx_buf[MAX_RX_QUEUE][MAX_RX_COUNT]; + dma_addr_t rx_ring_dma[MAX_RX_QUEUE]; + unsigned int rx_idx[MAX_RX_QUEUE]; + int rxringcount; + u16 rxbuffersize; + + u64 last_rx_desc_tsf; + + u32 receive_config; + u8 retry_data; + u8 retry_rts; + u16 rts; + + struct rtl8192_tx_ring tx_ring[MAX_TX_QUEUE_COUNT]; + int txringcount; + atomic_t tx_pending[0x10]; + + u16 short_retry_limit; + u16 long_retry_limit; + + bool hw_radio_off; + bool blinked_ingpio; + u8 polling_timer_on; + + /**********************************************************/ + struct work_struct qos_activate; + + short promisc; + + short chan; + bool ps_force; + + u32 irq_mask[2]; + + u8 rf_mode; + enum nic_t card_8192; + u8 card_8192_version; + + u8 ic_cut; + char nick[IW_ESSID_MAX_SIZE + 1]; + u8 check_roaming_cnt; + + u32 silent_reset_rx_slot_index; + u32 silent_reset_rx_stuck_event[MAX_SILENT_RESET_RX_SLOT_NUM]; + + u16 basic_rate; + u8 short_preamble; + u8 dot11_current_preamble_mode; + u8 slot_time; + + bool autoload_fail_flag; + + short epromtype; + u16 eeprom_vid; + u16 eeprom_did; + u8 eeprom_customer_id; + u16 eeprom_chnl_plan; + + u8 eeprom_tx_pwr_level_cck[14]; + u8 eeprom_tx_pwr_level_ofdm24g[14]; + u16 eeprom_ant_pwr_diff; + u8 eeprom_thermal_meter; + u8 eeprom_crystal_cap; + + u8 eeprom_legacy_ht_tx_pwr_diff; + + u8 crystal_cap; + u8 thermal_meter[2]; + + u8 sw_chnl_in_progress; + u8 sw_chnl_stage; + u8 sw_chnl_step; + u8 set_bw_mode_in_progress; + + u8 n_cur_40mhz_prime_sc; + + u32 rf_reg_0value[4]; + u8 num_total_rf_path; + bool brfpath_rxenable[4]; + + bool tx_pwr_data_read_from_eeprom; + + u16 chnl_plan; + u8 hw_rf_off_action; + + bool rf_change_in_progress; + bool set_rf_pwr_state_in_progress; + + u8 cck_pwr_enl; + u16 tssi_13dBm; + u32 pwr_track; + u8 cck_present_attn_20m_def; + u8 cck_present_attn_40m_def; + s8 cck_present_attn_diff; + s8 cck_present_attn; + long undecorated_smoothed_pwdb; + + u32 mcs_tx_pwr_level_org_offset[6]; + u8 tx_pwr_level_cck[14]; + u8 tx_pwr_level_ofdm_24g[14]; + u8 legacy_ht_tx_pwr_diff; + u8 antenna_tx_pwr_diff[3]; + + bool dynamic_tx_high_pwr; + bool dynamic_tx_low_pwr; + bool last_dtp_flag_high; + bool last_dtp_flag_low; + + u8 rfa_txpowertrackingindex; + u8 rfa_txpowertrackingindex_real; + u8 rfa_txpowertracking_default; + u8 rfc_txpowertrackingindex; + bool btxpower_tracking; + bool bcck_in_ch14; + + u8 txpower_count; + bool tx_pwr_tracking_init; + + u8 ofdm_index[2]; + u8 cck_index; + + u8 rec_cck_20m_idx; + u8 rec_cck_40m_idx; + + struct init_gain initgain_backup; + u8 def_initial_gain[4]; + bool bis_any_nonbepkts; + bool bcurrent_turbo_EDCA; + bool bis_cur_rdlstate; + + u32 rate_record; + u32 rate_count_diff_rec; + u32 continue_diff_count; + bool bswitch_fsync; + u8 framesync; + u32 reset_count; + + enum reset_type rst_progress; + u16 tx_counter; + u16 rx_ctr; + bool reset_in_progress; + bool force_reset; + bool force_lps; +}; + +extern const struct ethtool_ops rtl819x_ethtool_ops; + +u8 rtl92e_readb(struct net_device *dev, int x); +u32 rtl92e_readl(struct net_device *dev, int x); +u16 rtl92e_readw(struct net_device *dev, int x); +void rtl92e_writeb(struct net_device *dev, int x, u8 y); +void rtl92e_writew(struct net_device *dev, int x, u16 y); +void rtl92e_writel(struct net_device *dev, int x, u32 y); + +void force_pci_posting(struct net_device *dev); + +void rtl92e_rx_enable(struct net_device *dev); +void rtl92e_tx_enable(struct net_device *dev); + +void rtl92e_hw_sleep_wq(void *data); +void rtl92e_commit(struct net_device *dev); + +void rtl92e_check_rfctrl_gpio_timer(struct timer_list *t); + +void rtl92e_hw_wakeup_wq(void *data); + +void rtl92e_reset_desc_ring(struct net_device *dev); +void rtl92e_set_wireless_mode(struct net_device *dev, u8 wireless_mode); +void rtl92e_irq_enable(struct net_device *dev); +void rtl92e_config_rate(struct net_device *dev, u16 *rate_config); +void rtl92e_irq_disable(struct net_device *dev); + +void rtl92e_update_rx_pkt_timestamp(struct net_device *dev, + struct rtllib_rx_stats *stats); +long rtl92e_translate_to_dbm(struct r8192_priv *priv, u8 signal_strength_index); +void rtl92e_update_rx_statistics(struct r8192_priv *priv, + struct rtllib_rx_stats *pprevious_stats); +u8 rtl92e_evm_db_to_percent(s8 value); +u8 rtl92e_rx_db_to_percent(s8 antpower); +void rtl92e_copy_mpdu_stats(struct rtllib_rx_stats *psrc_stats, + struct rtllib_rx_stats *ptarget_stats); +bool rtl92e_enable_nic(struct net_device *dev); + +bool rtl92e_set_rf_state(struct net_device *dev, + enum rt_rf_power_state state_to_set, + RT_RF_CHANGE_SOURCE change_source); +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c new file mode 100644 index 0000000000..dbf765d601 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c @@ -0,0 +1,2157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "rtl_dm.h" +#include "r8192E_hw.h" +#include "r8192E_phy.h" +#include "r8192E_phyreg.h" +#include "r8190P_rtl8256.h" +#include "r8192E_cmdpkt.h" + +/*---------------------------Define Local Constant---------------------------*/ +static u32 edca_setting_DL[HT_IOT_PEER_MAX] = { + 0x5e4322, + 0x5e4322, + 0x5ea44f, + 0x5e4322, + 0x604322, + 0xa44f, + 0x5e4322, + 0x5e4332 +}; + +static u32 edca_setting_DL_GMode[HT_IOT_PEER_MAX] = { + 0x5e4322, + 0x5e4322, + 0x5e4322, + 0x5e4322, + 0x604322, + 0xa44f, + 0x5e4322, + 0x5e4322 +}; + +static u32 edca_setting_UL[HT_IOT_PEER_MAX] = { + 0x5e4322, + 0xa44f, + 0x5ea44f, + 0x5e4322, + 0x604322, + 0x5e4322, + 0x5e4322, + 0x5e4332 +}; + +const u32 dm_tx_bb_gain[TX_BB_GAIN_TABLE_LEN] = { + 0x7f8001fe, /* 12 dB */ + 0x788001e2, /* 11 dB */ + 0x71c001c7, + 0x6b8001ae, + 0x65400195, + 0x5fc0017f, + 0x5a400169, + 0x55400155, + 0x50800142, + 0x4c000130, + 0x47c0011f, + 0x43c0010f, + 0x40000100, + 0x3c8000f2, + 0x390000e4, + 0x35c000d7, + 0x32c000cb, + 0x300000c0, + 0x2d4000b5, + 0x2ac000ab, + 0x288000a2, + 0x26000098, + 0x24000090, + 0x22000088, + 0x20000080, + 0x1a00006c, + 0x1c800072, + 0x18000060, + 0x19800066, + 0x15800056, + 0x26c0005b, + 0x14400051, + 0x24400051, + 0x1300004c, + 0x12000048, + 0x11000044, + 0x10000040, /* -24 dB */ +}; + +const u8 dm_cck_tx_bb_gain[CCK_TX_BB_GAIN_TABLE_LEN][8] = { + {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, + {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, + {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, + {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, + {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, + {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, + {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, + {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, + {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, + {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, + {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, + {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, + {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, + {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, + {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, + {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, + {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, + {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, + {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, + {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, + {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, + {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, + {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01} +}; + +const u8 dm_cck_tx_bb_gain_ch14[CCK_TX_BB_GAIN_TABLE_LEN][8] = { + {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, + {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, + {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, + {0x2d, 0x2d, 0x27, 0x17, 0x00, 0x00, 0x00, 0x00}, + {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, + {0x28, 0x28, 0x22, 0x14, 0x00, 0x00, 0x00, 0x00}, + {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, + {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, + {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, + {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, + {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, + {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, + {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, + {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, + {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, + {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, + {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, + {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, + {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, + {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, + {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, + {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, + {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00} +}; + +/*---------------------------Define Local Constant---------------------------*/ + + +/*------------------------Define global variable-----------------------------*/ +struct dig_t dm_digtable; + +struct drx_path_sel dm_rx_path_sel_table; +/*------------------------Define global variable-----------------------------*/ + + +/*------------------------Define local variable------------------------------*/ +/*------------------------Define local variable------------------------------*/ + + + +/*---------------------Define local function prototype-----------------------*/ +static void _rtl92e_dm_check_rate_adaptive(struct net_device *dev); + +static void _rtl92e_dm_init_bandwidth_autoswitch(struct net_device *dev); +static void _rtl92e_dm_bandwidth_autoswitch(struct net_device *dev); + +static void _rtl92e_dm_check_tx_power_tracking(struct net_device *dev); + +static void _rtl92e_dm_bb_initialgain_restore(struct net_device *dev); +static void _rtl92e_dm_dig_init(struct net_device *dev); +static void _rtl92e_dm_ctrl_initgain_byrssi(struct net_device *dev); +static void _rtl92e_dm_ctrl_initgain_byrssi_highpwr(struct net_device *dev); +static void _rtl92e_dm_ctrl_initgain_byrssi_driver(struct net_device *dev); +static void _rtl92e_dm_ctrl_initgain_byrssi_false_alarm(struct net_device *dev); +static void _rtl92e_dm_initial_gain(struct net_device *dev); +static void _rtl92e_dm_pd_th(struct net_device *dev); +static void _rtl92e_dm_cs_ratio(struct net_device *dev); + +static void _rtl92e_dm_init_cts_to_self(struct net_device *dev); + +static void _rtl92e_dm_check_edca_turbo(struct net_device *dev); +static void _rtl92e_dm_check_rx_path_selection(struct net_device *dev); +static void _rtl92e_dm_init_rx_path_selection(struct net_device *dev); +static void _rtl92e_dm_rx_path_sel_byrssi(struct net_device *dev); + +static void _rtl92e_dm_init_fsync(struct net_device *dev); +static void _rtl92e_dm_deinit_fsync(struct net_device *dev); + +static void _rtl92e_dm_check_txrateandretrycount(struct net_device *dev); +static void _rtl92e_dm_check_fsync(struct net_device *dev); +static void _rtl92e_dm_check_rf_ctrl_gpio(void *data); +static void _rtl92e_dm_fsync_timer_callback(struct timer_list *t); + +/*---------------------Define local function prototype-----------------------*/ + +static void _rtl92e_dm_init_dynamic_tx_power(struct net_device *dev); +static void _rtl92e_dm_dynamic_tx_power(struct net_device *dev); + +static void _rtl92e_dm_send_rssi_to_fw(struct net_device *dev); +static void _rtl92e_dm_cts_to_self(struct net_device *dev); +/*---------------------------Define function prototype------------------------*/ + +void rtl92e_dm_init(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->undecorated_smoothed_pwdb = -1; + + _rtl92e_dm_init_dynamic_tx_power(dev); + + rtl92e_init_adaptive_rate(dev); + + _rtl92e_dm_dig_init(dev); + rtl92e_dm_init_edca_turbo(dev); + _rtl92e_dm_init_bandwidth_autoswitch(dev); + _rtl92e_dm_init_fsync(dev); + _rtl92e_dm_init_rx_path_selection(dev); + _rtl92e_dm_init_cts_to_self(dev); + + INIT_DELAYED_WORK(&priv->gpio_change_rf_wq, (void *)_rtl92e_dm_check_rf_ctrl_gpio); +} + +void rtl92e_dm_deinit(struct net_device *dev) +{ + _rtl92e_dm_deinit_fsync(dev); +} + +void rtl92e_dm_watchdog(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->being_init_adapter) + return; + + _rtl92e_dm_check_txrateandretrycount(dev); + _rtl92e_dm_check_edca_turbo(dev); + + _rtl92e_dm_check_rate_adaptive(dev); + _rtl92e_dm_dynamic_tx_power(dev); + _rtl92e_dm_check_tx_power_tracking(dev); + + _rtl92e_dm_ctrl_initgain_byrssi(dev); + _rtl92e_dm_bandwidth_autoswitch(dev); + + _rtl92e_dm_check_rx_path_selection(dev); + _rtl92e_dm_check_fsync(dev); + + _rtl92e_dm_send_rssi_to_fw(dev); + _rtl92e_dm_cts_to_self(dev); +} + +void rtl92e_init_adaptive_rate(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rate_adaptive *pra = &priv->rate_adaptive; + + pra->ratr_state = DM_RATR_STA_MAX; + pra->high2low_rssi_thresh_for_ra = RATE_ADAPTIVE_TH_HIGH; + pra->low2high_rssi_thresh_for_ra20M = RATE_ADAPTIVE_TH_LOW_20M + 5; + pra->low2high_rssi_thresh_for_ra40M = RATE_ADAPTIVE_TH_LOW_40M + 5; + + pra->high_rssi_thresh_for_ra = RATE_ADAPTIVE_TH_HIGH + 5; + pra->low_rssi_thresh_for_ra20M = RATE_ADAPTIVE_TH_LOW_20M; + pra->low_rssi_thresh_for_ra40M = RATE_ADAPTIVE_TH_LOW_40M; + + if (priv->customer_id == RT_CID_819X_NETCORE) + pra->ping_rssi_enable = 1; + else + pra->ping_rssi_enable = 0; + pra->ping_rssi_thresh_for_ra = 15; + + pra->upper_rssi_threshold_ratr = 0x000fc000; + pra->middle_rssi_threshold_ratr = 0x000ff000; + pra->low_rssi_threshold_ratr = 0x000ff001; + pra->low_rssi_threshold_ratr_40M = 0x000ff005; + pra->low_rssi_threshold_ratr_20M = 0x000ff001; + pra->ping_rssi_ratr = 0x0000000d; +} + +static void _rtl92e_dm_check_rate_adaptive(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_hi_throughput *ht_info = priv->rtllib->ht_info; + struct rate_adaptive *pra = &priv->rate_adaptive; + u32 current_ratr, target_ratr = 0; + u32 low_rssi_thresh_for_ra = 0, high_rssi_thresh_for_ra = 0; + bool bshort_gi_enabled = false; + static u8 ping_rssi_state; + + if (!priv->up) + return; + + if (pra->rate_adaptive_disabled) + return; + + if (priv->rtllib->mode != WIRELESS_MODE_N_24G) + return; + + if (priv->rtllib->link_state == MAC80211_LINKED) { + bshort_gi_enabled = (ht_info->cur_tx_bw40mhz && + ht_info->bCurShortGI40MHz) || + (!ht_info->cur_tx_bw40mhz && + ht_info->bCurShortGI20MHz); + + pra->upper_rssi_threshold_ratr = + (pra->upper_rssi_threshold_ratr & (~BIT31)) | + ((bshort_gi_enabled) ? BIT31 : 0); + + pra->middle_rssi_threshold_ratr = + (pra->middle_rssi_threshold_ratr & (~BIT31)) | + ((bshort_gi_enabled) ? BIT31 : 0); + + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) { + pra->low_rssi_threshold_ratr = + (pra->low_rssi_threshold_ratr_40M & (~BIT31)) | + ((bshort_gi_enabled) ? BIT31 : 0); + } else { + pra->low_rssi_threshold_ratr = + (pra->low_rssi_threshold_ratr_20M & (~BIT31)) | + ((bshort_gi_enabled) ? BIT31 : 0); + } + pra->ping_rssi_ratr = + (pra->ping_rssi_ratr & (~BIT31)) | + ((bshort_gi_enabled) ? BIT31 : 0); + + if (pra->ratr_state == DM_RATR_STA_HIGH) { + high_rssi_thresh_for_ra = pra->high2low_rssi_thresh_for_ra; + low_rssi_thresh_for_ra = (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) ? + (pra->low_rssi_thresh_for_ra40M) : (pra->low_rssi_thresh_for_ra20M); + } else if (pra->ratr_state == DM_RATR_STA_LOW) { + high_rssi_thresh_for_ra = pra->high_rssi_thresh_for_ra; + low_rssi_thresh_for_ra = (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) ? + (pra->low2high_rssi_thresh_for_ra40M) : (pra->low2high_rssi_thresh_for_ra20M); + } else { + high_rssi_thresh_for_ra = pra->high_rssi_thresh_for_ra; + low_rssi_thresh_for_ra = (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) ? + (pra->low_rssi_thresh_for_ra40M) : (pra->low_rssi_thresh_for_ra20M); + } + + if (priv->undecorated_smoothed_pwdb >= + (long)high_rssi_thresh_for_ra) { + pra->ratr_state = DM_RATR_STA_HIGH; + target_ratr = pra->upper_rssi_threshold_ratr; + } else if (priv->undecorated_smoothed_pwdb >= + (long)low_rssi_thresh_for_ra) { + pra->ratr_state = DM_RATR_STA_MIDDLE; + target_ratr = pra->middle_rssi_threshold_ratr; + } else { + pra->ratr_state = DM_RATR_STA_LOW; + target_ratr = pra->low_rssi_threshold_ratr; + } + + if (pra->ping_rssi_enable) { + if (priv->undecorated_smoothed_pwdb < + (long)(pra->ping_rssi_thresh_for_ra + 5)) { + if ((priv->undecorated_smoothed_pwdb < + (long)pra->ping_rssi_thresh_for_ra) || + ping_rssi_state) { + pra->ratr_state = DM_RATR_STA_LOW; + target_ratr = pra->ping_rssi_ratr; + ping_rssi_state = 1; + } + } else { + ping_rssi_state = 0; + } + } + + if (priv->rtllib->GetHalfNmodeSupportByAPsHandler(dev)) + target_ratr &= 0xf00fffff; + + current_ratr = rtl92e_readl(dev, RATR0); + if (target_ratr != current_ratr) { + u32 ratr_value; + + ratr_value = target_ratr; + ratr_value &= ~(RATE_ALL_OFDM_2SS); + rtl92e_writel(dev, RATR0, ratr_value); + rtl92e_writeb(dev, UFWP, 1); + + pra->last_ratr = target_ratr; + } + + } else { + pra->ratr_state = DM_RATR_STA_MAX; + } +} + +static void _rtl92e_dm_init_bandwidth_autoswitch(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->rtllib->bandwidth_auto_switch.threshold_20Mhzto40Mhz = BW_AUTO_SWITCH_LOW_HIGH; + priv->rtllib->bandwidth_auto_switch.threshold_40Mhzto20Mhz = BW_AUTO_SWITCH_HIGH_LOW; + priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz = false; + priv->rtllib->bandwidth_auto_switch.bautoswitch_enable = false; +} + +static void _rtl92e_dm_bandwidth_autoswitch(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->current_chnl_bw == HT_CHANNEL_WIDTH_20 || + !priv->rtllib->bandwidth_auto_switch.bautoswitch_enable) + return; + if (!priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz) { + if (priv->undecorated_smoothed_pwdb <= + priv->rtllib->bandwidth_auto_switch.threshold_40Mhzto20Mhz) + priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz = true; + } else { + if (priv->undecorated_smoothed_pwdb >= + priv->rtllib->bandwidth_auto_switch.threshold_20Mhzto40Mhz) + priv->rtllib->bandwidth_auto_switch.bforced_tx20Mhz = false; + } +} + +static u32 OFDMSwingTable[OFDM_TABLE_LEN] = { + 0x7f8001fe, + 0x71c001c7, + 0x65400195, + 0x5a400169, + 0x50800142, + 0x47c0011f, + 0x40000100, + 0x390000e4, + 0x32c000cb, + 0x2d4000b5, + 0x288000a2, + 0x24000090, + 0x20000080, + 0x1c800072, + 0x19800066, + 0x26c0005b, + 0x24400051, + 0x12000048, + 0x10000040 +}; + +static u8 CCKSwingTable_Ch1_Ch13[CCK_TABLE_LEN][8] = { + {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, + {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, + {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, + {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, + {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, + {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, + {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, + {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, + {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, + {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, + {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, + {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01} +}; + +static u8 CCKSwingTable_Ch14[CCK_TABLE_LEN][8] = { + {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, + {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, + {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, + {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, + {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, + {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, + {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, + {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, + {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, + {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, + {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, + {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00} +}; + +#define Pw_Track_Flag 0x11d +#define Tssi_Mea_Value 0x13c +#define Tssi_Report_Value1 0x134 +#define Tssi_Report_Value2 0x13e +#define FW_Busy_Flag 0x13f + +static void _rtl92e_dm_tx_update_tssi_weak_signal(struct net_device *dev) +{ + struct r8192_priv *p = rtllib_priv(dev); + + if (p->rfa_txpowertrackingindex > 0) { + p->rfa_txpowertrackingindex--; + if (p->rfa_txpowertrackingindex_real > 4) { + p->rfa_txpowertrackingindex_real--; + rtl92e_set_bb_reg(dev, + rOFDM0_XATxIQImbalance, + bMaskDWord, + dm_tx_bb_gain[p->rfa_txpowertrackingindex_real]); + } + } else { + rtl92e_set_bb_reg(dev, rOFDM0_XATxIQImbalance, + bMaskDWord, dm_tx_bb_gain[4]); + } +} + +static void _rtl92e_dm_tx_update_tssi_strong_signal(struct net_device *dev) +{ + struct r8192_priv *p = rtllib_priv(dev); + + if (p->rfa_txpowertrackingindex < (TX_BB_GAIN_TABLE_LEN - 1)) { + p->rfa_txpowertrackingindex++; + p->rfa_txpowertrackingindex_real++; + rtl92e_set_bb_reg(dev, rOFDM0_XATxIQImbalance, + bMaskDWord, + dm_tx_bb_gain[p->rfa_txpowertrackingindex_real]); + } else { + rtl92e_set_bb_reg(dev, rOFDM0_XATxIQImbalance, + bMaskDWord, + dm_tx_bb_gain[TX_BB_GAIN_TABLE_LEN - 1]); + } +} + +static void _rtl92e_dm_tx_power_tracking_callback_tssi(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + bool viviflag = false; + struct dcmd_txcmd tx_cmd; + int i = 0, j = 0, k = 0; + u8 tmp_report[5] = {0, 0, 0, 0, 0}; + u8 Pwr_Flag; + u16 Avg_TSSI_Meas, tssi_13dBm, Avg_TSSI_Meas_from_driver = 0; + u32 delta = 0; + + rtl92e_writeb(dev, Pw_Track_Flag, 0); + rtl92e_writeb(dev, FW_Busy_Flag, 0); + priv->rtllib->bdynamic_txpower_enable = false; + + for (j = 0; j <= 30; j++) { + tx_cmd.op = TXCMD_SET_TX_PWR_TRACKING; + tx_cmd.length = 4; + tx_cmd.value = priv->pwr_track >> 24; + rtl92e_send_cmd_pkt(dev, DESC_PACKET_TYPE_NORMAL, (u8 *)&tx_cmd, + sizeof(struct dcmd_txcmd)); + mdelay(1); + for (i = 0; i <= 30; i++) { + Pwr_Flag = rtl92e_readb(dev, Pw_Track_Flag); + + if (Pwr_Flag == 0) { + mdelay(1); + + if (priv->reset_in_progress) { + rtl92e_writeb(dev, Pw_Track_Flag, 0); + rtl92e_writeb(dev, FW_Busy_Flag, 0); + return; + } + if (priv->rtllib->rf_power_state != rf_on) { + rtl92e_writeb(dev, Pw_Track_Flag, 0); + rtl92e_writeb(dev, FW_Busy_Flag, 0); + return; + } + + continue; + } + + Avg_TSSI_Meas = rtl92e_readw(dev, Tssi_Mea_Value); + + if (Avg_TSSI_Meas == 0) { + rtl92e_writeb(dev, Pw_Track_Flag, 0); + rtl92e_writeb(dev, FW_Busy_Flag, 0); + return; + } + + for (k = 0; k < 5; k++) { + if (k != 4) + tmp_report[k] = rtl92e_readb(dev, + Tssi_Report_Value1 + k); + else + tmp_report[k] = rtl92e_readb(dev, + Tssi_Report_Value2); + + if (tmp_report[k] <= 20) { + viviflag = true; + break; + } + } + + if (viviflag) { + rtl92e_writeb(dev, Pw_Track_Flag, 0); + viviflag = false; + for (k = 0; k < 5; k++) + tmp_report[k] = 0; + break; + } + + for (k = 0; k < 5; k++) + Avg_TSSI_Meas_from_driver += tmp_report[k]; + + Avg_TSSI_Meas_from_driver *= 100 / 5; + tssi_13dBm = priv->tssi_13dBm; + + if (Avg_TSSI_Meas_from_driver > tssi_13dBm) + delta = Avg_TSSI_Meas_from_driver - tssi_13dBm; + else + delta = tssi_13dBm - Avg_TSSI_Meas_from_driver; + + if (delta <= E_FOR_TX_POWER_TRACK) { + priv->rtllib->bdynamic_txpower_enable = true; + rtl92e_writeb(dev, Pw_Track_Flag, 0); + rtl92e_writeb(dev, FW_Busy_Flag, 0); + return; + } + if (Avg_TSSI_Meas_from_driver < tssi_13dBm - E_FOR_TX_POWER_TRACK) + _rtl92e_dm_tx_update_tssi_weak_signal(dev); + else + _rtl92e_dm_tx_update_tssi_strong_signal(dev); + + priv->cck_present_attn_diff + = priv->rfa_txpowertrackingindex_real - priv->rfa_txpowertracking_default; + + if (priv->current_chnl_bw == HT_CHANNEL_WIDTH_20) + priv->cck_present_attn = + priv->cck_present_attn_20m_def + + priv->cck_present_attn_diff; + else + priv->cck_present_attn = + priv->cck_present_attn_40m_def + + priv->cck_present_attn_diff; + + if (priv->cck_present_attn > (CCK_TX_BB_GAIN_TABLE_LEN - 1)) + priv->cck_present_attn = CCK_TX_BB_GAIN_TABLE_LEN - 1; + if (priv->cck_present_attn < 0) + priv->cck_present_attn = 0; + + if (priv->cck_present_attn > -1 && + priv->cck_present_attn < CCK_TX_BB_GAIN_TABLE_LEN) { + if (priv->rtllib->current_network.channel == 14 && + !priv->bcck_in_ch14) { + priv->bcck_in_ch14 = true; + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } else if (priv->rtllib->current_network.channel != 14 && priv->bcck_in_ch14) { + priv->bcck_in_ch14 = false; + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } else { + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + } + } + + if (priv->cck_present_attn_diff <= -12 || + priv->cck_present_attn_diff >= 24) { + priv->rtllib->bdynamic_txpower_enable = true; + rtl92e_writeb(dev, Pw_Track_Flag, 0); + rtl92e_writeb(dev, FW_Busy_Flag, 0); + return; + } + + rtl92e_writeb(dev, Pw_Track_Flag, 0); + Avg_TSSI_Meas_from_driver = 0; + for (k = 0; k < 5; k++) + tmp_report[k] = 0; + break; + } + rtl92e_writeb(dev, FW_Busy_Flag, 0); + } + priv->rtllib->bdynamic_txpower_enable = true; + rtl92e_writeb(dev, Pw_Track_Flag, 0); +} + +static void _rtl92e_dm_tx_power_tracking_cb_thermal(struct net_device *dev) +{ +#define ThermalMeterVal 9 + struct r8192_priv *priv = rtllib_priv(dev); + u32 tmp_reg, tmp_cck; + u8 tmp_ofdm_index, tmp_cck_index, tmp_cck_20m_index, tmp_cck_40m_index, tmpval; + int i = 0, CCKSwingNeedUpdate = 0; + + if (!priv->tx_pwr_tracking_init) { + tmp_reg = rtl92e_get_bb_reg(dev, rOFDM0_XATxIQImbalance, + bMaskDWord); + for (i = 0; i < OFDM_TABLE_LEN; i++) { + if (tmp_reg == OFDMSwingTable[i]) + priv->ofdm_index[0] = i; + } + + tmp_cck = rtl92e_get_bb_reg(dev, rCCK0_TxFilter1, bMaskByte2); + for (i = 0; i < CCK_TABLE_LEN; i++) { + if (tmp_cck == (u32)CCKSwingTable_Ch1_Ch13[i][0]) { + priv->cck_index = i; + break; + } + } + priv->tx_pwr_tracking_init = true; + return; + } + + tmp_reg = rtl92e_get_rf_reg(dev, RF90_PATH_A, 0x12, 0x078); + if (tmp_reg < 3 || tmp_reg > 13) + return; + if (tmp_reg >= 12) + tmp_reg = 12; + priv->thermal_meter[0] = ThermalMeterVal; + priv->thermal_meter[1] = ThermalMeterVal; + + if (priv->thermal_meter[0] >= (u8)tmp_reg) { + tmp_ofdm_index = 6 + (priv->thermal_meter[0] - (u8)tmp_reg); + tmp_cck_20m_index = tmp_ofdm_index; + tmp_cck_40m_index = tmp_cck_20m_index - 6; + if (tmp_ofdm_index >= OFDM_TABLE_LEN) + tmp_ofdm_index = OFDM_TABLE_LEN - 1; + if (tmp_cck_20m_index >= CCK_TABLE_LEN) + tmp_cck_20m_index = CCK_TABLE_LEN - 1; + if (tmp_cck_40m_index >= CCK_TABLE_LEN) + tmp_cck_40m_index = CCK_TABLE_LEN - 1; + } else { + tmpval = (u8)tmp_reg - priv->thermal_meter[0]; + if (tmpval >= 6) { + tmp_ofdm_index = 0; + tmp_cck_20m_index = 0; + } else { + tmp_ofdm_index = 6 - tmpval; + tmp_cck_20m_index = 6 - tmpval; + } + tmp_cck_40m_index = 0; + } + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + tmp_cck_index = tmp_cck_40m_index; + else + tmp_cck_index = tmp_cck_20m_index; + + priv->rec_cck_20m_idx = tmp_cck_20m_index; + priv->rec_cck_40m_idx = tmp_cck_40m_index; + + if (priv->rtllib->current_network.channel == 14 && + !priv->bcck_in_ch14) { + priv->bcck_in_ch14 = true; + CCKSwingNeedUpdate = 1; + } else if (priv->rtllib->current_network.channel != 14 && + priv->bcck_in_ch14) { + priv->bcck_in_ch14 = false; + CCKSwingNeedUpdate = 1; + } + + if (priv->cck_index != tmp_cck_index) { + priv->cck_index = tmp_cck_index; + CCKSwingNeedUpdate = 1; + } + + if (CCKSwingNeedUpdate) + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + if (priv->ofdm_index[0] != tmp_ofdm_index) { + priv->ofdm_index[0] = tmp_ofdm_index; + rtl92e_set_bb_reg(dev, rOFDM0_XATxIQImbalance, bMaskDWord, + OFDMSwingTable[priv->ofdm_index[0]]); + } + priv->txpower_count = 0; +} + +void rtl92e_dm_txpower_tracking_wq(void *data) +{ + struct r8192_priv *priv = container_of_dwork_rsl(data, + struct r8192_priv, txpower_tracking_wq); + struct net_device *dev = priv->rtllib->dev; + + if (priv->ic_cut >= IC_VersionCut_D) + _rtl92e_dm_tx_power_tracking_callback_tssi(dev); + else + _rtl92e_dm_tx_power_tracking_cb_thermal(dev); +} + +static void _rtl92e_dm_initialize_tx_power_tracking_tssi(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->btxpower_tracking = true; + priv->txpower_count = 0; + priv->tx_pwr_tracking_init = false; +} + +static void _rtl92e_dm_init_tx_power_tracking_thermal(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->rtllib->FwRWRF) + priv->btxpower_tracking = true; + else + priv->btxpower_tracking = false; + priv->txpower_count = 0; + priv->tx_pwr_tracking_init = false; +} + +void rtl92e_dm_init_txpower_tracking(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->ic_cut >= IC_VersionCut_D) + _rtl92e_dm_initialize_tx_power_tracking_tssi(dev); + else + _rtl92e_dm_init_tx_power_tracking_thermal(dev); +} + +static void _rtl92e_dm_check_tx_power_tracking_tssi(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + static u32 tx_power_track_counter; + + if (rtl92e_readb(dev, 0x11e) == 1) + return; + if (!priv->btxpower_tracking) + return; + tx_power_track_counter++; + + if (tx_power_track_counter >= 180) { + schedule_delayed_work(&priv->txpower_tracking_wq, 0); + tx_power_track_counter = 0; + } +} + +static void _rtl92e_dm_check_tx_power_tracking_thermal(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + static u8 TM_Trigger; + u8 TxPowerCheckCnt = 0; + + TxPowerCheckCnt = 2; + if (!priv->btxpower_tracking) + return; + + if (priv->txpower_count <= TxPowerCheckCnt) { + priv->txpower_count++; + return; + } + + if (!TM_Trigger) { + rtl92e_set_rf_reg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4d); + rtl92e_set_rf_reg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4f); + rtl92e_set_rf_reg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4d); + rtl92e_set_rf_reg(dev, RF90_PATH_A, 0x02, bMask12Bits, 0x4f); + TM_Trigger = 1; + return; + } + netdev_info(dev, "===============>Schedule TxPowerTrackingWorkItem\n"); + schedule_delayed_work(&priv->txpower_tracking_wq, 0); + TM_Trigger = 0; +} + +static void _rtl92e_dm_check_tx_power_tracking(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->ic_cut >= IC_VersionCut_D) + _rtl92e_dm_check_tx_power_tracking_tssi(dev); + else + _rtl92e_dm_check_tx_power_tracking_thermal(dev); +} + +static void _rtl92e_dm_cck_tx_power_adjust_tssi(struct net_device *dev, + bool bInCH14) +{ + u32 TempVal; + struct r8192_priv *priv = rtllib_priv(dev); + u8 attenuation = priv->cck_present_attn; + + TempVal = 0; + if (!bInCH14) { + TempVal = (u32)(dm_cck_tx_bb_gain[attenuation][0] + + (dm_cck_tx_bb_gain[attenuation][1] << 8)); + + rtl92e_set_bb_reg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal); + TempVal = (u32)((dm_cck_tx_bb_gain[attenuation][2]) + + (dm_cck_tx_bb_gain[attenuation][3] << 8) + + (dm_cck_tx_bb_gain[attenuation][4] << 16) + + (dm_cck_tx_bb_gain[attenuation][5] << 24)); + rtl92e_set_bb_reg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal); + TempVal = (u32)(dm_cck_tx_bb_gain[attenuation][6] + + (dm_cck_tx_bb_gain[attenuation][7] << 8)); + + rtl92e_set_bb_reg(dev, rCCK0_DebugPort, bMaskLWord, TempVal); + } else { + TempVal = (u32)((dm_cck_tx_bb_gain_ch14[attenuation][0]) + + (dm_cck_tx_bb_gain_ch14[attenuation][1] << 8)); + + rtl92e_set_bb_reg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal); + TempVal = (u32)((dm_cck_tx_bb_gain_ch14[attenuation][2]) + + (dm_cck_tx_bb_gain_ch14[attenuation][3] << 8) + + (dm_cck_tx_bb_gain_ch14[attenuation][4] << 16) + + (dm_cck_tx_bb_gain_ch14[attenuation][5] << 24)); + rtl92e_set_bb_reg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal); + TempVal = (u32)((dm_cck_tx_bb_gain_ch14[attenuation][6]) + + (dm_cck_tx_bb_gain_ch14[attenuation][7] << 8)); + + rtl92e_set_bb_reg(dev, rCCK0_DebugPort, bMaskLWord, TempVal); + } +} + +static void _rtl92e_dm_cck_tx_power_adjust_thermal_meter(struct net_device *dev, + bool bInCH14) +{ + u32 TempVal; + struct r8192_priv *priv = rtllib_priv(dev); + + TempVal = 0; + if (!bInCH14) { + TempVal = CCKSwingTable_Ch1_Ch13[priv->cck_index][0] + + (CCKSwingTable_Ch1_Ch13[priv->cck_index][1] << 8); + rtl92e_set_bb_reg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal); + TempVal = CCKSwingTable_Ch1_Ch13[priv->cck_index][2] + + (CCKSwingTable_Ch1_Ch13[priv->cck_index][3] << 8) + + (CCKSwingTable_Ch1_Ch13[priv->cck_index][4] << 16) + + (CCKSwingTable_Ch1_Ch13[priv->cck_index][5] << 24); + rtl92e_set_bb_reg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal); + TempVal = CCKSwingTable_Ch1_Ch13[priv->cck_index][6] + + (CCKSwingTable_Ch1_Ch13[priv->cck_index][7] << 8); + + rtl92e_set_bb_reg(dev, rCCK0_DebugPort, bMaskLWord, TempVal); + } else { + TempVal = CCKSwingTable_Ch14[priv->cck_index][0] + + (CCKSwingTable_Ch14[priv->cck_index][1] << 8); + + rtl92e_set_bb_reg(dev, rCCK0_TxFilter1, bMaskHWord, TempVal); + TempVal = CCKSwingTable_Ch14[priv->cck_index][2] + + (CCKSwingTable_Ch14[priv->cck_index][3] << 8) + + (CCKSwingTable_Ch14[priv->cck_index][4] << 16) + + (CCKSwingTable_Ch14[priv->cck_index][5] << 24); + rtl92e_set_bb_reg(dev, rCCK0_TxFilter2, bMaskDWord, TempVal); + TempVal = CCKSwingTable_Ch14[priv->cck_index][6] + + (CCKSwingTable_Ch14[priv->cck_index][7] << 8); + + rtl92e_set_bb_reg(dev, rCCK0_DebugPort, bMaskLWord, TempVal); + } +} + +void rtl92e_dm_cck_txpower_adjust(struct net_device *dev, bool binch14) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->ic_cut >= IC_VersionCut_D) + _rtl92e_dm_cck_tx_power_adjust_tssi(dev, binch14); + else + _rtl92e_dm_cck_tx_power_adjust_thermal_meter(dev, binch14); +} + +static void _rtl92e_dm_tx_power_reset_recovery(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + rtl92e_set_bb_reg(dev, rOFDM0_XATxIQImbalance, bMaskDWord, + dm_tx_bb_gain[priv->rfa_txpowertrackingindex]); + rtl92e_dm_cck_txpower_adjust(dev, priv->bcck_in_ch14); + + rtl92e_set_bb_reg(dev, rOFDM0_XCTxIQImbalance, bMaskDWord, + dm_tx_bb_gain[priv->rfc_txpowertrackingindex]); +} + +void rtl92e_dm_restore_state(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 reg_ratr = priv->rate_adaptive.last_ratr; + u32 ratr_value; + + if (!priv->up) + return; + + if (priv->rate_adaptive.rate_adaptive_disabled) + return; + if (priv->rtllib->mode != WIRELESS_MODE_N_24G) + return; + ratr_value = reg_ratr; + ratr_value &= ~(RATE_ALL_OFDM_2SS); + rtl92e_writel(dev, RATR0, ratr_value); + rtl92e_writeb(dev, UFWP, 1); + if (priv->tx_pwr_tracking_init && priv->btxpower_tracking) + _rtl92e_dm_tx_power_reset_recovery(dev); + + _rtl92e_dm_bb_initialgain_restore(dev); +} + +static void _rtl92e_dm_bb_initialgain_restore(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 bit_mask = 0x7f; + + if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI) + return; + + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x8); + rtl92e_set_bb_reg(dev, rOFDM0_XAAGCCore1, bit_mask, + (u32)priv->initgain_backup.xaagccore1); + rtl92e_set_bb_reg(dev, rOFDM0_XBAGCCore1, bit_mask, + (u32)priv->initgain_backup.xbagccore1); + rtl92e_set_bb_reg(dev, rOFDM0_XCAGCCore1, bit_mask, + (u32)priv->initgain_backup.xcagccore1); + rtl92e_set_bb_reg(dev, rOFDM0_XDAGCCore1, bit_mask, + (u32)priv->initgain_backup.xdagccore1); + bit_mask = bMaskByte2; + rtl92e_set_bb_reg(dev, rCCK0_CCA, bit_mask, + (u32)priv->initgain_backup.cca); + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x1); +} + +void rtl92e_dm_backup_state(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 bit_mask = bMaskByte0; + + priv->bswitch_fsync = false; + + if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI) + return; + + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x8); + priv->initgain_backup.xaagccore1 = rtl92e_get_bb_reg(dev, rOFDM0_XAAGCCore1, bit_mask); + priv->initgain_backup.xbagccore1 = rtl92e_get_bb_reg(dev, rOFDM0_XBAGCCore1, bit_mask); + priv->initgain_backup.xcagccore1 = rtl92e_get_bb_reg(dev, rOFDM0_XCAGCCore1, bit_mask); + priv->initgain_backup.xdagccore1 = rtl92e_get_bb_reg(dev, rOFDM0_XDAGCCore1, bit_mask); + bit_mask = bMaskByte2; + priv->initgain_backup.cca = (u8)rtl92e_get_bb_reg(dev, rCCK0_CCA, bit_mask); +} + +static void _rtl92e_dm_dig_init(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + dm_digtable.dig_enable_flag = true; + + dm_digtable.dig_algorithm = DIG_ALGO_BY_RSSI; + + dm_digtable.dig_algorithm_switch = 0; + + dm_digtable.dig_state = DM_STA_DIG_MAX; + dm_digtable.dig_highpwr_state = DM_STA_DIG_MAX; + dm_digtable.cur_sta_connect_state = DIG_STA_DISCONNECT; + dm_digtable.pre_sta_connect_state = DIG_STA_DISCONNECT; + + dm_digtable.rssi_low_thresh = DM_DIG_THRESH_LOW; + dm_digtable.rssi_high_thresh = DM_DIG_THRESH_HIGH; + + dm_digtable.rssi_high_power_lowthresh = DM_DIG_HIGH_PWR_THRESH_LOW; + dm_digtable.rssi_high_power_highthresh = DM_DIG_HIGH_PWR_THRESH_HIGH; + + dm_digtable.rssi_val = 50; + dm_digtable.backoff_val = DM_DIG_BACKOFF; + dm_digtable.rx_gain_range_max = DM_DIG_MAX; + if (priv->customer_id == RT_CID_819X_NETCORE) + dm_digtable.rx_gain_range_min = DM_DIG_MIN_Netcore; + else + dm_digtable.rx_gain_range_min = DM_DIG_MIN; +} + +static void _rtl92e_dm_ctrl_initgain_byrssi(struct net_device *dev) +{ + if (!dm_digtable.dig_enable_flag) + return; + + if (dm_digtable.dig_algorithm == DIG_ALGO_BY_FALSE_ALARM) + _rtl92e_dm_ctrl_initgain_byrssi_false_alarm(dev); + else if (dm_digtable.dig_algorithm == DIG_ALGO_BY_RSSI) + _rtl92e_dm_ctrl_initgain_byrssi_driver(dev); + else + return; +} + +/*----------------------------------------------------------------------------- + * Function: dm_CtrlInitGainBeforeConnectByRssiAndFalseAlarm() + * + * Overview: Driver monitor RSSI and False Alarm to change initial gain. + Only change initial gain during link in progress. + * + * Input: IN PADAPTER pAdapter + * + * Output: NONE + * + * Return: NONE + * + * Revised History: + * When Who Remark + * 03/04/2009 hpfan Create Version 0. + * + ******************************************************************************/ + +static void _rtl92e_dm_ctrl_initgain_byrssi_driver(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 i; + static u8 fw_dig; + + if (!dm_digtable.dig_enable_flag) + return; + + if (dm_digtable.dig_algorithm_switch) + fw_dig = 0; + if (fw_dig <= 3) { + for (i = 0; i < 3; i++) + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x8); + fw_dig++; + dm_digtable.dig_state = DM_STA_DIG_OFF; + } + + if (priv->rtllib->link_state == MAC80211_LINKED) + dm_digtable.cur_sta_connect_state = DIG_STA_CONNECT; + else + dm_digtable.cur_sta_connect_state = DIG_STA_DISCONNECT; + + dm_digtable.rssi_val = priv->undecorated_smoothed_pwdb; + _rtl92e_dm_initial_gain(dev); + _rtl92e_dm_pd_th(dev); + _rtl92e_dm_cs_ratio(dev); + if (dm_digtable.dig_algorithm_switch) + dm_digtable.dig_algorithm_switch = 0; + dm_digtable.pre_sta_connect_state = dm_digtable.cur_sta_connect_state; +} + +static void _rtl92e_dm_ctrl_initgain_byrssi_false_alarm(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + static u32 reset_cnt; + u8 i; + + if (!dm_digtable.dig_enable_flag) + return; + + if (dm_digtable.dig_algorithm_switch) { + dm_digtable.dig_state = DM_STA_DIG_MAX; + for (i = 0; i < 3; i++) + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x1); + dm_digtable.dig_algorithm_switch = 0; + } + + if (priv->rtllib->link_state != MAC80211_LINKED) + return; + + if ((priv->undecorated_smoothed_pwdb > dm_digtable.rssi_low_thresh) && + (priv->undecorated_smoothed_pwdb < dm_digtable.rssi_high_thresh)) + return; + if (priv->undecorated_smoothed_pwdb <= dm_digtable.rssi_low_thresh) { + if (dm_digtable.dig_state == DM_STA_DIG_OFF && + (priv->reset_count == reset_cnt)) + return; + reset_cnt = priv->reset_count; + + dm_digtable.dig_highpwr_state = DM_STA_DIG_MAX; + dm_digtable.dig_state = DM_STA_DIG_OFF; + + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x8); + + rtl92e_writeb(dev, rOFDM0_XAAGCCore1, 0x17); + rtl92e_writeb(dev, rOFDM0_XBAGCCore1, 0x17); + rtl92e_writeb(dev, rOFDM0_XCAGCCore1, 0x17); + rtl92e_writeb(dev, rOFDM0_XDAGCCore1, 0x17); + + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + rtl92e_writeb(dev, (rOFDM0_XATxAFE + 3), 0x00); + else + rtl92e_writeb(dev, rOFDM0_RxDetector1, 0x42); + + rtl92e_writeb(dev, 0xa0a, 0x08); + + return; + } + + if (priv->undecorated_smoothed_pwdb >= dm_digtable.rssi_high_thresh) { + u8 reset_flag = 0; + + if (dm_digtable.dig_state == DM_STA_DIG_ON && + (priv->reset_count == reset_cnt)) { + _rtl92e_dm_ctrl_initgain_byrssi_highpwr(dev); + return; + } + if (priv->reset_count != reset_cnt) + reset_flag = 1; + + reset_cnt = priv->reset_count; + + dm_digtable.dig_state = DM_STA_DIG_ON; + + if (reset_flag == 1) { + rtl92e_writeb(dev, rOFDM0_XAAGCCore1, 0x2c); + rtl92e_writeb(dev, rOFDM0_XBAGCCore1, 0x2c); + rtl92e_writeb(dev, rOFDM0_XCAGCCore1, 0x2c); + rtl92e_writeb(dev, rOFDM0_XDAGCCore1, 0x2c); + } else { + rtl92e_writeb(dev, rOFDM0_XAAGCCore1, 0x20); + rtl92e_writeb(dev, rOFDM0_XBAGCCore1, 0x20); + rtl92e_writeb(dev, rOFDM0_XCAGCCore1, 0x20); + rtl92e_writeb(dev, rOFDM0_XDAGCCore1, 0x20); + } + + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + rtl92e_writeb(dev, (rOFDM0_XATxAFE + 3), 0x20); + else + rtl92e_writeb(dev, rOFDM0_RxDetector1, 0x44); + + rtl92e_writeb(dev, 0xa0a, 0xcd); + + rtl92e_set_bb_reg(dev, UFWP, bMaskByte1, 0x1); + } + _rtl92e_dm_ctrl_initgain_byrssi_highpwr(dev); +} + +static void _rtl92e_dm_ctrl_initgain_byrssi_highpwr(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + static u32 reset_cnt_highpwr; + + if ((priv->undecorated_smoothed_pwdb > + dm_digtable.rssi_high_power_lowthresh) && + (priv->undecorated_smoothed_pwdb < + dm_digtable.rssi_high_power_highthresh)) + return; + + if (priv->undecorated_smoothed_pwdb >= + dm_digtable.rssi_high_power_highthresh) { + if (dm_digtable.dig_highpwr_state == DM_STA_DIG_ON && + (priv->reset_count == reset_cnt_highpwr)) + return; + dm_digtable.dig_highpwr_state = DM_STA_DIG_ON; + + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + rtl92e_writeb(dev, (rOFDM0_XATxAFE + 3), 0x10); + else + rtl92e_writeb(dev, rOFDM0_RxDetector1, 0x43); + } else { + if (dm_digtable.dig_highpwr_state == DM_STA_DIG_OFF && + (priv->reset_count == reset_cnt_highpwr)) + return; + dm_digtable.dig_highpwr_state = DM_STA_DIG_OFF; + + if ((priv->undecorated_smoothed_pwdb < + dm_digtable.rssi_high_power_lowthresh) && + (priv->undecorated_smoothed_pwdb >= + dm_digtable.rssi_high_thresh)) { + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + rtl92e_writeb(dev, (rOFDM0_XATxAFE + 3), 0x20); + else + rtl92e_writeb(dev, rOFDM0_RxDetector1, 0x44); + } + } + reset_cnt_highpwr = priv->reset_count; +} + +static void _rtl92e_dm_initial_gain(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 initial_gain = 0; + static u8 initialized, force_write; + static u32 reset_cnt; + + if (dm_digtable.dig_algorithm_switch) { + initialized = 0; + reset_cnt = 0; + } + + if (rtllib_act_scanning(priv->rtllib, true)) { + force_write = 1; + return; + } + + if (dm_digtable.pre_sta_connect_state == dm_digtable.cur_sta_connect_state) { + if (dm_digtable.cur_sta_connect_state == DIG_STA_CONNECT) { + long gain_range = dm_digtable.rssi_val + 10 - + dm_digtable.backoff_val; + gain_range = clamp_t(long, gain_range, + dm_digtable.rx_gain_range_min, + dm_digtable.rx_gain_range_max); + dm_digtable.cur_ig_value = gain_range; + } else { + if (dm_digtable.cur_ig_value == 0) + dm_digtable.cur_ig_value = priv->def_initial_gain[0]; + else + dm_digtable.cur_ig_value = dm_digtable.pre_ig_value; + } + } else { + dm_digtable.cur_ig_value = priv->def_initial_gain[0]; + dm_digtable.pre_ig_value = 0; + } + + if (priv->reset_count != reset_cnt) { + force_write = 1; + reset_cnt = priv->reset_count; + } + + if (dm_digtable.pre_ig_value != rtl92e_readb(dev, rOFDM0_XAAGCCore1)) + force_write = 1; + + if ((dm_digtable.pre_ig_value != dm_digtable.cur_ig_value) + || !initialized || force_write) { + initial_gain = dm_digtable.cur_ig_value; + rtl92e_writeb(dev, rOFDM0_XAAGCCore1, initial_gain); + rtl92e_writeb(dev, rOFDM0_XBAGCCore1, initial_gain); + rtl92e_writeb(dev, rOFDM0_XCAGCCore1, initial_gain); + rtl92e_writeb(dev, rOFDM0_XDAGCCore1, initial_gain); + dm_digtable.pre_ig_value = dm_digtable.cur_ig_value; + initialized = 1; + force_write = 0; + } +} + +static void _rtl92e_dm_pd_th(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + static u8 initialized, force_write; + static u32 reset_cnt; + + if (dm_digtable.dig_algorithm_switch) { + initialized = 0; + reset_cnt = 0; + } + + if (dm_digtable.pre_sta_connect_state == dm_digtable.cur_sta_connect_state) { + if (dm_digtable.cur_sta_connect_state == DIG_STA_CONNECT) { + if (dm_digtable.rssi_val >= + dm_digtable.rssi_high_power_highthresh) + dm_digtable.curpd_thstate = + DIG_PD_AT_HIGH_POWER; + else if (dm_digtable.rssi_val <= + dm_digtable.rssi_low_thresh) + dm_digtable.curpd_thstate = + DIG_PD_AT_LOW_POWER; + else if ((dm_digtable.rssi_val >= + dm_digtable.rssi_high_thresh) && + (dm_digtable.rssi_val < + dm_digtable.rssi_high_power_lowthresh)) + dm_digtable.curpd_thstate = + DIG_PD_AT_NORMAL_POWER; + else + dm_digtable.curpd_thstate = + dm_digtable.prepd_thstate; + } else { + dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER; + } + } else { + dm_digtable.curpd_thstate = DIG_PD_AT_LOW_POWER; + } + + if (priv->reset_count != reset_cnt) { + force_write = 1; + reset_cnt = priv->reset_count; + } + + if ((dm_digtable.prepd_thstate != dm_digtable.curpd_thstate) || + (initialized <= 3) || force_write) { + if (dm_digtable.curpd_thstate == DIG_PD_AT_LOW_POWER) { + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + rtl92e_writeb(dev, (rOFDM0_XATxAFE + 3), 0x00); + else + rtl92e_writeb(dev, rOFDM0_RxDetector1, 0x42); + } else if (dm_digtable.curpd_thstate == + DIG_PD_AT_NORMAL_POWER) { + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + rtl92e_writeb(dev, (rOFDM0_XATxAFE + 3), 0x20); + else + rtl92e_writeb(dev, rOFDM0_RxDetector1, 0x44); + } else if (dm_digtable.curpd_thstate == DIG_PD_AT_HIGH_POWER) { + if (priv->current_chnl_bw != HT_CHANNEL_WIDTH_20) + rtl92e_writeb(dev, (rOFDM0_XATxAFE + 3), 0x10); + else + rtl92e_writeb(dev, rOFDM0_RxDetector1, 0x43); + } + dm_digtable.prepd_thstate = dm_digtable.curpd_thstate; + if (initialized <= 3) + initialized++; + force_write = 0; + } +} + +static void _rtl92e_dm_cs_ratio(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + static u8 initialized, force_write; + static u32 reset_cnt; + + if (dm_digtable.dig_algorithm_switch) { + initialized = 0; + reset_cnt = 0; + } + + if (dm_digtable.pre_sta_connect_state == dm_digtable.cur_sta_connect_state) { + if (dm_digtable.cur_sta_connect_state == DIG_STA_CONNECT) { + if (dm_digtable.rssi_val <= dm_digtable.rssi_low_thresh) + dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER; + else if (dm_digtable.rssi_val >= dm_digtable.rssi_high_thresh) + dm_digtable.curcs_ratio_state = DIG_CS_RATIO_HIGHER; + else + dm_digtable.curcs_ratio_state = dm_digtable.precs_ratio_state; + } else { + dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER; + } + } else { + dm_digtable.curcs_ratio_state = DIG_CS_RATIO_LOWER; + } + + if (priv->reset_count != reset_cnt) { + force_write = 1; + reset_cnt = priv->reset_count; + } + + if ((dm_digtable.precs_ratio_state != dm_digtable.curcs_ratio_state) || + !initialized || force_write) { + if (dm_digtable.curcs_ratio_state == DIG_CS_RATIO_LOWER) + rtl92e_writeb(dev, 0xa0a, 0x08); + else if (dm_digtable.curcs_ratio_state == DIG_CS_RATIO_HIGHER) + rtl92e_writeb(dev, 0xa0a, 0xcd); + dm_digtable.precs_ratio_state = dm_digtable.curcs_ratio_state; + initialized = 1; + force_write = 0; + } +} + +void rtl92e_dm_init_edca_turbo(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->bcurrent_turbo_EDCA = false; + priv->rtllib->bis_any_nonbepkts = false; + priv->bis_cur_rdlstate = false; +} + +static void _rtl92e_dm_check_edca_turbo(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_hi_throughput *ht_info = priv->rtllib->ht_info; + + static unsigned long lastTxOkCnt; + static unsigned long lastRxOkCnt; + unsigned long curTxOkCnt = 0; + unsigned long curRxOkCnt = 0; + + if (priv->rtllib->iw_mode == IW_MODE_ADHOC) + goto dm_CheckEdcaTurbo_EXIT; + if (priv->rtllib->link_state != MAC80211_LINKED) + goto dm_CheckEdcaTurbo_EXIT; + if (priv->rtllib->ht_info->iot_action & HT_IOT_ACT_DISABLE_EDCA_TURBO) + goto dm_CheckEdcaTurbo_EXIT; + + if (!priv->rtllib->bis_any_nonbepkts) { + curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt; + curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt; + if (ht_info->iot_action & HT_IOT_ACT_EDCA_BIAS_ON_RX) { + if (curTxOkCnt > 4 * curRxOkCnt) { + if (priv->bis_cur_rdlstate || + !priv->bcurrent_turbo_EDCA) { + rtl92e_writel(dev, EDCAPARA_BE, + edca_setting_UL[ht_info->IOTPeer]); + priv->bis_cur_rdlstate = false; + } + } else { + if (!priv->bis_cur_rdlstate || + !priv->bcurrent_turbo_EDCA) { + if (priv->rtllib->mode == WIRELESS_MODE_G) + rtl92e_writel(dev, EDCAPARA_BE, + edca_setting_DL_GMode[ht_info->IOTPeer]); + else + rtl92e_writel(dev, EDCAPARA_BE, + edca_setting_DL[ht_info->IOTPeer]); + priv->bis_cur_rdlstate = true; + } + } + priv->bcurrent_turbo_EDCA = true; + } else { + if (curRxOkCnt > 4 * curTxOkCnt) { + if (!priv->bis_cur_rdlstate || + !priv->bcurrent_turbo_EDCA) { + if (priv->rtllib->mode == WIRELESS_MODE_G) + rtl92e_writel(dev, EDCAPARA_BE, + edca_setting_DL_GMode[ht_info->IOTPeer]); + else + rtl92e_writel(dev, EDCAPARA_BE, + edca_setting_DL[ht_info->IOTPeer]); + priv->bis_cur_rdlstate = true; + } + } else { + if (priv->bis_cur_rdlstate || + !priv->bcurrent_turbo_EDCA) { + rtl92e_writel(dev, EDCAPARA_BE, + edca_setting_UL[ht_info->IOTPeer]); + priv->bis_cur_rdlstate = false; + } + } + + priv->bcurrent_turbo_EDCA = true; + } + } else { + if (priv->bcurrent_turbo_EDCA) { + u8 tmp = AC0_BE; + + priv->rtllib->SetHwRegHandler(dev, HW_VAR_AC_PARAM, + (u8 *)(&tmp)); + priv->bcurrent_turbo_EDCA = false; + } + } + +dm_CheckEdcaTurbo_EXIT: + priv->rtllib->bis_any_nonbepkts = false; + lastTxOkCnt = priv->stats.txbytesunicast; + lastRxOkCnt = priv->stats.rxbytesunicast; +} + +static void _rtl92e_dm_init_cts_to_self(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv((struct net_device *)dev); + + priv->rtllib->bCTSToSelfEnable = true; +} + +static void _rtl92e_dm_cts_to_self(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv((struct net_device *)dev); + struct rt_hi_throughput *ht_info = priv->rtllib->ht_info; + static unsigned long lastTxOkCnt; + static unsigned long lastRxOkCnt; + unsigned long curTxOkCnt = 0; + unsigned long curRxOkCnt = 0; + + if (!priv->rtllib->bCTSToSelfEnable) { + ht_info->iot_action &= ~HT_IOT_ACT_FORCED_CTS2SELF; + return; + } + if (ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) { + curTxOkCnt = priv->stats.txbytesunicast - lastTxOkCnt; + curRxOkCnt = priv->stats.rxbytesunicast - lastRxOkCnt; + if (curRxOkCnt > 4 * curTxOkCnt) + ht_info->iot_action &= ~HT_IOT_ACT_FORCED_CTS2SELF; + else + ht_info->iot_action |= HT_IOT_ACT_FORCED_CTS2SELF; + + lastTxOkCnt = priv->stats.txbytesunicast; + lastRxOkCnt = priv->stats.rxbytesunicast; + } +} + +static void _rtl92e_dm_check_rf_ctrl_gpio(void *data) +{ + struct r8192_priv *priv = container_of_dwork_rsl(data, + struct r8192_priv, gpio_change_rf_wq); + struct net_device *dev = priv->rtllib->dev; + u8 tmp1byte; + enum rt_rf_power_state rf_power_state_to_set; + bool bActuallySet = false; + + if ((priv->up_first_time == 1) || (priv->being_init_adapter)) + return; + + if (priv->bfirst_after_down) + return; + + tmp1byte = rtl92e_readb(dev, GPI); + + rf_power_state_to_set = (tmp1byte & BIT1) ? rf_on : rf_off; + + if (priv->hw_radio_off && (rf_power_state_to_set == rf_on)) { + netdev_info(dev, "gpiochangeRF - HW Radio ON\n"); + priv->hw_radio_off = false; + bActuallySet = true; + } else if (!priv->hw_radio_off && (rf_power_state_to_set == rf_off)) { + netdev_info(dev, "gpiochangeRF - HW Radio OFF\n"); + priv->hw_radio_off = true; + bActuallySet = true; + } + + if (bActuallySet) { + mdelay(1000); + priv->hw_rf_off_action = 1; + rtl92e_set_rf_state(dev, rf_power_state_to_set, RF_CHANGE_BY_HW); + } +} + +void rtl92e_dm_rf_pathcheck_wq(void *data) +{ + struct r8192_priv *priv = container_of_dwork_rsl(data, + struct r8192_priv, + rfpath_check_wq); + struct net_device *dev = priv->rtllib->dev; + u8 rfpath, i; + + rfpath = rtl92e_readb(dev, 0xc04); + + for (i = 0; i < RF90_PATH_MAX; i++) { + if (rfpath & (0x01 << i)) + priv->brfpath_rxenable[i] = true; + else + priv->brfpath_rxenable[i] = false; + } + if (!dm_rx_path_sel_table.enable) + return; + + _rtl92e_dm_rx_path_sel_byrssi(dev); +} + +static void _rtl92e_dm_init_rx_path_selection(struct net_device *dev) +{ + u8 i; + struct r8192_priv *priv = rtllib_priv(dev); + + dm_rx_path_sel_table.enable = 1; + dm_rx_path_sel_table.ss_th_low = RX_PATH_SEL_SS_TH_LOW; + dm_rx_path_sel_table.diff_th = RX_PATH_SEL_DIFF_TH; + if (priv->customer_id == RT_CID_819X_NETCORE) + dm_rx_path_sel_table.cck_method = CCK_Rx_Version_2; + else + dm_rx_path_sel_table.cck_method = CCK_Rx_Version_1; + dm_rx_path_sel_table.disabled_rf = 0; + for (i = 0; i < 4; i++) { + dm_rx_path_sel_table.rf_rssi[i] = 50; + dm_rx_path_sel_table.cck_pwdb_sta[i] = -64; + dm_rx_path_sel_table.rf_enable_rssi_th[i] = 100; + } +} + +#define PWDB_IN_RANGE ((cur_cck_pwdb < tmp_cck_max_pwdb) && \ + (cur_cck_pwdb > tmp_cck_sec_pwdb)) + +static void _rtl92e_dm_rx_path_sel_byrssi(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 i, max_rssi_index = 0, min_rssi_index = 0; + u8 sec_rssi_index = 0, rf_num = 0; + u8 tmp_max_rssi = 0, tmp_min_rssi = 0, tmp_sec_rssi = 0; + u8 cck_default_Rx = 0x2; + u8 cck_optional_Rx = 0x3; + long tmp_cck_max_pwdb = 0, tmp_cck_min_pwdb = 0, tmp_cck_sec_pwdb = 0; + u8 cck_rx_ver2_max_index = 0; + u8 cck_rx_ver2_sec_index = 0; + u8 cur_rf_rssi; + long cur_cck_pwdb; + static u8 disabled_rf_cnt, cck_Rx_Path_initialized; + u8 update_cck_rx_path; + + if (!cck_Rx_Path_initialized) { + dm_rx_path_sel_table.cck_rx_path = (rtl92e_readb(dev, 0xa07) & 0xf); + cck_Rx_Path_initialized = 1; + } + + dm_rx_path_sel_table.disabled_rf = 0xf; + dm_rx_path_sel_table.disabled_rf &= ~(rtl92e_readb(dev, 0xc04)); + + if (priv->rtllib->mode == WIRELESS_MODE_B) + dm_rx_path_sel_table.cck_method = CCK_Rx_Version_2; + + for (i = 0; i < RF90_PATH_MAX; i++) { + dm_rx_path_sel_table.rf_rssi[i] = priv->stats.rx_rssi_percentage[i]; + + if (priv->brfpath_rxenable[i]) { + rf_num++; + cur_rf_rssi = dm_rx_path_sel_table.rf_rssi[i]; + + if (rf_num == 1) { + max_rssi_index = min_rssi_index = sec_rssi_index = i; + tmp_max_rssi = tmp_min_rssi = tmp_sec_rssi = cur_rf_rssi; + } else if (rf_num == 2) { + if (cur_rf_rssi >= tmp_max_rssi) { + tmp_max_rssi = cur_rf_rssi; + max_rssi_index = i; + } else { + tmp_sec_rssi = tmp_min_rssi = cur_rf_rssi; + sec_rssi_index = min_rssi_index = i; + } + } else { + if (cur_rf_rssi > tmp_max_rssi) { + tmp_sec_rssi = tmp_max_rssi; + sec_rssi_index = max_rssi_index; + tmp_max_rssi = cur_rf_rssi; + max_rssi_index = i; + } else if (cur_rf_rssi == tmp_max_rssi) { + tmp_sec_rssi = cur_rf_rssi; + sec_rssi_index = i; + } else if ((cur_rf_rssi < tmp_max_rssi) && + (cur_rf_rssi > tmp_sec_rssi)) { + tmp_sec_rssi = cur_rf_rssi; + sec_rssi_index = i; + } else if (cur_rf_rssi == tmp_sec_rssi) { + if (tmp_sec_rssi == tmp_min_rssi) { + tmp_sec_rssi = cur_rf_rssi; + sec_rssi_index = i; + } + } else if ((cur_rf_rssi < tmp_sec_rssi) && + (cur_rf_rssi > tmp_min_rssi)) { + ; + } else if (cur_rf_rssi == tmp_min_rssi) { + if (tmp_sec_rssi == tmp_min_rssi) { + tmp_min_rssi = cur_rf_rssi; + min_rssi_index = i; + } + } else if (cur_rf_rssi < tmp_min_rssi) { + tmp_min_rssi = cur_rf_rssi; + min_rssi_index = i; + } + } + } + } + + rf_num = 0; + if (dm_rx_path_sel_table.cck_method == CCK_Rx_Version_2) { + for (i = 0; i < RF90_PATH_MAX; i++) { + if (priv->brfpath_rxenable[i]) { + rf_num++; + cur_cck_pwdb = + dm_rx_path_sel_table.cck_pwdb_sta[i]; + + if (rf_num == 1) { + cck_rx_ver2_max_index = i; + cck_rx_ver2_sec_index = i; + tmp_cck_max_pwdb = cur_cck_pwdb; + tmp_cck_min_pwdb = cur_cck_pwdb; + tmp_cck_sec_pwdb = cur_cck_pwdb; + } else if (rf_num == 2) { + if (cur_cck_pwdb >= tmp_cck_max_pwdb) { + tmp_cck_max_pwdb = cur_cck_pwdb; + cck_rx_ver2_max_index = i; + } else { + tmp_cck_sec_pwdb = cur_cck_pwdb; + tmp_cck_min_pwdb = cur_cck_pwdb; + cck_rx_ver2_sec_index = i; + } + } else { + if (cur_cck_pwdb > tmp_cck_max_pwdb) { + tmp_cck_sec_pwdb = + tmp_cck_max_pwdb; + cck_rx_ver2_sec_index = + cck_rx_ver2_max_index; + tmp_cck_max_pwdb = cur_cck_pwdb; + cck_rx_ver2_max_index = i; + } else if (cur_cck_pwdb == + tmp_cck_max_pwdb) { + tmp_cck_sec_pwdb = cur_cck_pwdb; + cck_rx_ver2_sec_index = i; + } else if (PWDB_IN_RANGE) { + tmp_cck_sec_pwdb = cur_cck_pwdb; + cck_rx_ver2_sec_index = i; + } else if (cur_cck_pwdb == + tmp_cck_sec_pwdb) { + if (tmp_cck_sec_pwdb == + tmp_cck_min_pwdb) { + tmp_cck_sec_pwdb = + cur_cck_pwdb; + cck_rx_ver2_sec_index = + i; + } + } else if ((cur_cck_pwdb < tmp_cck_sec_pwdb) && + (cur_cck_pwdb > tmp_cck_min_pwdb)) { + ; + } else if (cur_cck_pwdb == tmp_cck_min_pwdb) { + if (tmp_cck_sec_pwdb == tmp_cck_min_pwdb) + tmp_cck_min_pwdb = cur_cck_pwdb; + } else if (cur_cck_pwdb < tmp_cck_min_pwdb) { + tmp_cck_min_pwdb = cur_cck_pwdb; + } + } + } + } + } + + update_cck_rx_path = 0; + if (dm_rx_path_sel_table.cck_method == CCK_Rx_Version_2) { + cck_default_Rx = cck_rx_ver2_max_index; + cck_optional_Rx = cck_rx_ver2_sec_index; + if (tmp_cck_max_pwdb != -64) + update_cck_rx_path = 1; + } + + if (tmp_min_rssi < dm_rx_path_sel_table.ss_th_low && disabled_rf_cnt < 2) { + if ((tmp_max_rssi - tmp_min_rssi) >= + dm_rx_path_sel_table.diff_th) { + dm_rx_path_sel_table.rf_enable_rssi_th[min_rssi_index] = + tmp_max_rssi + 5; + rtl92e_set_bb_reg(dev, rOFDM0_TRxPathEnable, + 0x1 << min_rssi_index, 0x0); + rtl92e_set_bb_reg(dev, rOFDM1_TRxPathEnable, + 0x1 << min_rssi_index, 0x0); + disabled_rf_cnt++; + } + if (dm_rx_path_sel_table.cck_method == CCK_Rx_Version_1) { + cck_default_Rx = max_rssi_index; + cck_optional_Rx = sec_rssi_index; + if (tmp_max_rssi) + update_cck_rx_path = 1; + } + } + + if (update_cck_rx_path) { + dm_rx_path_sel_table.cck_rx_path = (cck_default_Rx << 2) | + (cck_optional_Rx); + rtl92e_set_bb_reg(dev, rCCK0_AFESetting, 0x0f000000, + dm_rx_path_sel_table.cck_rx_path); + } + + if (dm_rx_path_sel_table.disabled_rf) { + for (i = 0; i < 4; i++) { + if ((dm_rx_path_sel_table.disabled_rf >> i) & 0x1) { + if (tmp_max_rssi >= + dm_rx_path_sel_table.rf_enable_rssi_th[i]) { + rtl92e_set_bb_reg(dev, + rOFDM0_TRxPathEnable, + 0x1 << i, 0x1); + rtl92e_set_bb_reg(dev, + rOFDM1_TRxPathEnable, + 0x1 << i, 0x1); + dm_rx_path_sel_table.rf_enable_rssi_th[i] + = 100; + disabled_rf_cnt--; + } + } + } + } +} + +static void _rtl92e_dm_check_rx_path_selection(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + schedule_delayed_work(&priv->rfpath_check_wq, 0); +} + +static void _rtl92e_dm_init_fsync(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->rtllib->fsync_time_interval = 500; + priv->rtllib->fsync_rate_bitmap = 0x0f000800; + priv->rtllib->fsync_rssi_threshold = 30; + priv->rtllib->bfsync_enable = false; + priv->rtllib->fsync_multiple_timeinterval = 3; + priv->rtllib->fsync_firstdiff_ratethreshold = 100; + priv->rtllib->fsync_seconddiff_ratethreshold = 200; + priv->rtllib->fsync_state = Default_Fsync; + + timer_setup(&priv->fsync_timer, _rtl92e_dm_fsync_timer_callback, 0); +} + +static void _rtl92e_dm_deinit_fsync(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + del_timer_sync(&priv->fsync_timer); +} + +static void _rtl92e_dm_fsync_timer_callback(struct timer_list *t) +{ + struct r8192_priv *priv = from_timer(priv, t, fsync_timer); + struct net_device *dev = priv->rtllib->dev; + u32 rate_index, rate_count = 0, rate_count_diff = 0; + bool bSwitchFromCountDiff = false; + bool bDoubleTimeInterval = false; + + if (priv->rtllib->link_state == MAC80211_LINKED && + priv->rtllib->bfsync_enable && + (priv->rtllib->ht_info->iot_action & HT_IOT_ACT_CDD_FSYNC)) { + u32 rate_bitmap; + + for (rate_index = 0; rate_index <= 27; rate_index++) { + rate_bitmap = 1 << rate_index; + if (priv->rtllib->fsync_rate_bitmap & rate_bitmap) + rate_count += + priv->stats.received_rate_histogram[1] + [rate_index]; + } + + if (rate_count < priv->rate_record) + rate_count_diff = 0xffffffff - rate_count + + priv->rate_record; + else + rate_count_diff = rate_count - priv->rate_record; + if (rate_count_diff < priv->rate_count_diff_rec) { + u32 DiffNum = priv->rate_count_diff_rec - + rate_count_diff; + if (DiffNum >= + priv->rtllib->fsync_seconddiff_ratethreshold) + priv->continue_diff_count++; + else + priv->continue_diff_count = 0; + + if (priv->continue_diff_count >= 2) { + bSwitchFromCountDiff = true; + priv->continue_diff_count = 0; + } + } else { + priv->continue_diff_count = 0; + } + + if (rate_count_diff <= + priv->rtllib->fsync_firstdiff_ratethreshold) { + bSwitchFromCountDiff = true; + priv->continue_diff_count = 0; + } + priv->rate_record = rate_count; + priv->rate_count_diff_rec = rate_count_diff; + if (priv->undecorated_smoothed_pwdb > + priv->rtllib->fsync_rssi_threshold && + bSwitchFromCountDiff) { + bDoubleTimeInterval = true; + priv->bswitch_fsync = !priv->bswitch_fsync; + if (priv->bswitch_fsync) { + rtl92e_writeb(dev, 0xC36, 0x1c); + rtl92e_writeb(dev, 0xC3e, 0x90); + } else { + rtl92e_writeb(dev, 0xC36, 0x5c); + rtl92e_writeb(dev, 0xC3e, 0x96); + } + } else if (priv->undecorated_smoothed_pwdb <= + priv->rtllib->fsync_rssi_threshold) { + if (priv->bswitch_fsync) { + priv->bswitch_fsync = false; + rtl92e_writeb(dev, 0xC36, 0x5c); + rtl92e_writeb(dev, 0xC3e, 0x96); + } + } + if (bDoubleTimeInterval) { + if (timer_pending(&priv->fsync_timer)) + del_timer_sync(&priv->fsync_timer); + priv->fsync_timer.expires = jiffies + + msecs_to_jiffies(priv->rtllib->fsync_time_interval * + priv->rtllib->fsync_multiple_timeinterval); + add_timer(&priv->fsync_timer); + } else { + if (timer_pending(&priv->fsync_timer)) + del_timer_sync(&priv->fsync_timer); + priv->fsync_timer.expires = jiffies + + msecs_to_jiffies(priv->rtllib->fsync_time_interval); + add_timer(&priv->fsync_timer); + } + } else { + if (priv->bswitch_fsync) { + priv->bswitch_fsync = false; + rtl92e_writeb(dev, 0xC36, 0x5c); + rtl92e_writeb(dev, 0xC3e, 0x96); + } + priv->continue_diff_count = 0; + rtl92e_writel(dev, rOFDM0_RxDetector2, 0x465c52cd); + } +} + +static void _rtl92e_dm_start_hw_fsync(struct net_device *dev) +{ + u8 rf_timing = 0x77; + struct r8192_priv *priv = rtllib_priv(dev); + + rtl92e_writel(dev, rOFDM0_RxDetector2, 0x465c12cf); + priv->rtllib->SetHwRegHandler(dev, HW_VAR_RF_TIMING, + (u8 *)(&rf_timing)); + rtl92e_writeb(dev, 0xc3b, 0x41); +} + +static void _rtl92e_dm_end_hw_fsync(struct net_device *dev) +{ + u8 rf_timing = 0xaa; + struct r8192_priv *priv = rtllib_priv(dev); + + rtl92e_writel(dev, rOFDM0_RxDetector2, 0x465c52cd); + priv->rtllib->SetHwRegHandler(dev, HW_VAR_RF_TIMING, (u8 *) + (&rf_timing)); + rtl92e_writeb(dev, 0xc3b, 0x49); +} + +static void _rtl92e_dm_end_sw_fsync(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + del_timer_sync(&(priv->fsync_timer)); + + if (priv->bswitch_fsync) { + priv->bswitch_fsync = false; + + rtl92e_writeb(dev, 0xC36, 0x5c); + + rtl92e_writeb(dev, 0xC3e, 0x96); + } + + priv->continue_diff_count = 0; + rtl92e_writel(dev, rOFDM0_RxDetector2, 0x465c52cd); +} + +static void _rtl92e_dm_start_sw_fsync(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 rate_index; + u32 rate_bitmap; + + priv->rate_record = 0; + priv->continue_diff_count = 0; + priv->rate_count_diff_rec = 0; + priv->bswitch_fsync = false; + + if (priv->rtllib->mode == WIRELESS_MODE_N_24G) { + priv->rtllib->fsync_firstdiff_ratethreshold = 600; + priv->rtllib->fsync_seconddiff_ratethreshold = 0xffff; + } else { + priv->rtllib->fsync_firstdiff_ratethreshold = 200; + priv->rtllib->fsync_seconddiff_ratethreshold = 200; + } + for (rate_index = 0; rate_index <= 27; rate_index++) { + rate_bitmap = 1 << rate_index; + if (priv->rtllib->fsync_rate_bitmap & rate_bitmap) + priv->rate_record += + priv->stats.received_rate_histogram[1] + [rate_index]; + } + if (timer_pending(&priv->fsync_timer)) + del_timer_sync(&priv->fsync_timer); + priv->fsync_timer.expires = jiffies + + msecs_to_jiffies(priv->rtllib->fsync_time_interval); + add_timer(&priv->fsync_timer); + + rtl92e_writel(dev, rOFDM0_RxDetector2, 0x465c12cd); +} + +static void _rtl92e_dm_check_fsync(struct net_device *dev) +{ +#define RegC38_Default 0 +#define RegC38_NonFsync_Other_AP 1 +#define RegC38_Fsync_AP_BCM 2 + struct r8192_priv *priv = rtllib_priv(dev); + static u8 reg_c38_State = RegC38_Default; + static u32 reset_cnt; + + if (priv->rtllib->link_state == MAC80211_LINKED && + priv->rtllib->ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) { + if (priv->rtllib->bfsync_enable == 0) { + switch (priv->rtllib->fsync_state) { + case Default_Fsync: + _rtl92e_dm_start_hw_fsync(dev); + priv->rtllib->fsync_state = HW_Fsync; + break; + case SW_Fsync: + _rtl92e_dm_end_sw_fsync(dev); + _rtl92e_dm_start_hw_fsync(dev); + priv->rtllib->fsync_state = HW_Fsync; + break; + case HW_Fsync: + default: + break; + } + } else { + switch (priv->rtllib->fsync_state) { + case Default_Fsync: + _rtl92e_dm_start_sw_fsync(dev); + priv->rtllib->fsync_state = SW_Fsync; + break; + case HW_Fsync: + _rtl92e_dm_end_hw_fsync(dev); + _rtl92e_dm_start_sw_fsync(dev); + priv->rtllib->fsync_state = SW_Fsync; + break; + case SW_Fsync: + default: + break; + } + } + if (reg_c38_State != RegC38_Fsync_AP_BCM) { + rtl92e_writeb(dev, rOFDM0_RxDetector3, 0x95); + + reg_c38_State = RegC38_Fsync_AP_BCM; + } + } else { + switch (priv->rtllib->fsync_state) { + case HW_Fsync: + _rtl92e_dm_end_hw_fsync(dev); + priv->rtllib->fsync_state = Default_Fsync; + break; + case SW_Fsync: + _rtl92e_dm_end_sw_fsync(dev); + priv->rtllib->fsync_state = Default_Fsync; + break; + case Default_Fsync: + default: + break; + } + + if (priv->rtllib->link_state == MAC80211_LINKED) { + if (priv->undecorated_smoothed_pwdb <= + RegC38_TH) { + if (reg_c38_State != + RegC38_NonFsync_Other_AP) { + rtl92e_writeb(dev, + rOFDM0_RxDetector3, + 0x90); + + reg_c38_State = + RegC38_NonFsync_Other_AP; + } + } else if (priv->undecorated_smoothed_pwdb >= + (RegC38_TH + 5)) { + if (reg_c38_State) { + rtl92e_writeb(dev, + rOFDM0_RxDetector3, + priv->framesync); + reg_c38_State = RegC38_Default; + } + } + } else { + if (reg_c38_State) { + rtl92e_writeb(dev, rOFDM0_RxDetector3, + priv->framesync); + reg_c38_State = RegC38_Default; + } + } + } + if (priv->reset_count != reset_cnt) { + rtl92e_writeb(dev, rOFDM0_RxDetector3, + priv->framesync); + reg_c38_State = RegC38_Default; + reset_cnt = priv->reset_count; + } +} + +/*---------------------------Define function prototype------------------------*/ +static void _rtl92e_dm_init_dynamic_tx_power(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + priv->rtllib->bdynamic_txpower_enable = true; + priv->last_dtp_flag_high = false; + priv->last_dtp_flag_low = false; + priv->dynamic_tx_high_pwr = false; + priv->dynamic_tx_low_pwr = false; +} + +static void _rtl92e_dm_dynamic_tx_power(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + unsigned int txhipower_threshold = 0; + unsigned int txlowpower_threshold = 0; + + if (!priv->rtllib->bdynamic_txpower_enable) { + priv->dynamic_tx_high_pwr = false; + priv->dynamic_tx_low_pwr = false; + return; + } + if ((priv->rtllib->ht_info->IOTPeer == HT_IOT_PEER_ATHEROS) && + (priv->rtllib->mode == WIRELESS_MODE_G)) { + txhipower_threshold = TX_POWER_ATHEROAP_THRESH_HIGH; + txlowpower_threshold = TX_POWER_ATHEROAP_THRESH_LOW; + } else { + txhipower_threshold = TX_POWER_NEAR_FIELD_THRESH_HIGH; + txlowpower_threshold = TX_POWER_NEAR_FIELD_THRESH_LOW; + } + + if (priv->rtllib->link_state == MAC80211_LINKED) { + if (priv->undecorated_smoothed_pwdb >= txhipower_threshold) { + priv->dynamic_tx_high_pwr = true; + priv->dynamic_tx_low_pwr = false; + } else { + if (priv->undecorated_smoothed_pwdb < + txlowpower_threshold && priv->dynamic_tx_high_pwr) + priv->dynamic_tx_high_pwr = false; + if (priv->undecorated_smoothed_pwdb < 35) + priv->dynamic_tx_low_pwr = true; + else if (priv->undecorated_smoothed_pwdb >= 40) + priv->dynamic_tx_low_pwr = false; + } + } else { + priv->dynamic_tx_high_pwr = false; + priv->dynamic_tx_low_pwr = false; + } + + if ((priv->dynamic_tx_high_pwr != priv->last_dtp_flag_high) || + (priv->dynamic_tx_low_pwr != priv->last_dtp_flag_low)) { + rtl92e_set_tx_power(dev, priv->rtllib->current_network.channel); + } + priv->last_dtp_flag_high = priv->dynamic_tx_high_pwr; + priv->last_dtp_flag_low = priv->dynamic_tx_low_pwr; +} + +static void _rtl92e_dm_check_txrateandretrycount(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + ieee->softmac_stats.CurrentShowTxate = rtl92e_readb(dev, CURRENT_TX_RATE_REG); + ieee->softmac_stats.last_packet_rate = rtl92e_readb(dev, INITIAL_TX_RATE_REG); + ieee->softmac_stats.txretrycount = rtl92e_readl(dev, TX_RETRY_COUNT_REG); +} + +static void _rtl92e_dm_send_rssi_to_fw(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + rtl92e_writeb(dev, DRIVER_RSSI, priv->undecorated_smoothed_pwdb); +} diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.h b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.h new file mode 100644 index 0000000000..01587e2fec --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef __R8192UDM_H__ +#define __R8192UDM_H__ + +/*--------------------------Define Parameters-------------------------------*/ +#define OFDM_TABLE_LEN 19 +#define CCK_TABLE_LEN 12 + +#define DM_DIG_THRESH_HIGH 40 +#define DM_DIG_THRESH_LOW 35 + +#define DM_DIG_HIGH_PWR_THRESH_HIGH 75 +#define DM_DIG_HIGH_PWR_THRESH_LOW 70 + +#define BW_AUTO_SWITCH_HIGH_LOW 25 +#define BW_AUTO_SWITCH_LOW_HIGH 30 + +#define DM_DIG_BACKOFF 12 +#define DM_DIG_MAX 0x36 +#define DM_DIG_MIN 0x1c +#define DM_DIG_MIN_Netcore 0x12 + +#define RX_PATH_SEL_SS_TH_LOW 30 +#define RX_PATH_SEL_DIFF_TH 18 + +#define RATE_ADAPTIVE_TH_HIGH 50 +#define RATE_ADAPTIVE_TH_LOW_20M 30 +#define RATE_ADAPTIVE_TH_LOW_40M 10 +#define VERY_LOW_RSSI 15 + +#define WA_IOT_TH_VAL 25 + +#define E_FOR_TX_POWER_TRACK 300 +#define TX_POWER_NEAR_FIELD_THRESH_HIGH 68 +#define TX_POWER_NEAR_FIELD_THRESH_LOW 62 +#define TX_POWER_ATHEROAP_THRESH_HIGH 78 +#define TX_POWER_ATHEROAP_THRESH_LOW 72 + +#define CURRENT_TX_RATE_REG 0x1e0 +#define INITIAL_TX_RATE_REG 0x1e1 +#define TX_RETRY_COUNT_REG 0x1ac +#define RegC38_TH 20 + +/*--------------------------Define Parameters-------------------------------*/ + +/*------------------------------Define structure----------------------------*/ +struct dig_t { + u8 dig_enable_flag; + u8 dig_algorithm; + u8 dig_algorithm_switch; + + long rssi_low_thresh; + long rssi_high_thresh; + + long rssi_high_power_lowthresh; + long rssi_high_power_highthresh; + + u8 dig_state; + u8 dig_highpwr_state; + u8 cur_sta_connect_state; + u8 pre_sta_connect_state; + + u8 curpd_thstate; + u8 prepd_thstate; + u8 curcs_ratio_state; + u8 precs_ratio_state; + + u32 pre_ig_value; + u32 cur_ig_value; + + u8 backoff_val; + u8 rx_gain_range_max; + u8 rx_gain_range_min; + + long rssi_val; +}; + +enum dm_dig_sta { + DM_STA_DIG_OFF = 0, + DM_STA_DIG_ON, + DM_STA_DIG_MAX +}; + +enum dm_ratr_sta { + DM_RATR_STA_HIGH = 0, + DM_RATR_STA_MIDDLE = 1, + DM_RATR_STA_LOW = 2, + DM_RATR_STA_MAX +}; + +enum dm_dig_alg { + DIG_ALGO_BY_FALSE_ALARM = 0, + DIG_ALGO_BY_RSSI = 1, + DIG_ALGO_BEFORE_CONNECT_BY_RSSI_AND_ALARM = 2, + DIG_ALGO_BY_TOW_PORT = 3, + DIG_ALGO_MAX +}; + +enum dm_dig_connect { + DIG_STA_DISCONNECT = 0, + DIG_STA_CONNECT = 1, + DIG_STA_BEFORE_CONNECT = 2, + DIG_AP_DISCONNECT = 3, + DIG_AP_CONNECT = 4, + DIG_AP_ADD_STATION = 5, + DIG_CONNECT_MAX +}; + +enum dm_dig_pd_th { + DIG_PD_AT_LOW_POWER = 0, + DIG_PD_AT_NORMAL_POWER = 1, + DIG_PD_AT_HIGH_POWER = 2, + DIG_PD_MAX +}; + +enum dm_dig_cs_ratio { + DIG_CS_RATIO_LOWER = 0, + DIG_CS_RATIO_HIGHER = 1, + DIG_CS_MAX +}; + +struct drx_path_sel { + u8 enable; + u8 cck_method; + u8 cck_rx_path; + + u8 ss_th_low; + u8 diff_th; + u8 disabled_rf; + u8 reserved; + + u8 rf_rssi[4]; + u8 rf_enable_rssi_th[4]; + long cck_pwdb_sta[4]; +}; + +enum dm_cck_rx_path_method { + CCK_Rx_Version_1 = 0, + CCK_Rx_Version_2 = 1, + CCK_Rx_Version_MAX +}; + +struct dcmd_txcmd { + u32 op; + u32 length; + u32 value; +}; + +/*------------------------------Define structure----------------------------*/ + +/*------------------------Export global variable----------------------------*/ +extern struct dig_t dm_digtable; + +/* Pre-calculated gain tables */ +extern const u32 dm_tx_bb_gain[TX_BB_GAIN_TABLE_LEN]; +extern const u8 dm_cck_tx_bb_gain[CCK_TX_BB_GAIN_TABLE_LEN][8]; +extern const u8 dm_cck_tx_bb_gain_ch14[CCK_TX_BB_GAIN_TABLE_LEN][8]; +/* Maps table index to iq amplify gain (dB, 12 to -24dB) */ +#define dm_tx_bb_gain_idx_to_amplify(idx) (-idx + 12) + +/*------------------------Export global variable----------------------------*/ + +/*--------------------------Exported Function prototype---------------------*/ +/*--------------------------Exported Function prototype---------------------*/ + +void rtl92e_dm_init(struct net_device *dev); +void rtl92e_dm_deinit(struct net_device *dev); + +void rtl92e_dm_watchdog(struct net_device *dev); + +void rtl92e_init_adaptive_rate(struct net_device *dev); +void rtl92e_dm_txpower_tracking_wq(void *data); + +void rtl92e_dm_cck_txpower_adjust(struct net_device *dev, bool binch14); + +void rtl92e_dm_restore_state(struct net_device *dev); +void rtl92e_dm_backup_state(struct net_device *dev); +void rtl92e_dm_init_edca_turbo(struct net_device *dev); +void rtl92e_dm_rf_pathcheck_wq(void *data); +void rtl92e_dm_init_txpower_tracking(struct net_device *dev); +#endif /*__R8192UDM_H__ */ diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c new file mode 100644 index 0000000000..db57c655c6 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "rtl_eeprom.h" + +static void _rtl92e_gpio_write_bit(struct net_device *dev, int no, bool val) +{ + u8 reg = rtl92e_readb(dev, EPROM_CMD); + + if (val) + reg |= 1 << no; + else + reg &= ~(1 << no); + + rtl92e_writeb(dev, EPROM_CMD, reg); + udelay(EPROM_DELAY); +} + +static bool _rtl92e_gpio_get_bit(struct net_device *dev, int no) +{ + u8 reg = rtl92e_readb(dev, EPROM_CMD); + + return (reg >> no) & 0x1; +} + +static void _rtl92e_eeprom_ck_cycle(struct net_device *dev) +{ + _rtl92e_gpio_write_bit(dev, EPROM_CK_BIT, 1); + _rtl92e_gpio_write_bit(dev, EPROM_CK_BIT, 0); +} + +static u16 _rtl92e_eeprom_xfer(struct net_device *dev, u16 data, int tx_len) +{ + u16 ret = 0; + int rx_len = 16; + + _rtl92e_gpio_write_bit(dev, EPROM_CS_BIT, 1); + _rtl92e_eeprom_ck_cycle(dev); + + while (tx_len--) { + _rtl92e_gpio_write_bit(dev, EPROM_W_BIT, + (data >> tx_len) & 0x1); + _rtl92e_eeprom_ck_cycle(dev); + } + + _rtl92e_gpio_write_bit(dev, EPROM_W_BIT, 0); + + while (rx_len--) { + _rtl92e_eeprom_ck_cycle(dev); + ret |= _rtl92e_gpio_get_bit(dev, EPROM_R_BIT) << rx_len; + } + + _rtl92e_gpio_write_bit(dev, EPROM_CS_BIT, 0); + _rtl92e_eeprom_ck_cycle(dev); + + return ret; +} + +u32 rtl92e_eeprom_read(struct net_device *dev, u32 addr) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u32 ret = 0; + + rtl92e_writeb(dev, EPROM_CMD, + (EPROM_CMD_PROGRAM << EPROM_CMD_OPERATING_MODE_SHIFT)); + udelay(EPROM_DELAY); + + /* EEPROM is configured as x16 */ + if (priv->epromtype == EEPROM_93C56) + ret = _rtl92e_eeprom_xfer(dev, (addr & 0xFF) | (0x6 << 8), 11); + else + ret = _rtl92e_eeprom_xfer(dev, (addr & 0x3F) | (0x6 << 6), 9); + + rtl92e_writeb(dev, EPROM_CMD, + (EPROM_CMD_NORMAL << EPROM_CMD_OPERATING_MODE_SHIFT)); + return ret; +} diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h new file mode 100644 index 0000000000..66f1979bb1 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_eeprom.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#define EPROM_DELAY 10 + +u32 rtl92e_eeprom_read(struct net_device *dev, u32 addr); diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c b/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c new file mode 100644 index 0000000000..fab8932e67 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_ethtool.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/delay.h> + +#include "rtl_core.h" + +static void _rtl92e_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, pci_name(priv->pdev), sizeof(info->bus_info)); +} + +static u32 _rtl92e_ethtool_get_link(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return ((priv->rtllib->link_state == MAC80211_LINKED) || + (priv->rtllib->link_state == MAC80211_LINKED_SCANNING)); +} + +const struct ethtool_ops rtl819x_ethtool_ops = { + .get_drvinfo = _rtl92e_ethtool_get_drvinfo, + .get_link = _rtl92e_ethtool_get_link, +}; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c new file mode 100644 index 0000000000..0bc3e01300 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_pci.h" +#include "rtl_core.h" + +static void _rtl92e_parse_pci_configuration(struct pci_dev *pdev, + struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + + u8 tmp; + u16 link_ctrl_reg; + + pcie_capability_read_word(priv->pdev, PCI_EXP_LNKCTL, &link_ctrl_reg); + + pci_read_config_byte(pdev, 0x98, &tmp); + tmp |= BIT4; + pci_write_config_byte(pdev, 0x98, tmp); + + tmp = 0x17; + pci_write_config_byte(pdev, 0x70f, tmp); +} + +bool rtl92e_check_adapter(struct pci_dev *pdev, struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + u16 device_id; + u8 revision_id; + u16 irq_line; + + device_id = pdev->device; + revision_id = pdev->revision; + pci_read_config_word(pdev, 0x3C, &irq_line); + + priv->card_8192 = NIC_8192E; + + if (device_id == 0x8192) { + switch (revision_id) { + case HAL_HW_PCI_REVISION_ID_8192PCIE: + dev_info(&pdev->dev, + "Adapter(8192 PCI-E) is found - DeviceID=%x\n", + device_id); + priv->card_8192 = NIC_8192E; + break; + case HAL_HW_PCI_REVISION_ID_8192SE: + dev_info(&pdev->dev, + "Adapter(8192SE) is found - DeviceID=%x\n", + device_id); + priv->card_8192 = NIC_8192SE; + break; + default: + dev_info(&pdev->dev, + "UNKNOWN nic type(%4x:%4x)\n", + pdev->vendor, pdev->device); + priv->card_8192 = NIC_UNKNOWN; + return false; + } + } + + if (priv->card_8192 != NIC_8192E) { + dev_info(&pdev->dev, + "Detect info(%x) and hardware info(%x) not match!\n", + NIC_8192E, priv->card_8192); + dev_info(&pdev->dev, + "Please select proper driver before install!!!!\n"); + return false; + } + + _rtl92e_parse_pci_configuration(pdev, dev); + + return true; +} diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.h b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.h new file mode 100644 index 0000000000..866e0efbc4 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _RTL_PCI_H +#define _RTL_PCI_H + +#include <linux/types.h> +#include <linux/pci.h> + +struct net_device; +bool rtl92e_check_adapter(struct pci_dev *pdev, struct net_device *dev); + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.c new file mode 100644 index 0000000000..d124b5eee0 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_core.h" +#include "r8192E_hw.h" +#include "r8190P_rtl8256.h" +#include "rtl_pm.h" + +int rtl92e_suspend(struct device *dev_d) +{ + struct net_device *dev = dev_get_drvdata(dev_d); + struct r8192_priv *priv = rtllib_priv(dev); + u32 ulRegRead; + + netdev_info(dev, "============> r8192E suspend call.\n"); + del_timer_sync(&priv->gpio_polling_timer); + cancel_delayed_work_sync(&priv->gpio_change_rf_wq); + priv->polling_timer_on = 0; + + if (!netif_running(dev)) { + netdev_info(dev, + "RTL819XE:UI is open out of suspend function\n"); + goto out_pci_suspend; + } + + if (dev->netdev_ops->ndo_stop) + dev->netdev_ops->ndo_stop(dev); + netif_device_detach(dev); + + if (!priv->rtllib->bSupportRemoteWakeUp) { + rtl92e_set_rf_state(dev, rf_off, RF_CHANGE_BY_INIT); + ulRegRead = rtl92e_readl(dev, CPU_GEN); + ulRegRead |= CPU_GEN_SYSTEM_RESET; + rtl92e_writel(dev, CPU_GEN, ulRegRead); + } else { + rtl92e_writel(dev, WFCRC0, 0xffffffff); + rtl92e_writel(dev, WFCRC1, 0xffffffff); + rtl92e_writel(dev, WFCRC2, 0xffffffff); + rtl92e_writeb(dev, PMR, 0x5); + rtl92e_writeb(dev, MAC_BLK_CTRL, 0xa); + } +out_pci_suspend: + netdev_info(dev, "WOL is %s\n", priv->rtllib->bSupportRemoteWakeUp ? + "Supported" : "Not supported"); + device_set_wakeup_enable(dev_d, priv->rtllib->bSupportRemoteWakeUp); + + mdelay(20); + + return 0; +} + +int rtl92e_resume(struct device *dev_d) +{ + struct pci_dev *pdev = to_pci_dev(dev_d); + struct net_device *dev = dev_get_drvdata(dev_d); + struct r8192_priv *priv = rtllib_priv(dev); + u32 val; + + netdev_info(dev, "================>r8192E resume call.\n"); + + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + + device_wakeup_disable(dev_d); + + if (priv->polling_timer_on == 0) + rtl92e_check_rfctrl_gpio_timer(&priv->gpio_polling_timer); + + if (!netif_running(dev)) { + netdev_info(dev, + "RTL819XE:UI is open out of resume function\n"); + goto out; + } + + netif_device_attach(dev); + if (dev->netdev_ops->ndo_open) + dev->netdev_ops->ndo_open(dev); + + if (!priv->rtllib->bSupportRemoteWakeUp) + rtl92e_set_rf_state(dev, rf_on, RF_CHANGE_BY_INIT); + +out: + return 0; +} + diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pm.h b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.h new file mode 100644 index 0000000000..fd86114959 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pm.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef R8192E_PM_H +#define R8192E_PM_H + +#include <linux/types.h> +#include <linux/pci.h> + +int rtl92e_suspend(struct device *dev_d); +int rtl92e_resume(struct device *dev_d); + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_ps.c b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.c new file mode 100644 index 0000000000..598bfc0ff3 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtl_ps.h" +#include "rtl_core.h" +#include "r8192E_phy.h" +#include "r8192E_phyreg.h" +#include "r8190P_rtl8256.h" /* RTL8225 Radio frontend */ +#include "r8192E_cmdpkt.h" +#include <linux/jiffies.h> + +static void _rtl92e_hw_sleep(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + unsigned long flags = 0; + + spin_lock_irqsave(&priv->rf_ps_lock, flags); + if (priv->rf_change_in_progress) { + spin_unlock_irqrestore(&priv->rf_ps_lock, flags); + return; + } + spin_unlock_irqrestore(&priv->rf_ps_lock, flags); + rtl92e_set_rf_state(dev, rf_sleep, RF_CHANGE_BY_PS); +} + +void rtl92e_hw_sleep_wq(void *data) +{ + struct rtllib_device *ieee = container_of_dwork_rsl(data, + struct rtllib_device, hw_sleep_wq); + struct net_device *dev = ieee->dev; + + _rtl92e_hw_sleep(dev); +} + +void rtl92e_hw_wakeup(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + unsigned long flags = 0; + + spin_lock_irqsave(&priv->rf_ps_lock, flags); + if (priv->rf_change_in_progress) { + spin_unlock_irqrestore(&priv->rf_ps_lock, flags); + schedule_delayed_work(&priv->rtllib->hw_wakeup_wq, + msecs_to_jiffies(10)); + return; + } + spin_unlock_irqrestore(&priv->rf_ps_lock, flags); + rtl92e_set_rf_state(dev, rf_on, RF_CHANGE_BY_PS); +} + +void rtl92e_hw_wakeup_wq(void *data) +{ + struct rtllib_device *ieee = container_of_dwork_rsl(data, + struct rtllib_device, hw_wakeup_wq); + struct net_device *dev = ieee->dev; + + rtl92e_hw_wakeup(dev); +} + +#define MIN_SLEEP_TIME 50 +#define MAX_SLEEP_TIME 10000 +void rtl92e_enter_sleep(struct net_device *dev, u64 time) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + u32 tmp; + unsigned long flags; + unsigned long timeout; + + spin_lock_irqsave(&priv->ps_lock, flags); + + time -= msecs_to_jiffies(8 + 16 + 7); + + timeout = jiffies + msecs_to_jiffies(MIN_SLEEP_TIME); + if (time_before((unsigned long)time, timeout)) { + spin_unlock_irqrestore(&priv->ps_lock, flags); + netdev_info(dev, "too short to sleep::%lld < %ld\n", + time - jiffies, msecs_to_jiffies(MIN_SLEEP_TIME)); + return; + } + timeout = jiffies + msecs_to_jiffies(MAX_SLEEP_TIME); + if (time_after((unsigned long)time, timeout)) { + netdev_info(dev, "========>too long to sleep:%lld > %ld\n", + time - jiffies, msecs_to_jiffies(MAX_SLEEP_TIME)); + spin_unlock_irqrestore(&priv->ps_lock, flags); + return; + } + tmp = time - jiffies; + schedule_delayed_work(&priv->rtllib->hw_wakeup_wq, tmp); + schedule_delayed_work(&priv->rtllib->hw_sleep_wq, 0); + spin_unlock_irqrestore(&priv->ps_lock, flags); +} + +static void _rtl92e_ps_update_rf_state(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + &priv->rtllib->pwr_save_ctrl; + + psc->bSwRfProcessing = true; + rtl92e_set_rf_state(dev, psc->eInactivePowerState, RF_CHANGE_BY_IPS); + + psc->bSwRfProcessing = false; +} + +void rtl92e_ips_enter(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + &priv->rtllib->pwr_save_ctrl; + enum rt_rf_power_state rt_state; + + rt_state = priv->rtllib->rf_power_state; + if (rt_state == rf_on && !psc->bSwRfProcessing && + (priv->rtllib->link_state != MAC80211_LINKED)) { + psc->eInactivePowerState = rf_off; + _rtl92e_ps_update_rf_state(dev); + } +} + +void rtl92e_ips_leave(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + &priv->rtllib->pwr_save_ctrl; + enum rt_rf_power_state rt_state; + + rt_state = priv->rtllib->rf_power_state; + if (rt_state != rf_on && !psc->bSwRfProcessing && + priv->rtllib->rf_off_reason <= RF_CHANGE_BY_IPS) { + psc->eInactivePowerState = rf_on; + _rtl92e_ps_update_rf_state(dev); + } +} + +void rtl92e_ips_leave_wq(void *data) +{ + struct rtllib_device *ieee = container_of(data, struct rtllib_device, ips_leave_wq); + struct net_device *dev = ieee->dev; + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + + mutex_lock(&priv->rtllib->ips_mutex); + rtl92e_ips_leave(dev); + mutex_unlock(&priv->rtllib->ips_mutex); +} + +void rtl92e_rtllib_ips_leave_wq(struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + enum rt_rf_power_state rt_state; + + rt_state = priv->rtllib->rf_power_state; + if (rt_state == rf_off) { + if (priv->rtllib->rf_off_reason > RF_CHANGE_BY_IPS) { + netdev_warn(dev, "%s(): RF is OFF.\n", + __func__); + return; + } + netdev_info(dev, "=========>%s(): rtl92e_ips_leave\n", + __func__); + schedule_work(&priv->rtllib->ips_leave_wq); + } +} + +void rtl92e_rtllib_ips_leave(struct net_device *dev) +{ + struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev); + + mutex_lock(&priv->rtllib->ips_mutex); + rtl92e_ips_leave(dev); + mutex_unlock(&priv->rtllib->ips_mutex); +} + +static bool _rtl92e_ps_set_mode(struct net_device *dev, u8 rtPsMode) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->rtllib->iw_mode == IW_MODE_ADHOC) + return false; + + if (!priv->ps_force) + priv->rtllib->ps = rtPsMode; + if (priv->rtllib->sta_sleep != LPS_IS_WAKE && + rtPsMode == RTLLIB_PS_DISABLED) { + unsigned long flags; + + rtl92e_hw_wakeup(dev); + priv->rtllib->sta_sleep = LPS_IS_WAKE; + + spin_lock_irqsave(&(priv->rtllib->mgmt_tx_lock), flags); + rtllib_sta_ps_send_null_frame(priv->rtllib, 0); + spin_unlock_irqrestore(&(priv->rtllib->mgmt_tx_lock), flags); + } + + return true; +} + +void rtl92e_leisure_ps_enter(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + &priv->rtllib->pwr_save_ctrl; + + if (!((priv->rtllib->iw_mode == IW_MODE_INFRA) && + (priv->rtllib->link_state == MAC80211_LINKED)) + || (priv->rtllib->iw_mode == IW_MODE_ADHOC)) + return; + + if (psc->bLeisurePs) { + if (psc->LpsIdleCount >= RT_CHECK_FOR_HANG_PERIOD) { + + if (priv->rtllib->ps == RTLLIB_PS_DISABLED) + _rtl92e_ps_set_mode(dev, RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST); + } else { + psc->LpsIdleCount++; + } + } +} + +void rtl92e_leisure_ps_leave(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + &priv->rtllib->pwr_save_ctrl; + + if (psc->bLeisurePs) { + if (priv->rtllib->ps != RTLLIB_PS_DISABLED) + _rtl92e_ps_set_mode(dev, RTLLIB_PS_DISABLED); + } +} diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_ps.h b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.h new file mode 100644 index 0000000000..70fe5d39be --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_ps.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Based on the r8180 driver, which is: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _RTL_PS_H +#define _RTL_PS_H + +#include <linux/types.h> + +struct net_device; + +#define RT_CHECK_FOR_HANG_PERIOD 2 + +void rtl92e_hw_wakeup(struct net_device *dev); +void rtl92e_enter_sleep(struct net_device *dev, u64 time); +void rtl92e_rtllib_ips_leave_wq(struct net_device *dev); +void rtl92e_rtllib_ips_leave(struct net_device *dev); +void rtl92e_ips_leave_wq(void *data); + +void rtl92e_ips_enter(struct net_device *dev); +void rtl92e_ips_leave(struct net_device *dev); + +void rtl92e_leisure_ps_enter(struct net_device *dev); +void rtl92e_leisure_ps_leave(struct net_device *dev); + +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c new file mode 100644 index 0000000000..1897988525 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.c @@ -0,0 +1,1153 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include <linux/string.h> +#include "rtl_core.h" +#include "rtl_wx.h" + +#define RATE_COUNT 12 +static u32 rtl8192_rates[] = { + 1000000, 2000000, 5500000, 11000000, 6000000, 9000000, 12000000, + 18000000, 24000000, 36000000, 48000000, 54000000 +}; + +#ifndef ENETDOWN +#define ENETDOWN 1 +#endif + +static int _rtl92e_wx_get_freq(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_freq(priv->rtllib, a, wrqu, b); +} + +static int _rtl92e_wx_get_mode(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_mode(priv->rtllib, a, wrqu, b); +} + +static int _rtl92e_wx_get_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_rate(priv->rtllib, info, wrqu, extra); +} + +static int _rtl92e_wx_set_rate(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_set_rate(priv->rtllib, info, wrqu, extra); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_set_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_set_rts(priv->rtllib, info, wrqu, extra); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_get_rts(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_rts(priv->rtllib, info, wrqu, extra); +} + +static int _rtl92e_wx_set_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) { + netdev_warn(dev, "%s(): Can't set Power: Radio is Off.\n", + __func__); + return 0; + } + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_set_power(priv->rtllib, info, wrqu, extra); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_get_power(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_power(priv->rtllib, info, wrqu, extra); +} + +static int _rtl92e_wx_set_rawtx(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int ret; + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_set_rawtx(priv->rtllib, info, wrqu, extra); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_force_reset(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + mutex_lock(&priv->wx_mutex); + + priv->force_reset = *extra; + mutex_unlock(&priv->wx_mutex); + return 0; +} + +static int _rtl92e_wx_adapter_power_status(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + (&priv->rtllib->pwr_save_ctrl); + struct rtllib_device *ieee = priv->rtllib; + + mutex_lock(&priv->wx_mutex); + + if (*extra || priv->force_lps) { + priv->ps_force = false; + psc->bLeisurePs = true; + } else { + if (priv->rtllib->link_state == MAC80211_LINKED) + rtl92e_leisure_ps_leave(dev); + + priv->ps_force = true; + psc->bLeisurePs = false; + ieee->ps = *extra; + } + + mutex_unlock(&priv->wx_mutex); + + return 0; +} + +static int _rtl92e_wx_set_lps_awake_interval(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, + char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rt_pwr_save_ctrl *psc = (struct rt_pwr_save_ctrl *) + (&priv->rtllib->pwr_save_ctrl); + + mutex_lock(&priv->wx_mutex); + + netdev_info(dev, "%s(): set lps awake interval ! extra is %d\n", + __func__, *extra); + + psc->reg_max_lps_awake_intvl = *extra; + mutex_unlock(&priv->wx_mutex); + return 0; +} + +static int _rtl92e_wx_set_force_lps(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + mutex_lock(&priv->wx_mutex); + + netdev_info(dev, + "%s(): force LPS ! extra is %d (1 is open 0 is close)\n", + __func__, *extra); + priv->force_lps = *extra; + mutex_unlock(&priv->wx_mutex); + return 0; +} + +static int _rtl92e_wx_set_debug(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + u8 c = *extra; + + if (priv->hw_radio_off) + return 0; + + netdev_info(dev, "=====>%s(), *extra:%x, debugflag:%x\n", __func__, + *extra, rt_global_debug_component); + if (c > 0) + rt_global_debug_component |= (1 << c); + else + rt_global_debug_component &= BIT31; + return 0; +} + +static int _rtl92e_wx_set_mode(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = netdev_priv_rsl(dev); + + enum rt_rf_power_state rt_state; + int ret; + + if (priv->hw_radio_off) + return 0; + rt_state = priv->rtllib->rf_power_state; + mutex_lock(&priv->wx_mutex); + if (wrqu->mode == IW_MODE_ADHOC || wrqu->mode == IW_MODE_MONITOR || + ieee->net_promiscuous_md) { + if (rt_state == rf_off) { + if (priv->rtllib->rf_off_reason > + RF_CHANGE_BY_IPS) { + netdev_warn(dev, "%s(): RF is OFF.\n", + __func__); + mutex_unlock(&priv->wx_mutex); + return -1; + } + netdev_info(dev, + "=========>%s(): rtl92e_ips_leave\n", + __func__); + mutex_lock(&priv->rtllib->ips_mutex); + rtl92e_ips_leave(dev); + mutex_unlock(&priv->rtllib->ips_mutex); + } + } + ret = rtllib_wx_set_mode(priv->rtllib, a, wrqu, b); + + mutex_unlock(&priv->wx_mutex); + return ret; +} + +struct iw_range_with_scan_capa { + /* Informative stuff (to choose between different interface) */ + __u32 throughput; /* To give an idea... */ + /* In theory this value should be the maximum benchmarked + * TCP/IP throughput, because with most of these devices the + * bit rate is meaningless (overhead an co) to estimate how + * fast the connection will go and pick the fastest one. + * I suggest people to play with Netperf or any benchmark... + */ + + /* NWID (or domain id) */ + __u32 min_nwid; /* Minimal NWID we are able to set */ + __u32 max_nwid; /* Maximal NWID we are able to set */ + + /* Old Frequency (backward compat - moved lower ) */ + __u16 old_num_channels; + __u8 old_num_frequency; + + /* Scan capabilities */ + __u8 scan_capa; +}; + +static int _rtl92e_wx_get_range(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_range *range = (struct iw_range *)extra; + struct r8192_priv *priv = rtllib_priv(dev); + u16 val; + int i; + + wrqu->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + + /* ~130 Mb/s real (802.11n) */ + range->throughput = 130 * 1000 * 1000; + + range->max_qual.qual = 100; + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.updated = 7; /* Updated all three */ + + range->avg_qual.qual = 70; /* > 8% missed beacons is 'bad' */ + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + range->avg_qual.updated = 7; /* Updated all three */ + + range->num_bitrates = min(RATE_COUNT, IW_MAX_BITRATES); + + for (i = 0; i < range->num_bitrates; i++) + range->bitrate[i] = rtl8192_rates[i]; + + range->max_rts = DEFAULT_RTS_THRESHOLD; + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->min_pmp = 0; + range->max_pmp = 5000000; + range->min_pmt = 0; + range->max_pmt = 65535 * 1000; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 18; + + for (i = 0, val = 0; i < 14; i++) { + if ((priv->rtllib->active_channel_map)[i + 1]) { + s32 freq_khz; + + range->freq[val].i = i + 1; + freq_khz = ieee80211_channel_to_freq_khz(i + 1, NL80211_BAND_2GHZ); + range->freq[val].m = freq_khz * 100; + range->freq[val].e = 1; + val++; + } + + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + range->num_channels = val; + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | IW_ENC_CAPA_CIPHER_CCMP; + range->scan_capa = IW_SCAN_CAPA_ESSID | IW_SCAN_CAPA_TYPE; + + /* Event capability (kernel + driver) */ + + return 0; +} + +static int _rtl92e_wx_set_scan(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + enum rt_rf_power_state rt_state; + int ret; + + if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) { + if ((ieee->link_state >= RTLLIB_ASSOCIATING) && + (ieee->link_state <= RTLLIB_ASSOCIATING_AUTHENTICATED)) + return 0; + if ((priv->rtllib->link_state == MAC80211_LINKED) && + (priv->rtllib->CntAfterLink < 2)) + return 0; + } + + if (priv->hw_radio_off) { + netdev_info(dev, "================>%s(): hwradio off\n", + __func__); + return 0; + } + rt_state = priv->rtllib->rf_power_state; + if (!priv->up) + return -ENETDOWN; + if (priv->rtllib->link_detect_info.bBusyTraffic) + return -EAGAIN; + + if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { + struct iw_scan_req *req = (struct iw_scan_req *)b; + + if (req->essid_len) { + int len = min_t(int, req->essid_len, IW_ESSID_MAX_SIZE); + + ieee->current_network.ssid_len = len; + memcpy(ieee->current_network.ssid, req->essid, len); + } + } + + mutex_lock(&priv->wx_mutex); + + priv->rtllib->FirstIe_InScan = true; + + if (priv->rtllib->link_state != MAC80211_LINKED) { + if (rt_state == rf_off) { + if (priv->rtllib->rf_off_reason > + RF_CHANGE_BY_IPS) { + netdev_warn(dev, "%s(): RF is OFF.\n", + __func__); + mutex_unlock(&priv->wx_mutex); + return -1; + } + mutex_lock(&priv->rtllib->ips_mutex); + rtl92e_ips_leave(dev); + mutex_unlock(&priv->rtllib->ips_mutex); + } + rtllib_stop_scan(priv->rtllib); + if (priv->rtllib->rf_power_state != rf_off) { + priv->rtllib->actscanning = true; + + ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP); + + rtllib_start_scan_syncro(priv->rtllib); + + ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE); + } + ret = 0; + } else { + priv->rtllib->actscanning = true; + ret = rtllib_wx_set_scan(priv->rtllib, a, wrqu, b); + } + + mutex_unlock(&priv->wx_mutex); + return ret; +} + +static int _rtl92e_wx_get_scan(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret; + struct r8192_priv *priv = rtllib_priv(dev); + + if (!priv->up) + return -ENETDOWN; + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_get_scan(priv->rtllib, a, wrqu, b); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_set_essid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int ret; + + if (priv->hw_radio_off) { + netdev_info(dev, + "=========>%s():hw radio off,or Rf state is rf_off, return\n", + __func__); + return 0; + } + mutex_lock(&priv->wx_mutex); + ret = rtllib_wx_set_essid(priv->rtllib, a, wrqu, b); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_get_essid(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret; + struct r8192_priv *priv = rtllib_priv(dev); + + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_get_essid(priv->rtllib, a, wrqu, b); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_set_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (wrqu->data.length > IW_ESSID_MAX_SIZE) + return -E2BIG; + mutex_lock(&priv->wx_mutex); + wrqu->data.length = min_t(size_t, wrqu->data.length, + sizeof(priv->nick)); + memset(priv->nick, 0, sizeof(priv->nick)); + memcpy(priv->nick, extra, wrqu->data.length); + mutex_unlock(&priv->wx_mutex); + return 0; +} + +static int _rtl92e_wx_get_nick(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + mutex_lock(&priv->wx_mutex); + wrqu->data.length = strlen(priv->nick); + memcpy(extra, priv->nick, wrqu->data.length); + wrqu->data.flags = 1; /* active */ + mutex_unlock(&priv->wx_mutex); + return 0; +} + +static int _rtl92e_wx_set_freq(struct net_device *dev, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_set_freq(priv->rtllib, a, wrqu, b); + + mutex_unlock(&priv->wx_mutex); + return ret; +} + +static int _rtl92e_wx_get_name(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_name(priv->rtllib, info, wrqu, extra); +} + +static int _rtl92e_wx_set_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + if (wrqu->frag.disabled) { + priv->rtllib->fts = DEFAULT_FRAG_THRESHOLD; + } else { + if (wrqu->frag.value < MIN_FRAG_THRESHOLD || + wrqu->frag.value > MAX_FRAG_THRESHOLD) + return -EINVAL; + + priv->rtllib->fts = wrqu->frag.value & ~0x1; + } + + return 0; +} + +static int _rtl92e_wx_get_frag(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + wrqu->frag.value = priv->rtllib->fts; + wrqu->frag.fixed = 0; /* no auto select */ + wrqu->frag.disabled = (wrqu->frag.value == DEFAULT_FRAG_THRESHOLD); + + return 0; +} + +static int _rtl92e_wx_set_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *awrq, char *extra) +{ + int ret; + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_set_wap(priv->rtllib, info, awrq, extra); + + mutex_unlock(&priv->wx_mutex); + + return ret; +} + +static int _rtl92e_wx_get_wap(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_wap(priv->rtllib, info, wrqu, extra); +} + +static int _rtl92e_wx_get_enc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + return rtllib_wx_get_encode(priv->rtllib, info, wrqu, key); +} + +static int _rtl92e_wx_set_enc(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int ret; + + struct rtllib_device *ieee = priv->rtllib; + u32 hwkey[4] = {0, 0, 0, 0}; + u8 mask = 0xff; + u32 key_idx = 0; + u8 zero_addr[4][6] = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} }; + int i; + + if (priv->hw_radio_off) + return 0; + + if (!priv->up) + return -ENETDOWN; + + priv->rtllib->wx_set_enc = 1; + mutex_lock(&priv->rtllib->ips_mutex); + rtl92e_ips_leave(dev); + mutex_unlock(&priv->rtllib->ips_mutex); + mutex_lock(&priv->wx_mutex); + + ret = rtllib_wx_set_encode(priv->rtllib, info, wrqu, key); + mutex_unlock(&priv->wx_mutex); + + if (wrqu->encoding.flags & IW_ENCODE_DISABLED) { + ieee->pairwise_key_type = ieee->group_key_type = KEY_TYPE_NA; + rtl92e_cam_reset(dev); + memset(priv->rtllib->swcamtable, 0, + sizeof(struct sw_cam_table) * 32); + goto end_hw_sec; + } + if (wrqu->encoding.length != 0) { + for (i = 0; i < 4; i++) { + hwkey[i] |= key[4 * i + 0] & mask; + if (i == 1 && (4 * i + 1) == wrqu->encoding.length) + mask = 0x00; + if (i == 3 && (4 * i + 1) == wrqu->encoding.length) + mask = 0x00; + hwkey[i] |= (key[4 * i + 1] & mask) << 8; + hwkey[i] |= (key[4 * i + 2] & mask) << 16; + hwkey[i] |= (key[4 * i + 3] & mask) << 24; + } + + switch (wrqu->encoding.flags & IW_ENCODE_INDEX) { + case 0: + key_idx = ieee->crypt_info.tx_keyidx; + break; + case 1: + key_idx = 0; + break; + case 2: + key_idx = 1; + break; + case 3: + key_idx = 2; + break; + case 4: + key_idx = 3; + break; + default: + break; + } + if (wrqu->encoding.length == 0x5) { + ieee->pairwise_key_type = KEY_TYPE_WEP40; + rtl92e_enable_hw_security_config(dev); + } + + else if (wrqu->encoding.length == 0xd) { + ieee->pairwise_key_type = KEY_TYPE_WEP104; + rtl92e_enable_hw_security_config(dev); + rtl92e_set_key(dev, key_idx, key_idx, KEY_TYPE_WEP104, + zero_addr[key_idx], 0, hwkey); + rtl92e_set_swcam(dev, key_idx, key_idx, KEY_TYPE_WEP104, + zero_addr[key_idx], hwkey); + } else { + netdev_info(dev, + "wrong type in WEP, not WEP40 and WEP104\n"); + } + } + +end_hw_sec: + priv->rtllib->wx_set_enc = 0; + return ret; +} + +static int _rtl92e_wx_set_scan_type(struct net_device *dev, + struct iw_request_info *aa, + union iwreq_data *wrqu, char *p) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int *parms = (int *)p; + int mode = parms[0]; + + if (priv->hw_radio_off) + return 0; + + priv->rtllib->active_scan = mode; + + return 1; +} + +#define R8192_MAX_RETRY 255 +static int _rtl92e_wx_set_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + int err = 0; + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + if (wrqu->retry.flags & IW_RETRY_LIFETIME || + wrqu->retry.disabled) { + err = -EINVAL; + goto exit; + } + if (!(wrqu->retry.flags & IW_RETRY_LIMIT)) { + err = -EINVAL; + goto exit; + } + + if (wrqu->retry.value > R8192_MAX_RETRY) { + err = -EINVAL; + goto exit; + } + if (wrqu->retry.flags & IW_RETRY_MAX) + priv->retry_rts = wrqu->retry.value; + else + priv->retry_data = wrqu->retry.value; + + rtl92e_commit(dev); +exit: + mutex_unlock(&priv->wx_mutex); + + return err; +} + +static int _rtl92e_wx_get_retry(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + + wrqu->retry.disabled = 0; /* can't be disabled */ + + if ((wrqu->retry.flags & IW_RETRY_TYPE) == + IW_RETRY_LIFETIME) + return -EINVAL; + + if (wrqu->retry.flags & IW_RETRY_MAX) { + wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + wrqu->retry.value = priv->retry_rts; + } else { + wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; + wrqu->retry.value = priv->retry_data; + } + return 0; +} + +static int _rtl92e_wx_set_encode_ext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + + priv->rtllib->wx_set_enc = 1; + mutex_lock(&priv->rtllib->ips_mutex); + rtl92e_ips_leave(dev); + mutex_unlock(&priv->rtllib->ips_mutex); + + ret = rtllib_wx_set_encode_ext(ieee, info, wrqu, extra); + { + const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 zero[ETH_ALEN] = {0}; + u32 key[4] = {0}; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + struct iw_point *encoding = &wrqu->encoding; + u8 idx = 0, alg = 0, group = 0; + + if ((encoding->flags & IW_ENCODE_DISABLED) || + ext->alg == IW_ENCODE_ALG_NONE) { + ieee->pairwise_key_type = ieee->group_key_type + = KEY_TYPE_NA; + rtl92e_cam_reset(dev); + memset(priv->rtllib->swcamtable, 0, + sizeof(struct sw_cam_table) * 32); + goto end_hw_sec; + } + alg = (ext->alg == IW_ENCODE_ALG_CCMP) ? KEY_TYPE_CCMP : + ext->alg; + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) + idx--; + group = ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY; + + if ((!group) || (ieee->iw_mode == IW_MODE_ADHOC) || + (alg == KEY_TYPE_WEP40)) { + if ((ext->key_len == 13) && (alg == KEY_TYPE_WEP40)) + alg = KEY_TYPE_WEP104; + ieee->pairwise_key_type = alg; + rtl92e_enable_hw_security_config(dev); + } + memcpy((u8 *)key, ext->key, 16); + + if ((alg & KEY_TYPE_WEP40) && (ieee->auth_mode != 2)) { + if (ext->key_len == 13) + ieee->pairwise_key_type = alg = KEY_TYPE_WEP104; + rtl92e_set_key(dev, idx, idx, alg, zero, 0, key); + rtl92e_set_swcam(dev, idx, idx, alg, zero, key); + } else if (group) { + ieee->group_key_type = alg; + rtl92e_set_key(dev, idx, idx, alg, broadcast_addr, 0, + key); + rtl92e_set_swcam(dev, idx, idx, alg, broadcast_addr, key); + } else { + if ((ieee->pairwise_key_type == KEY_TYPE_CCMP) && + ieee->ht_info->bCurrentHTSupport) + rtl92e_writeb(dev, 0x173, 1); + rtl92e_set_key(dev, 4, idx, alg, + (u8 *)ieee->ap_mac_addr, 0, key); + rtl92e_set_swcam(dev, 4, idx, alg, (u8 *)ieee->ap_mac_addr, key); + } + } + +end_hw_sec: + priv->rtllib->wx_set_enc = 0; + mutex_unlock(&priv->wx_mutex); + return ret; +} + +static int _rtl92e_wx_set_auth(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + int ret = 0; + + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + ret = rtllib_wx_set_auth(priv->rtllib, info, &data->param, extra); + mutex_unlock(&priv->wx_mutex); + return ret; +} + +static int _rtl92e_wx_set_mlme(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + ret = rtllib_wx_set_mlme(priv->rtllib, info, wrqu, extra); + mutex_unlock(&priv->wx_mutex); + return ret; +} + +static int _rtl92e_wx_set_gen_ie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + int ret = 0; + + struct r8192_priv *priv = rtllib_priv(dev); + + if (priv->hw_radio_off) + return 0; + + mutex_lock(&priv->wx_mutex); + ret = rtllib_wx_set_gen_ie(priv->rtllib, extra, data->data.length); + mutex_unlock(&priv->wx_mutex); + return ret; +} + +static int _rtl92e_wx_get_gen_ie(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *data, char *extra) +{ + int ret = 0; + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { + data->data.length = 0; + return 0; + } + + if (data->data.length < ieee->wpa_ie_len) + return -E2BIG; + + data->data.length = ieee->wpa_ie_len; + memcpy(extra, ieee->wpa_ie, ieee->wpa_ie_len); + return ret; +} + +#define OID_RT_INTEL_PROMISCUOUS_MODE 0xFF0101F6 + +static int _rtl92e_wx_set_promisc_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + u32 info_buf[3]; + + u32 oid; + u32 promiscuous_on; + u32 fltr_src_sta_frame; + + if (copy_from_user(info_buf, wrqu->data.pointer, sizeof(info_buf))) + return -EFAULT; + + oid = info_buf[0]; + promiscuous_on = info_buf[1]; + fltr_src_sta_frame = info_buf[2]; + + if (oid == OID_RT_INTEL_PROMISCUOUS_MODE) { + ieee->intel_promiscuous_md_info.promiscuous_on = + (promiscuous_on) ? (true) : (false); + ieee->intel_promiscuous_md_info.fltr_src_sta_frame = + (fltr_src_sta_frame) ? (true) : (false); + (promiscuous_on) ? + (rtllib_EnableIntelPromiscuousMode(dev, false)) : + (rtllib_DisableIntelPromiscuousMode(dev, false)); + + netdev_info(dev, + "=======>%s(), on = %d, filter src sta = %d\n", + __func__, promiscuous_on, + fltr_src_sta_frame); + } else { + return -1; + } + + return 0; +} + +static int _rtl92e_wx_get_promisc_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + + mutex_lock(&priv->wx_mutex); + + snprintf(extra, 45, "PromiscuousMode:%d, FilterSrcSTAFrame:%d", + ieee->intel_promiscuous_md_info.promiscuous_on, + ieee->intel_promiscuous_md_info.fltr_src_sta_frame); + wrqu->data.length = strlen(extra) + 1; + + mutex_unlock(&priv->wx_mutex); + + return 0; +} + +#define IW_IOCTL(x) ((x) - SIOCSIWCOMMIT) +static iw_handler r8192_wx_handlers[] = { + [IW_IOCTL(SIOCGIWNAME)] = _rtl92e_wx_get_name, + [IW_IOCTL(SIOCSIWFREQ)] = _rtl92e_wx_set_freq, + [IW_IOCTL(SIOCGIWFREQ)] = _rtl92e_wx_get_freq, + [IW_IOCTL(SIOCSIWMODE)] = _rtl92e_wx_set_mode, + [IW_IOCTL(SIOCGIWMODE)] = _rtl92e_wx_get_mode, + [IW_IOCTL(SIOCGIWRANGE)] = _rtl92e_wx_get_range, + [IW_IOCTL(SIOCSIWAP)] = _rtl92e_wx_set_wap, + [IW_IOCTL(SIOCGIWAP)] = _rtl92e_wx_get_wap, + [IW_IOCTL(SIOCSIWSCAN)] = _rtl92e_wx_set_scan, + [IW_IOCTL(SIOCGIWSCAN)] = _rtl92e_wx_get_scan, + [IW_IOCTL(SIOCSIWESSID)] = _rtl92e_wx_set_essid, + [IW_IOCTL(SIOCGIWESSID)] = _rtl92e_wx_get_essid, + [IW_IOCTL(SIOCSIWNICKN)] = _rtl92e_wx_set_nick, + [IW_IOCTL(SIOCGIWNICKN)] = _rtl92e_wx_get_nick, + [IW_IOCTL(SIOCSIWRATE)] = _rtl92e_wx_set_rate, + [IW_IOCTL(SIOCGIWRATE)] = _rtl92e_wx_get_rate, + [IW_IOCTL(SIOCSIWRTS)] = _rtl92e_wx_set_rts, + [IW_IOCTL(SIOCGIWRTS)] = _rtl92e_wx_get_rts, + [IW_IOCTL(SIOCSIWFRAG)] = _rtl92e_wx_set_frag, + [IW_IOCTL(SIOCGIWFRAG)] = _rtl92e_wx_get_frag, + [IW_IOCTL(SIOCSIWRETRY)] = _rtl92e_wx_set_retry, + [IW_IOCTL(SIOCGIWRETRY)] = _rtl92e_wx_get_retry, + [IW_IOCTL(SIOCSIWENCODE)] = _rtl92e_wx_set_enc, + [IW_IOCTL(SIOCGIWENCODE)] = _rtl92e_wx_get_enc, + [IW_IOCTL(SIOCSIWPOWER)] = _rtl92e_wx_set_power, + [IW_IOCTL(SIOCGIWPOWER)] = _rtl92e_wx_get_power, + [IW_IOCTL(SIOCSIWGENIE)] = _rtl92e_wx_set_gen_ie, + [IW_IOCTL(SIOCGIWGENIE)] = _rtl92e_wx_get_gen_ie, + [IW_IOCTL(SIOCSIWMLME)] = _rtl92e_wx_set_mlme, + [IW_IOCTL(SIOCSIWAUTH)] = _rtl92e_wx_set_auth, + [IW_IOCTL(SIOCSIWENCODEEXT)] = _rtl92e_wx_set_encode_ext, +}; + +/* the following rule need to be following, + * Odd : get (world access), + * even : set (root access) + */ +static const struct iw_priv_args r8192_private_args[] = { + { + SIOCIWFIRSTPRIV + 0x0, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_debugflag" + }, { + SIOCIWFIRSTPRIV + 0x1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "activescan" + }, { + SIOCIWFIRSTPRIV + 0x2, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "rawtx" + }, { + SIOCIWFIRSTPRIV + 0x3, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "forcereset" + }, { + SIOCIWFIRSTPRIV + 0x6, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE, + "set_power" + }, { + SIOCIWFIRSTPRIV + 0xa, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE, + "lps_interv" + }, { + SIOCIWFIRSTPRIV + 0xb, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, IW_PRIV_TYPE_NONE, + "lps_force" + }, { + SIOCIWFIRSTPRIV + 0x16, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, 0, "setpromisc" + }, { + SIOCIWFIRSTPRIV + 0x17, + 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 45, "getpromisc" + } + +}; + +static iw_handler r8192_private_handler[] = { + (iw_handler)_rtl92e_wx_set_debug, /*SIOCIWSECONDPRIV*/ + (iw_handler)_rtl92e_wx_set_scan_type, + (iw_handler)_rtl92e_wx_set_rawtx, + (iw_handler)_rtl92e_wx_force_reset, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)_rtl92e_wx_adapter_power_status, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)_rtl92e_wx_set_lps_awake_interval, + (iw_handler)_rtl92e_wx_set_force_lps, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)NULL, + (iw_handler)_rtl92e_wx_set_promisc_mode, + (iw_handler)_rtl92e_wx_get_promisc_mode, +}; + +static struct iw_statistics *_rtl92e_get_wireless_stats(struct net_device *dev) +{ + struct r8192_priv *priv = rtllib_priv(dev); + struct rtllib_device *ieee = priv->rtllib; + struct iw_statistics *wstats = &priv->wstats; + int tmp_level = 0; + int tmp_qual = 0; + int tmp_noise = 0; + + if (ieee->link_state < MAC80211_LINKED) { + wstats->qual.qual = 10; + wstats->qual.level = 0; + wstats->qual.noise = 0x100 - 100; /* -100 dBm */ + wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + return wstats; + } + + tmp_level = (&ieee->current_network)->stats.rssi; + tmp_qual = (&ieee->current_network)->stats.signal; + tmp_noise = (&ieee->current_network)->stats.noise; + + wstats->qual.level = tmp_level; + wstats->qual.qual = tmp_qual; + wstats->qual.noise = tmp_noise; + wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + return wstats; +} + +const struct iw_handler_def r8192_wx_handlers_def = { + .standard = r8192_wx_handlers, + .num_standard = ARRAY_SIZE(r8192_wx_handlers), + .private = r8192_private_handler, + .num_private = ARRAY_SIZE(r8192_private_handler), + .num_private_args = sizeof(r8192_private_args) / + sizeof(struct iw_priv_args), + .get_wireless_stats = _rtl92e_get_wireless_stats, + .private_args = (struct iw_priv_args *)r8192_private_args, +}; diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_wx.h b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.h new file mode 100644 index 0000000000..d70a747ac1 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/rtl_wx.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef R819x_WX_H +#define R819x_WX_H + +struct iw_handler_def; + +extern const struct iw_handler_def r8192_wx_handlers_def; +#endif diff --git a/drivers/staging/rtl8192e/rtl8192e/table.c b/drivers/staging/rtl8192e/rtl8192e/table.c new file mode 100644 index 0000000000..0b5cc60492 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/table.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "table.h" + +u32 RTL8192E_PHY_REG_1T2R_ARR[RTL8192E_PHY_REG_1T2R_ARR_LEN] = { + 0x800, 0x00000000, + 0x804, 0x00000001, + 0x808, 0x0000fc00, + 0x80c, 0x0000001c, + 0x810, 0x801010aa, + 0x814, 0x008514d0, + 0x818, 0x00000040, + 0x81c, 0x00000000, + 0x820, 0x00000004, + 0x824, 0x00690000, + 0x828, 0x00000004, + 0x82c, 0x00e90000, + 0x830, 0x00000004, + 0x834, 0x00690000, + 0x838, 0x00000004, + 0x83c, 0x00e90000, + 0x840, 0x00000000, + 0x844, 0x00000000, + 0x848, 0x00000000, + 0x84c, 0x00000000, + 0x850, 0x00000000, + 0x854, 0x00000000, + 0x858, 0x65a965a9, + 0x85c, 0x65a965a9, + 0x860, 0x001f0010, + 0x864, 0x007f0010, + 0x868, 0x001f0010, + 0x86c, 0x007f0010, + 0x870, 0x0f100f70, + 0x874, 0x0f100f70, + 0x878, 0x00000000, + 0x87c, 0x00000000, + 0x880, 0x6870e36c, + 0x884, 0xe3573600, + 0x888, 0x4260c340, + 0x88c, 0x0000ff00, + 0x890, 0x00000000, + 0x894, 0xfffffffe, + 0x898, 0x4c42382f, + 0x89c, 0x00656056, + 0x8b0, 0x00000000, + 0x8e0, 0x00000000, + 0x8e4, 0x00000000, + 0x900, 0x00000000, + 0x904, 0x00000023, + 0x908, 0x00000000, + 0x90c, 0x31121311, + 0xa00, 0x00d0c7d8, + 0xa04, 0x811f0008, + 0xa08, 0x80cd8300, + 0xa0c, 0x2e62740f, + 0xa10, 0x95009b78, + 0xa14, 0x11145008, + 0xa18, 0x00881117, + 0xa1c, 0x89140fa0, + 0xa20, 0x1a1b0000, + 0xa24, 0x090e1317, + 0xa28, 0x00000204, + 0xa2c, 0x00000000, + 0xc00, 0x00000040, + 0xc04, 0x00005433, + 0xc08, 0x000000e4, + 0xc0c, 0x6c6c6c6c, + 0xc10, 0x08800000, + 0xc14, 0x40000100, + 0xc18, 0x08000000, + 0xc1c, 0x40000100, + 0xc20, 0x08000000, + 0xc24, 0x40000100, + 0xc28, 0x08000000, + 0xc2c, 0x40000100, + 0xc30, 0x6de9ac44, + 0xc34, 0x465c52cd, + 0xc38, 0x497f5994, + 0xc3c, 0x0a969764, + 0xc40, 0x1f7c403f, + 0xc44, 0x000100b7, + 0xc48, 0xec020000, + 0xc4c, 0x00000300, + 0xc50, 0x69543420, + 0xc54, 0x433c0094, + 0xc58, 0x69543420, + 0xc5c, 0x433c0094, + 0xc60, 0x69543420, + 0xc64, 0x433c0094, + 0xc68, 0x69543420, + 0xc6c, 0x433c0094, + 0xc70, 0x2c7f000d, + 0xc74, 0x0186175b, + 0xc78, 0x0000001f, + 0xc7c, 0x00b91612, + 0xc80, 0x40000100, + 0xc84, 0x20000000, + 0xc88, 0x40000100, + 0xc8c, 0x20200000, + 0xc90, 0x40000100, + 0xc94, 0x00000000, + 0xc98, 0x40000100, + 0xc9c, 0x00000000, + 0xca0, 0x00492492, + 0xca4, 0x00000000, + 0xca8, 0x00000000, + 0xcac, 0x00000000, + 0xcb0, 0x00000000, + 0xcb4, 0x00000000, + 0xcb8, 0x00000000, + 0xcbc, 0x00492492, + 0xcc0, 0x00000000, + 0xcc4, 0x00000000, + 0xcc8, 0x00000000, + 0xccc, 0x00000000, + 0xcd0, 0x00000000, + 0xcd4, 0x00000000, + 0xcd8, 0x64b22427, + 0xcdc, 0x00766932, + 0xce0, 0x00222222, + 0xd00, 0x00000750, + 0xd04, 0x00000403, + 0xd08, 0x0000907f, + 0xd0c, 0x00000001, + 0xd10, 0xa0633333, + 0xd14, 0x33333c63, + 0xd18, 0x6a8f5b6b, + 0xd1c, 0x00000000, + 0xd20, 0x00000000, + 0xd24, 0x00000000, + 0xd28, 0x00000000, + 0xd2c, 0xcc979975, + 0xd30, 0x00000000, + 0xd34, 0x00000000, + 0xd38, 0x00000000, + 0xd3c, 0x00027293, + 0xd40, 0x00000000, + 0xd44, 0x00000000, + 0xd48, 0x00000000, + 0xd4c, 0x00000000, + 0xd50, 0x6437140a, + 0xd54, 0x024dbd02, + 0xd58, 0x00000000, + 0xd5c, 0x04032064, + 0xe00, 0x161a1a1a, + 0xe04, 0x12121416, + 0xe08, 0x00001800, + 0xe0c, 0x00000000, + 0xe10, 0x161a1a1a, + 0xe14, 0x12121416, + 0xe18, 0x161a1a1a, + 0xe1c, 0x12121416, +}; + +u32 RTL8192E_RADIO_A_ARR[RTL8192E_RADIO_A_ARR_LEN] = { + 0x019, 0x00000003, + 0x000, 0x000000bf, + 0x001, 0x00000ee0, + 0x002, 0x0000004c, + 0x003, 0x000007f1, + 0x004, 0x00000975, + 0x005, 0x00000c58, + 0x006, 0x00000ae6, + 0x007, 0x000000ca, + 0x008, 0x00000e1c, + 0x009, 0x000007f0, + 0x00a, 0x000009d0, + 0x00b, 0x000001ba, + 0x00c, 0x00000240, + 0x00e, 0x00000020, + 0x00f, 0x00000990, + 0x012, 0x00000806, + 0x014, 0x000005ab, + 0x015, 0x00000f80, + 0x016, 0x00000020, + 0x017, 0x00000597, + 0x018, 0x0000050a, + 0x01a, 0x00000f80, + 0x01b, 0x00000f5e, + 0x01c, 0x00000008, + 0x01d, 0x00000607, + 0x01e, 0x000006cc, + 0x01f, 0x00000000, + 0x020, 0x000001a5, + 0x01f, 0x00000001, + 0x020, 0x00000165, + 0x01f, 0x00000002, + 0x020, 0x000000c6, + 0x01f, 0x00000003, + 0x020, 0x00000086, + 0x01f, 0x00000004, + 0x020, 0x00000046, + 0x01f, 0x00000005, + 0x020, 0x000001e6, + 0x01f, 0x00000006, + 0x020, 0x000001a6, + 0x01f, 0x00000007, + 0x020, 0x00000166, + 0x01f, 0x00000008, + 0x020, 0x000000c7, + 0x01f, 0x00000009, + 0x020, 0x00000087, + 0x01f, 0x0000000a, + 0x020, 0x000000f7, + 0x01f, 0x0000000b, + 0x020, 0x000000d7, + 0x01f, 0x0000000c, + 0x020, 0x000000b7, + 0x01f, 0x0000000d, + 0x020, 0x00000097, + 0x01f, 0x0000000e, + 0x020, 0x00000077, + 0x01f, 0x0000000f, + 0x020, 0x00000057, + 0x01f, 0x00000010, + 0x020, 0x00000037, + 0x01f, 0x00000011, + 0x020, 0x000000fb, + 0x01f, 0x00000012, + 0x020, 0x000000db, + 0x01f, 0x00000013, + 0x020, 0x000000bb, + 0x01f, 0x00000014, + 0x020, 0x000000ff, + 0x01f, 0x00000015, + 0x020, 0x000000e3, + 0x01f, 0x00000016, + 0x020, 0x000000c3, + 0x01f, 0x00000017, + 0x020, 0x000000a3, + 0x01f, 0x00000018, + 0x020, 0x00000083, + 0x01f, 0x00000019, + 0x020, 0x00000063, + 0x01f, 0x0000001a, + 0x020, 0x00000043, + 0x01f, 0x0000001b, + 0x020, 0x00000023, + 0x01f, 0x0000001c, + 0x020, 0x00000003, + 0x01f, 0x0000001d, + 0x020, 0x000001e3, + 0x01f, 0x0000001e, + 0x020, 0x000001c3, + 0x01f, 0x0000001f, + 0x020, 0x000001a3, + 0x01f, 0x00000020, + 0x020, 0x00000183, + 0x01f, 0x00000021, + 0x020, 0x00000163, + 0x01f, 0x00000022, + 0x020, 0x00000143, + 0x01f, 0x00000023, + 0x020, 0x00000123, + 0x01f, 0x00000024, + 0x020, 0x00000103, + 0x023, 0x00000203, + 0x024, 0x00000100, + 0x00b, 0x000001ba, + 0x02c, 0x000003d7, + 0x02d, 0x00000ff0, + 0x000, 0x00000037, + 0x004, 0x00000160, + 0x007, 0x00000080, + 0x002, 0x0000088d, + 0x0fe, 0x00000000, + 0x0fe, 0x00000000, + 0x016, 0x00000200, + 0x016, 0x00000380, + 0x016, 0x00000020, + 0x016, 0x000001a0, + 0x000, 0x000000bf, + 0x00d, 0x0000001f, + 0x00d, 0x00000c9f, + 0x002, 0x0000004d, + 0x000, 0x00000cbf, + 0x004, 0x00000975, + 0x007, 0x00000700, +}; + +u32 RTL8192E_RADIO_B_ARR[RTL8192E_RADIO_B_ARR_LEN] = { + 0x019, 0x00000003, + 0x000, 0x000000bf, + 0x001, 0x000006e0, + 0x002, 0x0000004c, + 0x003, 0x000007f1, + 0x004, 0x00000975, + 0x005, 0x00000c58, + 0x006, 0x00000ae6, + 0x007, 0x000000ca, + 0x008, 0x00000e1c, + 0x000, 0x000000b7, + 0x00a, 0x00000850, + 0x000, 0x000000bf, + 0x00b, 0x000001ba, + 0x00c, 0x00000240, + 0x00e, 0x00000020, + 0x015, 0x00000f80, + 0x016, 0x00000020, + 0x017, 0x00000597, + 0x018, 0x0000050a, + 0x01a, 0x00000e00, + 0x01b, 0x00000f5e, + 0x01d, 0x00000607, + 0x01e, 0x000006cc, + 0x00b, 0x000001ba, + 0x023, 0x00000203, + 0x024, 0x00000100, + 0x000, 0x00000037, + 0x004, 0x00000160, + 0x016, 0x00000200, + 0x016, 0x00000380, + 0x016, 0x00000020, + 0x016, 0x000001a0, + 0x00d, 0x00000ccc, + 0x000, 0x000000bf, + 0x002, 0x0000004d, + 0x000, 0x00000cbf, + 0x004, 0x00000975, + 0x007, 0x00000700, +}; + +u32 RTL8192E_MACPHY_ARR[] = { + 0x03c, 0xffff0000, 0x00000f0f, + 0x340, 0xffffffff, 0x161a1a1a, + 0x344, 0xffffffff, 0x12121416, + 0x348, 0x0000ffff, 0x00001818, + 0x12c, 0xffffffff, 0x04000802, + 0x318, 0x00000fff, 0x00000100, +}; + +u32 RTL8192E_MACPHY_ARR_PG[] = { + 0x03c, 0xffff0000, 0x00000f0f, + 0xe00, 0xffffffff, 0x06090909, + 0xe04, 0xffffffff, 0x00030306, + 0xe08, 0x0000ff00, 0x00000000, + 0xe10, 0xffffffff, 0x0a0c0d0f, + 0xe14, 0xffffffff, 0x06070809, + 0xe18, 0xffffffff, 0x0a0c0d0f, + 0xe1c, 0xffffffff, 0x06070809, + 0x12c, 0xffffffff, 0x04000802, + 0x318, 0x00000fff, 0x00000800, +}; + +u32 RTL8192E_AGCTAB_ARR[RTL8192E_AGCTAB_ARR_LEN] = { + 0xc78, 0x7d000001, + 0xc78, 0x7d010001, + 0xc78, 0x7d020001, + 0xc78, 0x7d030001, + 0xc78, 0x7d040001, + 0xc78, 0x7d050001, + 0xc78, 0x7c060001, + 0xc78, 0x7b070001, + 0xc78, 0x7a080001, + 0xc78, 0x79090001, + 0xc78, 0x780a0001, + 0xc78, 0x770b0001, + 0xc78, 0x760c0001, + 0xc78, 0x750d0001, + 0xc78, 0x740e0001, + 0xc78, 0x730f0001, + 0xc78, 0x72100001, + 0xc78, 0x71110001, + 0xc78, 0x70120001, + 0xc78, 0x6f130001, + 0xc78, 0x6e140001, + 0xc78, 0x6d150001, + 0xc78, 0x6c160001, + 0xc78, 0x6b170001, + 0xc78, 0x6a180001, + 0xc78, 0x69190001, + 0xc78, 0x681a0001, + 0xc78, 0x671b0001, + 0xc78, 0x661c0001, + 0xc78, 0x651d0001, + 0xc78, 0x641e0001, + 0xc78, 0x491f0001, + 0xc78, 0x48200001, + 0xc78, 0x47210001, + 0xc78, 0x46220001, + 0xc78, 0x45230001, + 0xc78, 0x44240001, + 0xc78, 0x43250001, + 0xc78, 0x28260001, + 0xc78, 0x27270001, + 0xc78, 0x26280001, + 0xc78, 0x25290001, + 0xc78, 0x242a0001, + 0xc78, 0x232b0001, + 0xc78, 0x222c0001, + 0xc78, 0x212d0001, + 0xc78, 0x202e0001, + 0xc78, 0x0a2f0001, + 0xc78, 0x08300001, + 0xc78, 0x06310001, + 0xc78, 0x05320001, + 0xc78, 0x04330001, + 0xc78, 0x03340001, + 0xc78, 0x02350001, + 0xc78, 0x01360001, + 0xc78, 0x00370001, + 0xc78, 0x00380001, + 0xc78, 0x00390001, + 0xc78, 0x003a0001, + 0xc78, 0x003b0001, + 0xc78, 0x003c0001, + 0xc78, 0x003d0001, + 0xc78, 0x003e0001, + 0xc78, 0x003f0001, + 0xc78, 0x7d400001, + 0xc78, 0x7d410001, + 0xc78, 0x7d420001, + 0xc78, 0x7d430001, + 0xc78, 0x7d440001, + 0xc78, 0x7d450001, + 0xc78, 0x7c460001, + 0xc78, 0x7b470001, + 0xc78, 0x7a480001, + 0xc78, 0x79490001, + 0xc78, 0x784a0001, + 0xc78, 0x774b0001, + 0xc78, 0x764c0001, + 0xc78, 0x754d0001, + 0xc78, 0x744e0001, + 0xc78, 0x734f0001, + 0xc78, 0x72500001, + 0xc78, 0x71510001, + 0xc78, 0x70520001, + 0xc78, 0x6f530001, + 0xc78, 0x6e540001, + 0xc78, 0x6d550001, + 0xc78, 0x6c560001, + 0xc78, 0x6b570001, + 0xc78, 0x6a580001, + 0xc78, 0x69590001, + 0xc78, 0x685a0001, + 0xc78, 0x675b0001, + 0xc78, 0x665c0001, + 0xc78, 0x655d0001, + 0xc78, 0x645e0001, + 0xc78, 0x495f0001, + 0xc78, 0x48600001, + 0xc78, 0x47610001, + 0xc78, 0x46620001, + 0xc78, 0x45630001, + 0xc78, 0x44640001, + 0xc78, 0x43650001, + 0xc78, 0x28660001, + 0xc78, 0x27670001, + 0xc78, 0x26680001, + 0xc78, 0x25690001, + 0xc78, 0x246a0001, + 0xc78, 0x236b0001, + 0xc78, 0x226c0001, + 0xc78, 0x216d0001, + 0xc78, 0x206e0001, + 0xc78, 0x0a6f0001, + 0xc78, 0x08700001, + 0xc78, 0x06710001, + 0xc78, 0x05720001, + 0xc78, 0x04730001, + 0xc78, 0x03740001, + 0xc78, 0x02750001, + 0xc78, 0x01760001, + 0xc78, 0x00770001, + 0xc78, 0x00780001, + 0xc78, 0x00790001, + 0xc78, 0x007a0001, + 0xc78, 0x007b0001, + 0xc78, 0x007c0001, + 0xc78, 0x007d0001, + 0xc78, 0x007e0001, + 0xc78, 0x007f0001, + 0xc78, 0x2e00001e, + 0xc78, 0x2e01001e, + 0xc78, 0x2e02001e, + 0xc78, 0x2e03001e, + 0xc78, 0x2e04001e, + 0xc78, 0x2e05001e, + 0xc78, 0x3006001e, + 0xc78, 0x3407001e, + 0xc78, 0x3908001e, + 0xc78, 0x3c09001e, + 0xc78, 0x3f0a001e, + 0xc78, 0x420b001e, + 0xc78, 0x440c001e, + 0xc78, 0x450d001e, + 0xc78, 0x460e001e, + 0xc78, 0x460f001e, + 0xc78, 0x4710001e, + 0xc78, 0x4811001e, + 0xc78, 0x4912001e, + 0xc78, 0x4a13001e, + 0xc78, 0x4b14001e, + 0xc78, 0x4b15001e, + 0xc78, 0x4c16001e, + 0xc78, 0x4d17001e, + 0xc78, 0x4e18001e, + 0xc78, 0x4f19001e, + 0xc78, 0x4f1a001e, + 0xc78, 0x501b001e, + 0xc78, 0x511c001e, + 0xc78, 0x521d001e, + 0xc78, 0x521e001e, + 0xc78, 0x531f001e, + 0xc78, 0x5320001e, + 0xc78, 0x5421001e, + 0xc78, 0x5522001e, + 0xc78, 0x5523001e, + 0xc78, 0x5624001e, + 0xc78, 0x5725001e, + 0xc78, 0x5726001e, + 0xc78, 0x5827001e, + 0xc78, 0x5828001e, + 0xc78, 0x5929001e, + 0xc78, 0x592a001e, + 0xc78, 0x5a2b001e, + 0xc78, 0x5b2c001e, + 0xc78, 0x5c2d001e, + 0xc78, 0x5c2e001e, + 0xc78, 0x5d2f001e, + 0xc78, 0x5e30001e, + 0xc78, 0x5f31001e, + 0xc78, 0x6032001e, + 0xc78, 0x6033001e, + 0xc78, 0x6134001e, + 0xc78, 0x6235001e, + 0xc78, 0x6336001e, + 0xc78, 0x6437001e, + 0xc78, 0x6438001e, + 0xc78, 0x6539001e, + 0xc78, 0x663a001e, + 0xc78, 0x673b001e, + 0xc78, 0x673c001e, + 0xc78, 0x683d001e, + 0xc78, 0x693e001e, + 0xc78, 0x6a3f001e, +}; diff --git a/drivers/staging/rtl8192e/rtl8192e/table.h b/drivers/staging/rtl8192e/rtl8192e/table.h new file mode 100644 index 0000000000..82be44a9d4 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl8192e/table.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef __INC_HAL8192PciE_FW_IMG_H +#define __INC_HAL8192PciE_FW_IMG_H + +/*Created on 2008/11/18, 3: 7*/ + +#include <linux/types.h> + +#define RTL8192E_PHY_REG_1T2R_ARR_LEN 296 +extern u32 RTL8192E_PHY_REG_1T2R_ARR[RTL8192E_PHY_REG_1T2R_ARR_LEN]; +#define RTL8192E_RADIO_A_ARR_LEN 246 +extern u32 RTL8192E_RADIO_A_ARR[RTL8192E_RADIO_A_ARR_LEN]; +#define RTL8192E_RADIO_B_ARR_LEN 78 +extern u32 RTL8192E_RADIO_B_ARR[RTL8192E_RADIO_B_ARR_LEN]; +#define RTL8192E_MACPHY_ARR_LEN 18 +extern u32 RTL8192E_MACPHY_ARR[RTL8192E_MACPHY_ARR_LEN]; +#define RTL8192E_MACPHY_ARR_PG_LEN 30 +extern u32 RTL8192E_MACPHY_ARR_PG[RTL8192E_MACPHY_ARR_PG_LEN]; +#define RTL8192E_AGCTAB_ARR_LEN 384 +extern u32 RTL8192E_AGCTAB_ARR[RTL8192E_AGCTAB_ARR_LEN]; + +#endif diff --git a/drivers/staging/rtl8192e/rtl819x_BA.h b/drivers/staging/rtl8192e/rtl819x_BA.h new file mode 100644 index 0000000000..8a35d7a3ee --- /dev/null +++ b/drivers/staging/rtl8192e/rtl819x_BA.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _BATYPE_H_ +#define _BATYPE_H_ + +#define BA_SETUP_TIMEOUT 200 + +#define BA_POLICY_DELAYED 0 +#define BA_POLICY_IMMEDIATE 1 + +#define ADDBA_STATUS_SUCCESS 0 +#define ADDBA_STATUS_REFUSED 37 +#define ADDBA_STATUS_INVALID_PARAM 38 + +#define DELBA_REASON_END_BA 37 +#define DELBA_REASON_UNKNOWN_BA 38 +#define DELBA_REASON_TIMEOUT 39 +union sequence_control { + u16 short_data; + struct { + u16 frag_num:4; + u16 seq_num:12; + } field; +}; + +union ba_param_set { + u8 char_data[2]; + u16 short_data; + struct { + u16 amsdu_support:1; + u16 ba_policy:1; + u16 tid:4; + u16 buffer_size:10; + } field; +}; + +union delba_param_set { + u8 char_data[2]; + u16 short_data; + struct { + u16 reserved:11; + u16 initiator:1; + u16 tid:4; + } field; +}; + +struct ba_record { + struct timer_list timer; + u8 b_valid; + u8 dialog_token; + union ba_param_set ba_param_set; + u16 ba_timeout_value; + union sequence_control ba_start_seq_ctrl; +}; + +#endif diff --git a/drivers/staging/rtl8192e/rtl819x_BAProc.c b/drivers/staging/rtl8192e/rtl819x_BAProc.c new file mode 100644 index 0000000000..0e3372868f --- /dev/null +++ b/drivers/staging/rtl8192e/rtl819x_BAProc.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include <asm/byteorder.h> +#include <asm/unaligned.h> +#include <linux/etherdevice.h> +#include "rtllib.h" +#include "rtl819x_BA.h" + +static void activate_ba_entry(struct ba_record *pBA, u16 Time) +{ + pBA->b_valid = true; + if (Time != 0) + mod_timer(&pBA->timer, jiffies + msecs_to_jiffies(Time)); +} + +static void deactivate_ba_entry(struct rtllib_device *ieee, struct ba_record *pBA) +{ + pBA->b_valid = false; + del_timer_sync(&pBA->timer); +} + +static u8 tx_ts_delete_ba(struct rtllib_device *ieee, struct tx_ts_record *pTxTs) +{ + struct ba_record *pAdmittedBa = &pTxTs->TxAdmittedBARecord; + struct ba_record *pPendingBa = &pTxTs->TxPendingBARecord; + u8 bSendDELBA = false; + + if (pPendingBa->b_valid) { + deactivate_ba_entry(ieee, pPendingBa); + bSendDELBA = true; + } + + if (pAdmittedBa->b_valid) { + deactivate_ba_entry(ieee, pAdmittedBa); + bSendDELBA = true; + } + return bSendDELBA; +} + +static u8 rx_ts_delete_ba(struct rtllib_device *ieee, struct rx_ts_record *pRxTs) +{ + struct ba_record *pBa = &pRxTs->rx_admitted_ba_record; + u8 bSendDELBA = false; + + if (pBa->b_valid) { + deactivate_ba_entry(ieee, pBa); + bSendDELBA = true; + } + + return bSendDELBA; +} + +void rtllib_reset_ba_entry(struct ba_record *pBA) +{ + pBA->b_valid = false; + pBA->ba_param_set.short_data = 0; + pBA->ba_timeout_value = 0; + pBA->dialog_token = 0; + pBA->ba_start_seq_ctrl.short_data = 0; +} + +static struct sk_buff *rtllib_ADDBA(struct rtllib_device *ieee, u8 *Dst, + struct ba_record *pBA, + u16 StatusCode, u8 type) +{ + struct sk_buff *skb = NULL; + struct rtllib_hdr_3addr *BAReq = NULL; + u8 *tag = NULL; + u16 len = ieee->tx_headroom + 9; + + netdev_dbg(ieee->dev, "%s(): frame(%d) sentd to: %pM, ieee->dev:%p\n", + __func__, type, Dst, ieee->dev); + + if (!pBA) { + netdev_warn(ieee->dev, "pBA is NULL\n"); + return NULL; + } + skb = dev_alloc_skb(len + sizeof(struct rtllib_hdr_3addr)); + if (!skb) + return NULL; + + memset(skb->data, 0, sizeof(struct rtllib_hdr_3addr)); + + skb_reserve(skb, ieee->tx_headroom); + + BAReq = skb_put(skb, sizeof(struct rtllib_hdr_3addr)); + + ether_addr_copy(BAReq->addr1, Dst); + ether_addr_copy(BAReq->addr2, ieee->dev->dev_addr); + + ether_addr_copy(BAReq->addr3, ieee->current_network.bssid); + BAReq->frame_ctl = cpu_to_le16(RTLLIB_STYPE_MANAGE_ACT); + + tag = skb_put(skb, 9); + *tag++ = ACT_CAT_BA; + *tag++ = type; + *tag++ = pBA->dialog_token; + + if (type == ACT_ADDBARSP) { + put_unaligned_le16(StatusCode, tag); + tag += 2; + } + + put_unaligned_le16(pBA->ba_param_set.short_data, tag); + tag += 2; + + put_unaligned_le16(pBA->ba_timeout_value, tag); + tag += 2; + + if (type == ACT_ADDBAREQ) { + memcpy(tag, (u8 *)&pBA->ba_start_seq_ctrl, 2); + tag += 2; + } + +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, skb->data, + __func__, skb->len); +#endif + return skb; +} + +static struct sk_buff *rtllib_DELBA(struct rtllib_device *ieee, u8 *dst, + struct ba_record *pBA, + enum tr_select TxRxSelect, u16 ReasonCode) +{ + union delba_param_set DelbaParamSet; + struct sk_buff *skb = NULL; + struct rtllib_hdr_3addr *Delba = NULL; + u8 *tag = NULL; + u16 len = 6 + ieee->tx_headroom; + + if (net_ratelimit()) + netdev_dbg(ieee->dev, "%s(): ReasonCode(%d) sentd to: %pM\n", + __func__, ReasonCode, dst); + + memset(&DelbaParamSet, 0, 2); + + DelbaParamSet.field.initiator = (TxRxSelect == TX_DIR) ? 1 : 0; + DelbaParamSet.field.tid = pBA->ba_param_set.field.tid; + + skb = dev_alloc_skb(len + sizeof(struct rtllib_hdr_3addr)); + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + Delba = skb_put(skb, sizeof(struct rtllib_hdr_3addr)); + + ether_addr_copy(Delba->addr1, dst); + ether_addr_copy(Delba->addr2, ieee->dev->dev_addr); + ether_addr_copy(Delba->addr3, ieee->current_network.bssid); + Delba->frame_ctl = cpu_to_le16(RTLLIB_STYPE_MANAGE_ACT); + + tag = skb_put(skb, 6); + + *tag++ = ACT_CAT_BA; + *tag++ = ACT_DELBA; + + put_unaligned_le16(DelbaParamSet.short_data, tag); + tag += 2; + + put_unaligned_le16(ReasonCode, tag); + tag += 2; + +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, skb->data, + __func__, skb->len); +#endif + return skb; +} + +static void rtllib_send_ADDBAReq(struct rtllib_device *ieee, u8 *dst, + struct ba_record *pBA) +{ + struct sk_buff *skb; + + skb = rtllib_ADDBA(ieee, dst, pBA, 0, ACT_ADDBAREQ); + + if (skb) + softmac_mgmt_xmit(skb, ieee); + else + netdev_dbg(ieee->dev, "Failed to generate ADDBAReq packet.\n"); +} + +static void rtllib_send_ADDBARsp(struct rtllib_device *ieee, u8 *dst, + struct ba_record *pBA, u16 StatusCode) +{ + struct sk_buff *skb; + + skb = rtllib_ADDBA(ieee, dst, pBA, StatusCode, ACT_ADDBARSP); + if (skb) + softmac_mgmt_xmit(skb, ieee); + else + netdev_dbg(ieee->dev, "Failed to generate ADDBARsp packet.\n"); +} + +static void rtllib_send_DELBA(struct rtllib_device *ieee, u8 *dst, + struct ba_record *pBA, enum tr_select TxRxSelect, + u16 ReasonCode) +{ + struct sk_buff *skb; + + skb = rtllib_DELBA(ieee, dst, pBA, TxRxSelect, ReasonCode); + if (skb) + softmac_mgmt_xmit(skb, ieee); + else + netdev_dbg(ieee->dev, "Failed to generate DELBA packet.\n"); +} + +int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb) +{ + struct rtllib_hdr_3addr *req = NULL; + u16 rc = 0; + u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL; + struct ba_record *pBA = NULL; + union ba_param_set *pBaParamSet = NULL; + u16 *pBaTimeoutVal = NULL; + union sequence_control *pBaStartSeqCtrl = NULL; + struct rx_ts_record *pTS = NULL; + + if (skb->len < sizeof(struct rtllib_hdr_3addr) + 9) { + netdev_warn(ieee->dev, "Invalid skb len in BAREQ(%d / %d)\n", + (int)skb->len, + (int)(sizeof(struct rtllib_hdr_3addr) + 9)); + return -1; + } + +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, __func__, + skb->data, skb->len); +#endif + + req = (struct rtllib_hdr_3addr *)skb->data; + tag = (u8 *)req; + dst = (u8 *)(&req->addr2[0]); + tag += sizeof(struct rtllib_hdr_3addr); + pDialogToken = tag + 2; + pBaParamSet = (union ba_param_set *)(tag + 3); + pBaTimeoutVal = (u16 *)(tag + 5); + pBaStartSeqCtrl = (union sequence_control *)(req + 7); + + if (!ieee->current_network.qos_data.active || + !ieee->ht_info->bCurrentHTSupport || + (ieee->ht_info->iot_action & HT_IOT_ACT_REJECT_ADDBA_REQ)) { + rc = ADDBA_STATUS_REFUSED; + netdev_warn(ieee->dev, + "Failed to reply on ADDBA_REQ as some capability is not ready(%d, %d)\n", + ieee->current_network.qos_data.active, + ieee->ht_info->bCurrentHTSupport); + goto OnADDBAReq_Fail; + } + if (!GetTs(ieee, (struct ts_common_info **)&pTS, dst, + (u8)(pBaParamSet->field.tid), RX_DIR, true)) { + rc = ADDBA_STATUS_REFUSED; + netdev_warn(ieee->dev, "%s(): can't get TS\n", __func__); + goto OnADDBAReq_Fail; + } + pBA = &pTS->rx_admitted_ba_record; + + if (pBaParamSet->field.ba_policy == BA_POLICY_DELAYED) { + rc = ADDBA_STATUS_INVALID_PARAM; + netdev_warn(ieee->dev, "%s(): BA Policy is not correct\n", + __func__); + goto OnADDBAReq_Fail; + } + + rtllib_FlushRxTsPendingPkts(ieee, pTS); + + deactivate_ba_entry(ieee, pBA); + pBA->dialog_token = *pDialogToken; + pBA->ba_param_set = *pBaParamSet; + pBA->ba_timeout_value = *pBaTimeoutVal; + pBA->ba_start_seq_ctrl = *pBaStartSeqCtrl; + + if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev) || + (ieee->ht_info->iot_action & HT_IOT_ACT_ALLOW_PEER_AGG_ONE_PKT)) + pBA->ba_param_set.field.buffer_size = 1; + else + pBA->ba_param_set.field.buffer_size = 32; + + activate_ba_entry(pBA, 0); + rtllib_send_ADDBARsp(ieee, dst, pBA, ADDBA_STATUS_SUCCESS); + + return 0; + +OnADDBAReq_Fail: + { + struct ba_record BA; + + BA.ba_param_set = *pBaParamSet; + BA.ba_timeout_value = *pBaTimeoutVal; + BA.dialog_token = *pDialogToken; + BA.ba_param_set.field.ba_policy = BA_POLICY_IMMEDIATE; + rtllib_send_ADDBARsp(ieee, dst, &BA, rc); + return 0; + } +} + +int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb) +{ + struct rtllib_hdr_3addr *rsp = NULL; + struct ba_record *pPendingBA, *pAdmittedBA; + struct tx_ts_record *pTS = NULL; + u8 *dst = NULL, *pDialogToken = NULL, *tag = NULL; + u16 *pStatusCode = NULL, *pBaTimeoutVal = NULL; + union ba_param_set *pBaParamSet = NULL; + u16 ReasonCode; + + if (skb->len < sizeof(struct rtllib_hdr_3addr) + 9) { + netdev_warn(ieee->dev, "Invalid skb len in BARSP(%d / %d)\n", + (int)skb->len, + (int)(sizeof(struct rtllib_hdr_3addr) + 9)); + return -1; + } + rsp = (struct rtllib_hdr_3addr *)skb->data; + tag = (u8 *)rsp; + dst = (u8 *)(&rsp->addr2[0]); + tag += sizeof(struct rtllib_hdr_3addr); + pDialogToken = tag + 2; + pStatusCode = (u16 *)(tag + 3); + pBaParamSet = (union ba_param_set *)(tag + 5); + pBaTimeoutVal = (u16 *)(tag + 7); + + if (!ieee->current_network.qos_data.active || + !ieee->ht_info->bCurrentHTSupport || + !ieee->ht_info->bCurrentAMPDUEnable) { + netdev_warn(ieee->dev, + "reject to ADDBA_RSP as some capability is not ready(%d, %d, %d)\n", + ieee->current_network.qos_data.active, + ieee->ht_info->bCurrentHTSupport, + ieee->ht_info->bCurrentAMPDUEnable); + ReasonCode = DELBA_REASON_UNKNOWN_BA; + goto OnADDBARsp_Reject; + } + + if (!GetTs(ieee, (struct ts_common_info **)&pTS, dst, + (u8)(pBaParamSet->field.tid), TX_DIR, false)) { + netdev_warn(ieee->dev, "%s(): can't get TS\n", __func__); + ReasonCode = DELBA_REASON_UNKNOWN_BA; + goto OnADDBARsp_Reject; + } + + pTS->bAddBaReqInProgress = false; + pPendingBA = &pTS->TxPendingBARecord; + pAdmittedBA = &pTS->TxAdmittedBARecord; + + if (pAdmittedBA->b_valid) { + netdev_dbg(ieee->dev, "%s(): ADDBA response already admitted\n", + __func__); + return -1; + } else if (!pPendingBA->b_valid || + (*pDialogToken != pPendingBA->dialog_token)) { + netdev_warn(ieee->dev, + "%s(): ADDBA Rsp. BA invalid, DELBA!\n", + __func__); + ReasonCode = DELBA_REASON_UNKNOWN_BA; + goto OnADDBARsp_Reject; + } else { + netdev_dbg(ieee->dev, + "%s(): Recv ADDBA Rsp. BA is admitted! Status code:%X\n", + __func__, *pStatusCode); + deactivate_ba_entry(ieee, pPendingBA); + } + + if (*pStatusCode == ADDBA_STATUS_SUCCESS) { + if (pBaParamSet->field.ba_policy == BA_POLICY_DELAYED) { + pTS->bAddBaReqDelayed = true; + deactivate_ba_entry(ieee, pAdmittedBA); + ReasonCode = DELBA_REASON_END_BA; + goto OnADDBARsp_Reject; + } + + pAdmittedBA->dialog_token = *pDialogToken; + pAdmittedBA->ba_timeout_value = *pBaTimeoutVal; + pAdmittedBA->ba_start_seq_ctrl = pPendingBA->ba_start_seq_ctrl; + pAdmittedBA->ba_param_set = *pBaParamSet; + deactivate_ba_entry(ieee, pAdmittedBA); + activate_ba_entry(pAdmittedBA, *pBaTimeoutVal); + } else { + pTS->bAddBaReqDelayed = true; + pTS->bDisable_AddBa = true; + ReasonCode = DELBA_REASON_END_BA; + goto OnADDBARsp_Reject; + } + + return 0; + +OnADDBARsp_Reject: + { + struct ba_record BA; + + BA.ba_param_set = *pBaParamSet; + rtllib_send_DELBA(ieee, dst, &BA, TX_DIR, ReasonCode); + return 0; + } +} + +int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb) +{ + struct rtllib_hdr_3addr *delba = NULL; + union delba_param_set *pDelBaParamSet = NULL; + u8 *dst = NULL; + + if (skb->len < sizeof(struct rtllib_hdr_3addr) + 6) { + netdev_warn(ieee->dev, "Invalid skb len in DELBA(%d / %d)\n", + (int)skb->len, + (int)(sizeof(struct rtllib_hdr_3addr) + 6)); + return -1; + } + + if (!ieee->current_network.qos_data.active || + !ieee->ht_info->bCurrentHTSupport) { + netdev_warn(ieee->dev, + "received DELBA while QOS or HT is not supported(%d, %d)\n", + ieee->current_network. qos_data.active, + ieee->ht_info->bCurrentHTSupport); + return -1; + } + +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("%s: ", DUMP_PREFIX_NONE, skb->data, + __func__, skb->len); +#endif + delba = (struct rtllib_hdr_3addr *)skb->data; + dst = (u8 *)(&delba->addr2[0]); + pDelBaParamSet = (union delba_param_set *)&delba->payload[2]; + + if (pDelBaParamSet->field.initiator == 1) { + struct rx_ts_record *pRxTs; + + if (!GetTs(ieee, (struct ts_common_info **)&pRxTs, dst, + (u8)pDelBaParamSet->field.tid, RX_DIR, false)) { + netdev_warn(ieee->dev, + "%s(): can't get TS for RXTS. dst:%pM TID:%d\n", + __func__, dst, + (u8)pDelBaParamSet->field.tid); + return -1; + } + + rx_ts_delete_ba(ieee, pRxTs); + } else { + struct tx_ts_record *pTxTs; + + if (!GetTs(ieee, (struct ts_common_info **)&pTxTs, dst, + (u8)pDelBaParamSet->field.tid, TX_DIR, false)) { + netdev_warn(ieee->dev, "%s(): can't get TS for TXTS\n", + __func__); + return -1; + } + + pTxTs->bUsingBa = false; + pTxTs->bAddBaReqInProgress = false; + pTxTs->bAddBaReqDelayed = false; + del_timer_sync(&pTxTs->TsAddBaTimer); + tx_ts_delete_ba(ieee, pTxTs); + } + return 0; +} + +void rtllib_ts_init_add_ba(struct rtllib_device *ieee, struct tx_ts_record *pTS, + u8 Policy, u8 bOverwritePending) +{ + struct ba_record *pBA = &pTS->TxPendingBARecord; + + if (pBA->b_valid && !bOverwritePending) + return; + + deactivate_ba_entry(ieee, pBA); + + pBA->dialog_token++; + pBA->ba_param_set.field.amsdu_support = 0; + pBA->ba_param_set.field.ba_policy = Policy; + pBA->ba_param_set.field.tid = pTS->TsCommonInfo.TSpec.f.TSInfo.field.ucTSID; + pBA->ba_param_set.field.buffer_size = 32; + pBA->ba_timeout_value = 0; + pBA->ba_start_seq_ctrl.field.seq_num = (pTS->TxCurSeq + 3) % 4096; + + activate_ba_entry(pBA, BA_SETUP_TIMEOUT); + + rtllib_send_ADDBAReq(ieee, pTS->TsCommonInfo.Addr, pBA); +} + +void rtllib_ts_init_del_ba(struct rtllib_device *ieee, + struct ts_common_info *pTsCommonInfo, + enum tr_select TxRxSelect) +{ + if (TxRxSelect == TX_DIR) { + struct tx_ts_record *pTxTs = + (struct tx_ts_record *)pTsCommonInfo; + + if (tx_ts_delete_ba(ieee, pTxTs)) + rtllib_send_DELBA(ieee, pTsCommonInfo->Addr, + (pTxTs->TxAdmittedBARecord.b_valid) ? + (&pTxTs->TxAdmittedBARecord) : + (&pTxTs->TxPendingBARecord), + TxRxSelect, DELBA_REASON_END_BA); + } else if (TxRxSelect == RX_DIR) { + struct rx_ts_record *pRxTs = + (struct rx_ts_record *)pTsCommonInfo; + if (rx_ts_delete_ba(ieee, pRxTs)) + rtllib_send_DELBA(ieee, pTsCommonInfo->Addr, + &pRxTs->rx_admitted_ba_record, + TxRxSelect, DELBA_REASON_END_BA); + } +} + +void rtllib_ba_setup_timeout(struct timer_list *t) +{ + struct tx_ts_record *pTxTs = from_timer(pTxTs, t, + TxPendingBARecord.timer); + + pTxTs->bAddBaReqInProgress = false; + pTxTs->bAddBaReqDelayed = true; + pTxTs->TxPendingBARecord.b_valid = false; +} + +void rtllib_tx_ba_inact_timeout(struct timer_list *t) +{ + struct tx_ts_record *pTxTs = from_timer(pTxTs, t, + TxAdmittedBARecord.timer); + struct rtllib_device *ieee = container_of(pTxTs, struct rtllib_device, + TxTsRecord[pTxTs->num]); + tx_ts_delete_ba(ieee, pTxTs); + rtllib_send_DELBA(ieee, pTxTs->TsCommonInfo.Addr, + &pTxTs->TxAdmittedBARecord, TX_DIR, + DELBA_REASON_TIMEOUT); +} + +void rtllib_rx_ba_inact_timeout(struct timer_list *t) +{ + struct rx_ts_record *pRxTs = from_timer(pRxTs, t, + rx_admitted_ba_record.timer); + struct rtllib_device *ieee = container_of(pRxTs, struct rtllib_device, + RxTsRecord[pRxTs->num]); + + rx_ts_delete_ba(ieee, pRxTs); + rtllib_send_DELBA(ieee, pRxTs->ts_common_info.Addr, + &pRxTs->rx_admitted_ba_record, RX_DIR, + DELBA_REASON_TIMEOUT); +} diff --git a/drivers/staging/rtl8192e/rtl819x_HT.h b/drivers/staging/rtl8192e/rtl819x_HT.h new file mode 100644 index 0000000000..2bbd010485 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl819x_HT.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _RTL819XU_HTTYPE_H_ +#define _RTL819XU_HTTYPE_H_ + +#define MIMO_PS_STATIC 0 + +#define sHTCLng 4 + +enum ht_channel_width { + HT_CHANNEL_WIDTH_20 = 0, + HT_CHANNEL_WIDTH_20_40 = 1, +}; + +enum ht_extchnl_offset { + HT_EXTCHNL_OFFSET_NO_EXT = 0, + HT_EXTCHNL_OFFSET_UPPER = 1, + HT_EXTCHNL_OFFSET_NO_DEF = 2, + HT_EXTCHNL_OFFSET_LOWER = 3, +}; + +struct ht_capab_ele { + u8 AdvCoding:1; + u8 ChlWidth:1; + u8 MimoPwrSave:2; + u8 GreenField:1; + u8 ShortGI20Mhz:1; + u8 ShortGI40Mhz:1; + u8 TxSTBC:1; + u8 RxSTBC:2; + u8 DelayBA:1; + u8 MaxAMSDUSize:1; + u8 DssCCk:1; + u8 PSMP:1; + u8 Rsvd1:1; + u8 LSigTxopProtect:1; + + u8 MaxRxAMPDUFactor:2; + u8 MPDUDensity:3; + u8 Rsvd2:3; + + u8 MCS[16]; + + u16 ExtHTCapInfo; + + u8 TxBFCap[4]; + + u8 ASCap; + +} __packed; + +struct ht_info_ele { + u8 ControlChl; + + u8 ExtChlOffset:2; + u8 RecommemdedTxWidth:1; + u8 RIFS:1; + u8 PSMPAccessOnly:1; + u8 SrvIntGranularity:3; + + u8 OptMode:2; + u8 NonGFDevPresent:1; + u8 Revd1:5; + u8 Revd2:8; + + u8 Rsvd3:6; + u8 DualBeacon:1; + u8 DualCTSProtect:1; + + u8 SecondaryBeacon:1; + u8 LSigTxopProtectFull:1; + u8 PcoActive:1; + u8 PcoPhase:1; + u8 Rsvd4:4; + + u8 BasicMSC[16]; +} __packed; + +enum ht_spec_ver { + HT_SPEC_VER_IEEE = 0, + HT_SPEC_VER_EWC = 1, +}; + +enum ht_aggre_mode { + HT_AGG_AUTO = 0, + HT_AGG_FORCE_ENABLE = 1, + HT_AGG_FORCE_DISABLE = 2, +}; + +struct rt_hi_throughput { + u8 enable_ht; + u8 bCurrentHTSupport; + u8 bRegBW40MHz; + u8 bCurBW40MHz; + u8 bRegShortGI40MHz; + u8 bCurShortGI40MHz; + u8 bRegShortGI20MHz; + u8 bCurShortGI20MHz; + u8 bRegSuppCCK; + u8 bCurSuppCCK; + enum ht_spec_ver ePeerHTSpecVer; + struct ht_capab_ele SelfHTCap; + struct ht_info_ele SelfHTInfo; + u8 PeerHTCapBuf[32]; + u8 PeerHTInfoBuf[32]; + u8 bAMSDU_Support; + u16 nAMSDU_MaxSize; + u8 bCurrent_AMSDU_Support; + u16 nCurrent_AMSDU_MaxSize; + u8 bAMPDUEnable; + u8 bCurrentAMPDUEnable; + u8 AMPDU_Factor; + u8 CurrentAMPDUFactor; + u8 MPDU_Density; + u8 current_mpdu_density; + enum ht_aggre_mode ForcedAMPDUMode; + u8 forced_ampdu_factor; + u8 forced_mpdu_density; + enum ht_aggre_mode ForcedAMSDUMode; + u8 forced_short_gi; + u8 current_op_mode; + u8 self_mimo_ps; + u8 peer_mimo_ps; + enum ht_extchnl_offset CurSTAExtChnlOffset; + u8 cur_tx_bw40mhz; + u8 sw_bw_in_progress; + u8 reg_rt2rt_aggregation; + u8 RT2RT_HT_Mode; + u8 current_rt2rt_aggregation; + u8 current_rt2rt_long_slot_time; + u8 sz_rt2rt_agg_buf[10]; + u8 reg_rx_reorder_enable; + u8 cur_rx_reorder_enable; + u8 rx_reorder_win_size; + u8 rx_reorder_pending_time; + u16 rx_reorder_drop_counter; + u8 IOTPeer; + u32 iot_action; + u8 iot_ra_func; +} __packed; + +struct bss_ht { + u8 bd_support_ht; + + u8 bd_ht_cap_buf[32]; + u16 bd_ht_cap_len; + u8 bd_ht_info_buf[32]; + u16 bd_ht_info_len; + + enum ht_spec_ver bd_ht_spec_ver; + enum ht_channel_width bd_bandwidth; + + u8 bd_rt2rt_aggregation; + u8 bd_rt2rt_long_slot_time; + u8 rt2rt_ht_mode; + u8 bd_ht_1r; +}; + +extern u8 MCS_FILTER_ALL[16]; +extern u8 MCS_FILTER_1SS[16]; + +#define RATE_ADPT_1SS_MASK 0xFF +#define RATE_ADPT_2SS_MASK 0xF0 +#define RATE_ADPT_MCS32_MASK 0x01 + +enum ht_aggre_size { + HT_AGG_SIZE_8K = 0, + HT_AGG_SIZE_16K = 1, + HT_AGG_SIZE_32K = 2, + HT_AGG_SIZE_64K = 3, +}; + +enum ht_iot_peer { + HT_IOT_PEER_UNKNOWN = 0, + HT_IOT_PEER_REALTEK = 1, + HT_IOT_PEER_REALTEK_92SE = 2, + HT_IOT_PEER_BROADCOM = 3, + HT_IOT_PEER_RALINK = 4, + HT_IOT_PEER_ATHEROS = 5, + HT_IOT_PEER_CISCO = 6, + HT_IOT_PEER_MARVELL = 7, + HT_IOT_PEER_92U_SOFTAP = 8, + HT_IOT_PEER_SELF_SOFTAP = 9, + HT_IOT_PEER_AIRGO = 10, + HT_IOT_PEER_MAX = 11, +}; + +enum ht_iot_action { + HT_IOT_ACT_TX_USE_AMSDU_4K = 0x00000001, + HT_IOT_ACT_TX_USE_AMSDU_8K = 0x00000002, + HT_IOT_ACT_DISABLE_MCS14 = 0x00000004, + HT_IOT_ACT_DISABLE_MCS15 = 0x00000008, + HT_IOT_ACT_DISABLE_ALL_2SS = 0x00000010, + HT_IOT_ACT_DISABLE_EDCA_TURBO = 0x00000020, + HT_IOT_ACT_MGNT_USE_CCK_6M = 0x00000040, + HT_IOT_ACT_CDD_FSYNC = 0x00000080, + HT_IOT_ACT_PURE_N_MODE = 0x00000100, + HT_IOT_ACT_FORCED_CTS2SELF = 0x00000200, + HT_IOT_ACT_FORCED_RTS = 0x00000400, + HT_IOT_ACT_AMSDU_ENABLE = 0x00000800, + HT_IOT_ACT_REJECT_ADDBA_REQ = 0x00001000, + HT_IOT_ACT_ALLOW_PEER_AGG_ONE_PKT = 0x00002000, + HT_IOT_ACT_EDCA_BIAS_ON_RX = 0x00004000, + + HT_IOT_ACT_HYBRID_AGGREGATION = 0x00010000, + HT_IOT_ACT_DISABLE_SHORT_GI = 0x00020000, + HT_IOT_ACT_DISABLE_HIGH_POWER = 0x00040000, + HT_IOT_ACT_DISABLE_TX_40_MHZ = 0x00080000, + HT_IOT_ACT_TX_NO_AGGREGATION = 0x00100000, + HT_IOT_ACT_DISABLE_TX_2SS = 0x00200000, + + HT_IOT_ACT_MID_HIGHPOWER = 0x00400000, + HT_IOT_ACT_NULL_DATA_POWER_SAVING = 0x00800000, + + HT_IOT_ACT_DISABLE_CCK_RATE = 0x01000000, + HT_IOT_ACT_FORCED_ENABLE_BE_TXOP = 0x02000000, + HT_IOT_ACT_WA_IOT_Broadcom = 0x04000000, + + HT_IOT_ACT_DISABLE_RX_40MHZ_SHORT_GI = 0x08000000, + +}; + +enum ht_iot_rafunc { + HT_IOT_RAFUNC_DISABLE_ALL = 0x00, + HT_IOT_RAFUNC_PEER_1R = 0x01, + HT_IOT_RAFUNC_TX_AMSDU = 0x02, +}; + +enum rt_ht_capability { + RT_HT_CAP_USE_TURBO_AGGR = 0x01, + RT_HT_CAP_USE_LONG_PREAMBLE = 0x02, + RT_HT_CAP_USE_AMPDU = 0x04, + RT_HT_CAP_USE_WOW = 0x8, + RT_HT_CAP_USE_SOFTAP = 0x10, + RT_HT_CAP_USE_92SE = 0x20, +}; + +#endif diff --git a/drivers/staging/rtl8192e/rtl819x_HTProc.c b/drivers/staging/rtl8192e/rtl819x_HTProc.c new file mode 100644 index 0000000000..f19feea461 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl819x_HTProc.c @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtllib.h" +#include "rtl819x_HT.h" +u8 MCS_FILTER_ALL[16] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +u8 MCS_FILTER_1SS[16] = { + 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} +; + +u16 MCS_DATA_RATE[2][2][77] = { + {{13, 26, 39, 52, 78, 104, 117, 130, 26, 52, 78, 104, 156, 208, 234, + 260, 39, 78, 117, 234, 312, 351, 390, 52, 104, 156, 208, 312, 416, + 468, 520, 0, 78, 104, 130, 117, 156, 195, 104, 130, 130, 156, 182, + 182, 208, 156, 195, 195, 234, 273, 273, 312, 130, 156, 181, 156, + 181, 208, 234, 208, 234, 260, 260, 286, 195, 234, 273, 234, 273, + 312, 351, 312, 351, 390, 390, 429}, + {14, 29, 43, 58, 87, 116, 130, 144, 29, 58, 87, 116, 173, 231, 260, 289, + 43, 87, 130, 173, 260, 347, 390, 433, 58, 116, 173, 231, 347, 462, 520, + 578, 0, 87, 116, 144, 130, 173, 217, 116, 144, 144, 173, 202, 202, 231, + 173, 217, 217, 260, 303, 303, 347, 144, 173, 202, 173, 202, 231, 260, + 231, 260, 289, 289, 318, 217, 260, 303, 260, 303, 347, 390, 347, 390, + 433, 433, 477} }, + {{27, 54, 81, 108, 162, 216, 243, 270, 54, 108, 162, 216, 324, 432, 486, + 540, 81, 162, 243, 324, 486, 648, 729, 810, 108, 216, 324, 432, 648, + 864, 972, 1080, 12, 162, 216, 270, 243, 324, 405, 216, 270, 270, 324, + 378, 378, 432, 324, 405, 405, 486, 567, 567, 648, 270, 324, 378, 324, + 378, 432, 486, 432, 486, 540, 540, 594, 405, 486, 567, 486, 567, 648, + 729, 648, 729, 810, 810, 891}, + {30, 60, 90, 120, 180, 240, 270, 300, 60, 120, 180, 240, 360, 480, 540, + 600, 90, 180, 270, 360, 540, 720, 810, 900, 120, 240, 360, 480, 720, + 960, 1080, 1200, 13, 180, 240, 300, 270, 360, 450, 240, 300, 300, 360, + 420, 420, 480, 360, 450, 450, 540, 630, 630, 720, 300, 360, 420, 360, + 420, 480, 540, 480, 540, 600, 600, 660, 450, 540, 630, 540, 630, 720, + 810, 720, 810, 900, 900, 990} } +}; + +static u8 UNKNOWN_BORADCOM[3] = {0x00, 0x14, 0xbf}; + +static u8 LINKSYSWRT330_LINKSYSWRT300_BROADCOM[3] = {0x00, 0x1a, 0x70}; + +static u8 LINKSYSWRT350_LINKSYSWRT150_BROADCOM[3] = {0x00, 0x1d, 0x7e}; + +static u8 BELKINF5D8233V1_RALINK[3] = {0x00, 0x17, 0x3f}; + +static u8 BELKINF5D82334V3_RALINK[3] = {0x00, 0x1c, 0xdf}; + +static u8 PCI_RALINK[3] = {0x00, 0x90, 0xcc}; + +static u8 EDIMAX_RALINK[3] = {0x00, 0x0e, 0x2e}; + +static u8 AIRLINK_RALINK[3] = {0x00, 0x18, 0x02}; + +static u8 DLINK_ATHEROS_1[3] = {0x00, 0x1c, 0xf0}; + +static u8 DLINK_ATHEROS_2[3] = {0x00, 0x21, 0x91}; + +static u8 CISCO_BROADCOM[3] = {0x00, 0x17, 0x94}; + +static u8 LINKSYS_MARVELL_4400N[3] = {0x00, 0x14, 0xa4}; + +void HTUpdateDefaultSetting(struct rtllib_device *ieee) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + ht_info->bRegShortGI20MHz = 1; + ht_info->bRegShortGI40MHz = 1; + + ht_info->bRegBW40MHz = 1; + + if (ht_info->bRegBW40MHz) + ht_info->bRegSuppCCK = 1; + else + ht_info->bRegSuppCCK = true; + + ht_info->nAMSDU_MaxSize = 7935UL; + ht_info->bAMSDU_Support = 0; + + ht_info->bAMPDUEnable = 1; + ht_info->AMPDU_Factor = 2; + ht_info->MPDU_Density = 0; + + ht_info->self_mimo_ps = 3; + if (ht_info->self_mimo_ps == 2) + ht_info->self_mimo_ps = 3; + ieee->tx_dis_rate_fallback = 0; + ieee->tx_use_drv_assinged_rate = 0; + + ieee->bTxEnableFwCalcDur = 1; + + ht_info->reg_rt2rt_aggregation = 1; + + ht_info->reg_rx_reorder_enable = 1; + ht_info->rx_reorder_win_size = 64; + ht_info->rx_reorder_pending_time = 30; +} + +static u16 HTMcsToDataRate(struct rtllib_device *ieee, u8 nMcsRate) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + u8 is40MHz = (ht_info->bCurBW40MHz) ? 1 : 0; + u8 isShortGI = (ht_info->bCurBW40MHz) ? + ((ht_info->bCurShortGI40MHz) ? 1 : 0) : + ((ht_info->bCurShortGI20MHz) ? 1 : 0); + return MCS_DATA_RATE[is40MHz][isShortGI][(nMcsRate & 0x7f)]; +} + +u16 TxCountToDataRate(struct rtllib_device *ieee, u8 nDataRate) +{ + u16 CCKOFDMRate[12] = {0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, + 0x24, 0x30, 0x48, 0x60, 0x6c}; + u8 is40MHz = 0; + u8 isShortGI = 0; + + if (nDataRate < 12) + return CCKOFDMRate[nDataRate]; + if (nDataRate >= 0x10 && nDataRate <= 0x1f) { + is40MHz = 0; + isShortGI = 0; + } else if (nDataRate >= 0x20 && nDataRate <= 0x2f) { + is40MHz = 1; + isShortGI = 0; + } else if (nDataRate >= 0x30 && nDataRate <= 0x3f) { + is40MHz = 0; + isShortGI = 1; + } else if (nDataRate >= 0x40 && nDataRate <= 0x4f) { + is40MHz = 1; + isShortGI = 1; + } + return MCS_DATA_RATE[is40MHz][isShortGI][nDataRate & 0xf]; +} + +bool IsHTHalfNmodeAPs(struct rtllib_device *ieee) +{ + bool retValue = false; + struct rtllib_network *net = &ieee->current_network; + + if ((memcmp(net->bssid, BELKINF5D8233V1_RALINK, 3) == 0) || + (memcmp(net->bssid, BELKINF5D82334V3_RALINK, 3) == 0) || + (memcmp(net->bssid, PCI_RALINK, 3) == 0) || + (memcmp(net->bssid, EDIMAX_RALINK, 3) == 0) || + (memcmp(net->bssid, AIRLINK_RALINK, 3) == 0) || + (net->ralink_cap_exist)) + retValue = true; + else if (!memcmp(net->bssid, UNKNOWN_BORADCOM, 3) || + !memcmp(net->bssid, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3) || + !memcmp(net->bssid, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3) || + (net->broadcom_cap_exist)) + retValue = true; + else if (net->bssht.bd_rt2rt_aggregation) + retValue = true; + else + retValue = false; + + return retValue; +} + +static void HTIOTPeerDetermine(struct rtllib_device *ieee) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + struct rtllib_network *net = &ieee->current_network; + + if (net->bssht.bd_rt2rt_aggregation) { + ht_info->IOTPeer = HT_IOT_PEER_REALTEK; + if (net->bssht.rt2rt_ht_mode & RT_HT_CAP_USE_92SE) + ht_info->IOTPeer = HT_IOT_PEER_REALTEK_92SE; + if (net->bssht.rt2rt_ht_mode & RT_HT_CAP_USE_SOFTAP) + ht_info->IOTPeer = HT_IOT_PEER_92U_SOFTAP; + } else if (net->broadcom_cap_exist) { + ht_info->IOTPeer = HT_IOT_PEER_BROADCOM; + } else if (!memcmp(net->bssid, UNKNOWN_BORADCOM, 3) || + !memcmp(net->bssid, LINKSYSWRT330_LINKSYSWRT300_BROADCOM, 3) || + !memcmp(net->bssid, LINKSYSWRT350_LINKSYSWRT150_BROADCOM, 3)) { + ht_info->IOTPeer = HT_IOT_PEER_BROADCOM; + } else if ((memcmp(net->bssid, BELKINF5D8233V1_RALINK, 3) == 0) || + (memcmp(net->bssid, BELKINF5D82334V3_RALINK, 3) == 0) || + (memcmp(net->bssid, PCI_RALINK, 3) == 0) || + (memcmp(net->bssid, EDIMAX_RALINK, 3) == 0) || + (memcmp(net->bssid, AIRLINK_RALINK, 3) == 0) || + net->ralink_cap_exist) { + ht_info->IOTPeer = HT_IOT_PEER_RALINK; + } else if ((net->atheros_cap_exist) || + (memcmp(net->bssid, DLINK_ATHEROS_1, 3) == 0) || + (memcmp(net->bssid, DLINK_ATHEROS_2, 3) == 0)) { + ht_info->IOTPeer = HT_IOT_PEER_ATHEROS; + } else if ((memcmp(net->bssid, CISCO_BROADCOM, 3) == 0) || + net->cisco_cap_exist) { + ht_info->IOTPeer = HT_IOT_PEER_CISCO; + } else if ((memcmp(net->bssid, LINKSYS_MARVELL_4400N, 3) == 0) || + net->marvell_cap_exist) { + ht_info->IOTPeer = HT_IOT_PEER_MARVELL; + } else if (net->airgo_cap_exist) { + ht_info->IOTPeer = HT_IOT_PEER_AIRGO; + } else { + ht_info->IOTPeer = HT_IOT_PEER_UNKNOWN; + } + + netdev_dbg(ieee->dev, "IOTPEER: %x\n", ht_info->IOTPeer); +} + +static u8 HTIOTActIsDisableMCS14(struct rtllib_device *ieee, u8 *PeerMacAddr) +{ + return 0; +} + +static bool HTIOTActIsDisableMCS15(struct rtllib_device *ieee) +{ + return false; +} + +static bool HTIOTActIsDisableMCSTwoSpatialStream(struct rtllib_device *ieee) +{ + return false; +} + +static u8 HTIOTActIsDisableEDCATurbo(struct rtllib_device *ieee, + u8 *PeerMacAddr) +{ + return false; +} + +static u8 HTIOTActIsMgntUseCCK6M(struct rtllib_device *ieee, + struct rtllib_network *network) +{ + u8 retValue = 0; + + if (ieee->ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) + retValue = 1; + + return retValue; +} + +static u8 HTIOTActIsCCDFsync(struct rtllib_device *ieee) +{ + u8 retValue = 0; + + if (ieee->ht_info->IOTPeer == HT_IOT_PEER_BROADCOM) + retValue = 1; + return retValue; +} + +static void HTIOTActDetermineRaFunc(struct rtllib_device *ieee, bool bPeerRx2ss) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + ht_info->iot_ra_func &= HT_IOT_RAFUNC_DISABLE_ALL; + + if (ht_info->IOTPeer == HT_IOT_PEER_RALINK && !bPeerRx2ss) + ht_info->iot_ra_func |= HT_IOT_RAFUNC_PEER_1R; + + if (ht_info->iot_action & HT_IOT_ACT_AMSDU_ENABLE) + ht_info->iot_ra_func |= HT_IOT_RAFUNC_TX_AMSDU; +} + +void HTResetIOTSetting(struct rt_hi_throughput *ht_info) +{ + ht_info->iot_action = 0; + ht_info->IOTPeer = HT_IOT_PEER_UNKNOWN; + ht_info->iot_ra_func = 0; +} + +void HTConstructCapabilityElement(struct rtllib_device *ieee, u8 *posHTCap, + u8 *len, u8 IsEncrypt, bool bAssoc) +{ + struct rt_hi_throughput *pHT = ieee->ht_info; + struct ht_capab_ele *pCapELE = NULL; + + if (!posHTCap || !pHT) { + netdev_warn(ieee->dev, + "%s(): posHTCap and ht_info are null\n", __func__); + return; + } + memset(posHTCap, 0, *len); + + if ((bAssoc) && (pHT->ePeerHTSpecVer == HT_SPEC_VER_EWC)) { + static const u8 EWC11NHTCap[] = { 0x00, 0x90, 0x4c, 0x33 }; + + memcpy(posHTCap, EWC11NHTCap, sizeof(EWC11NHTCap)); + pCapELE = (struct ht_capab_ele *)&posHTCap[4]; + *len = 30 + 2; + } else { + pCapELE = (struct ht_capab_ele *)posHTCap; + *len = 26 + 2; + } + + pCapELE->AdvCoding = 0; + if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) + pCapELE->ChlWidth = 0; + else + pCapELE->ChlWidth = (pHT->bRegBW40MHz ? 1 : 0); + + pCapELE->MimoPwrSave = pHT->self_mimo_ps; + pCapELE->GreenField = 0; + pCapELE->ShortGI20Mhz = 1; + pCapELE->ShortGI40Mhz = 1; + + pCapELE->TxSTBC = 1; + pCapELE->RxSTBC = 0; + pCapELE->DelayBA = 0; + pCapELE->MaxAMSDUSize = (MAX_RECEIVE_BUFFER_SIZE >= 7935) ? 1 : 0; + pCapELE->DssCCk = ((pHT->bRegBW40MHz) ? (pHT->bRegSuppCCK ? 1 : 0) : 0); + pCapELE->PSMP = 0; + pCapELE->LSigTxopProtect = 0; + + netdev_dbg(ieee->dev, + "TX HT cap/info ele BW=%d MaxAMSDUSize:%d DssCCk:%d\n", + pCapELE->ChlWidth, pCapELE->MaxAMSDUSize, pCapELE->DssCCk); + + if (IsEncrypt) { + pCapELE->MPDUDensity = 7; + pCapELE->MaxRxAMPDUFactor = 2; + } else { + pCapELE->MaxRxAMPDUFactor = 3; + pCapELE->MPDUDensity = 0; + } + + memcpy(pCapELE->MCS, ieee->reg_dot11ht_oper_rate_set, 16); + memset(&pCapELE->ExtHTCapInfo, 0, 2); + memset(pCapELE->TxBFCap, 0, 4); + + pCapELE->ASCap = 0; + + if (bAssoc) { + if (pHT->iot_action & HT_IOT_ACT_DISABLE_MCS15) + pCapELE->MCS[1] &= 0x7f; + + if (pHT->iot_action & HT_IOT_ACT_DISABLE_MCS14) + pCapELE->MCS[1] &= 0xbf; + + if (pHT->iot_action & HT_IOT_ACT_DISABLE_ALL_2SS) + pCapELE->MCS[1] &= 0x00; + + if (pHT->iot_action & HT_IOT_ACT_DISABLE_RX_40MHZ_SHORT_GI) + pCapELE->ShortGI40Mhz = 0; + + if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) { + pCapELE->ChlWidth = 0; + pCapELE->MCS[1] = 0; + } + } +} + +void HTConstructInfoElement(struct rtllib_device *ieee, u8 *posHTInfo, + u8 *len, u8 IsEncrypt) +{ + struct rt_hi_throughput *pHT = ieee->ht_info; + struct ht_info_ele *pHTInfoEle = (struct ht_info_ele *)posHTInfo; + + if (!posHTInfo || !pHTInfoEle) { + netdev_warn(ieee->dev, + "%s(): posHTInfo and pHTInfoEle are null\n", + __func__); + return; + } + + memset(posHTInfo, 0, *len); + if (ieee->iw_mode == IW_MODE_ADHOC) { + pHTInfoEle->ControlChl = ieee->current_network.channel; + pHTInfoEle->ExtChlOffset = ((!pHT->bRegBW40MHz) ? + HT_EXTCHNL_OFFSET_NO_EXT : + (ieee->current_network.channel <= 6) + ? HT_EXTCHNL_OFFSET_UPPER : + HT_EXTCHNL_OFFSET_LOWER); + pHTInfoEle->RecommemdedTxWidth = pHT->bRegBW40MHz; + pHTInfoEle->RIFS = 0; + pHTInfoEle->PSMPAccessOnly = 0; + pHTInfoEle->SrvIntGranularity = 0; + pHTInfoEle->OptMode = pHT->current_op_mode; + pHTInfoEle->NonGFDevPresent = 0; + pHTInfoEle->DualBeacon = 0; + pHTInfoEle->SecondaryBeacon = 0; + pHTInfoEle->LSigTxopProtectFull = 0; + pHTInfoEle->PcoActive = 0; + pHTInfoEle->PcoPhase = 0; + + memset(pHTInfoEle->BasicMSC, 0, 16); + + *len = 22 + 2; + + } else { + *len = 0; + } +} + +void HTConstructRT2RTAggElement(struct rtllib_device *ieee, u8 *posRT2RTAgg, + u8 *len) +{ + if (!posRT2RTAgg) { + netdev_warn(ieee->dev, "%s(): posRT2RTAgg is null\n", __func__); + return; + } + memset(posRT2RTAgg, 0, *len); + *posRT2RTAgg++ = 0x00; + *posRT2RTAgg++ = 0xe0; + *posRT2RTAgg++ = 0x4c; + *posRT2RTAgg++ = 0x02; + *posRT2RTAgg++ = 0x01; + + *posRT2RTAgg = 0x30; + + if (ieee->bSupportRemoteWakeUp) + *posRT2RTAgg |= RT_HT_CAP_USE_WOW; + + *len = 6 + 2; +} + +static u8 HT_PickMCSRate(struct rtllib_device *ieee, u8 *pOperateMCS) +{ + u8 i; + + if (!pOperateMCS) { + netdev_warn(ieee->dev, "%s(): pOperateMCS is null\n", __func__); + return false; + } + + switch (ieee->mode) { + case WIRELESS_MODE_B: + case WIRELESS_MODE_G: + for (i = 0; i <= 15; i++) + pOperateMCS[i] = 0; + break; + case WIRELESS_MODE_N_24G: + pOperateMCS[0] &= RATE_ADPT_1SS_MASK; + pOperateMCS[1] &= RATE_ADPT_2SS_MASK; + pOperateMCS[3] &= RATE_ADPT_MCS32_MASK; + break; + default: + break; + } + + return true; +} + +u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet, + u8 *pMCSFilter) +{ + u8 i, j; + u8 bitMap; + u8 mcsRate = 0; + u8 availableMcsRate[16]; + + if (!pMCSRateSet || !pMCSFilter) { + netdev_warn(ieee->dev, + "%s(): pMCSRateSet and pMCSFilter are null\n", + __func__); + return false; + } + for (i = 0; i < 16; i++) + availableMcsRate[i] = pMCSRateSet[i] & pMCSFilter[i]; + + for (i = 0; i < 16; i++) { + if (availableMcsRate[i] != 0) + break; + } + if (i == 16) + return false; + + for (i = 0; i < 16; i++) { + if (availableMcsRate[i] != 0) { + bitMap = availableMcsRate[i]; + for (j = 0; j < 8; j++) { + if ((bitMap % 2) != 0) { + if (HTMcsToDataRate(ieee, (8 * i + j)) > + HTMcsToDataRate(ieee, mcsRate)) + mcsRate = 8 * i + j; + } + bitMap >>= 1; + } + } + } + return mcsRate | 0x80; +} + +static u8 HTFilterMCSRate(struct rtllib_device *ieee, u8 *pSupportMCS, + u8 *pOperateMCS) +{ + u8 i; + + for (i = 0; i <= 15; i++) + pOperateMCS[i] = ieee->reg_dot11tx_ht_oper_rate_set[i] & + pSupportMCS[i]; + + HT_PickMCSRate(ieee, pOperateMCS); + + if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) + pOperateMCS[1] = 0; + + for (i = 2; i <= 15; i++) + pOperateMCS[i] = 0; + + return true; +} + +void HTSetConnectBwMode(struct rtllib_device *ieee, + enum ht_channel_width bandwidth, + enum ht_extchnl_offset Offset); + +void HTOnAssocRsp(struct rtllib_device *ieee) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + struct ht_capab_ele *pPeerHTCap = NULL; + struct ht_info_ele *pPeerHTInfo = NULL; + u16 nMaxAMSDUSize = 0; + u8 *pMcsFilter = NULL; + + static const u8 EWC11NHTCap[] = { 0x00, 0x90, 0x4c, 0x33 }; + static const u8 EWC11NHTInfo[] = { 0x00, 0x90, 0x4c, 0x34 }; + + if (!ht_info->bCurrentHTSupport) { + netdev_warn(ieee->dev, "%s(): HT_DISABLE\n", __func__); + return; + } + netdev_dbg(ieee->dev, "%s(): HT_ENABLE\n", __func__); + + if (!memcmp(ht_info->PeerHTCapBuf, EWC11NHTCap, sizeof(EWC11NHTCap))) + pPeerHTCap = (struct ht_capab_ele *)(&ht_info->PeerHTCapBuf[4]); + else + pPeerHTCap = (struct ht_capab_ele *)(ht_info->PeerHTCapBuf); + + if (!memcmp(ht_info->PeerHTInfoBuf, EWC11NHTInfo, sizeof(EWC11NHTInfo))) + pPeerHTInfo = (struct ht_info_ele *) + (&ht_info->PeerHTInfoBuf[4]); + else + pPeerHTInfo = (struct ht_info_ele *)(ht_info->PeerHTInfoBuf); + +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("%s: ", __func__, DUMP_PREFIX_NONE, + pPeerHTCap, sizeof(struct ht_capab_ele)); +#endif + HTSetConnectBwMode(ieee, (enum ht_channel_width)(pPeerHTCap->ChlWidth), + (enum ht_extchnl_offset)(pPeerHTInfo->ExtChlOffset)); + ht_info->cur_tx_bw40mhz = ((pPeerHTInfo->RecommemdedTxWidth == 1) ? + true : false); + + ht_info->bCurShortGI20MHz = ((ht_info->bRegShortGI20MHz) ? + ((pPeerHTCap->ShortGI20Mhz == 1) ? + true : false) : false); + ht_info->bCurShortGI40MHz = ((ht_info->bRegShortGI40MHz) ? + ((pPeerHTCap->ShortGI40Mhz == 1) ? + true : false) : false); + + ht_info->bCurSuppCCK = ((ht_info->bRegSuppCCK) ? + ((pPeerHTCap->DssCCk == 1) ? true : + false) : false); + + ht_info->bCurrent_AMSDU_Support = ht_info->bAMSDU_Support; + + nMaxAMSDUSize = (pPeerHTCap->MaxAMSDUSize == 0) ? 3839 : 7935; + + if (ht_info->nAMSDU_MaxSize > nMaxAMSDUSize) + ht_info->nCurrent_AMSDU_MaxSize = nMaxAMSDUSize; + else + ht_info->nCurrent_AMSDU_MaxSize = ht_info->nAMSDU_MaxSize; + + ht_info->bCurrentAMPDUEnable = ht_info->bAMPDUEnable; + if (ieee->rtllib_ap_sec_type && + (ieee->rtllib_ap_sec_type(ieee) & (SEC_ALG_WEP | SEC_ALG_TKIP))) { + if ((ht_info->IOTPeer == HT_IOT_PEER_ATHEROS) || + (ht_info->IOTPeer == HT_IOT_PEER_UNKNOWN)) + ht_info->bCurrentAMPDUEnable = false; + } + + if (!ht_info->reg_rt2rt_aggregation) { + if (ht_info->AMPDU_Factor > pPeerHTCap->MaxRxAMPDUFactor) + ht_info->CurrentAMPDUFactor = + pPeerHTCap->MaxRxAMPDUFactor; + else + ht_info->CurrentAMPDUFactor = ht_info->AMPDU_Factor; + + } else { + if (ieee->current_network.bssht.bd_rt2rt_aggregation) { + if (ieee->pairwise_key_type != KEY_TYPE_NA) + ht_info->CurrentAMPDUFactor = + pPeerHTCap->MaxRxAMPDUFactor; + else + ht_info->CurrentAMPDUFactor = HT_AGG_SIZE_64K; + } else { + ht_info->CurrentAMPDUFactor = min_t(u32, pPeerHTCap->MaxRxAMPDUFactor, + HT_AGG_SIZE_32K); + } + } + ht_info->current_mpdu_density = max_t(u8, ht_info->MPDU_Density, + pPeerHTCap->MPDUDensity); + if (ht_info->iot_action & HT_IOT_ACT_TX_USE_AMSDU_8K) { + ht_info->bCurrentAMPDUEnable = false; + ht_info->ForcedAMSDUMode = HT_AGG_FORCE_ENABLE; + } + ht_info->cur_rx_reorder_enable = ht_info->reg_rx_reorder_enable; + + if (pPeerHTCap->MCS[0] == 0) + pPeerHTCap->MCS[0] = 0xff; + + HTIOTActDetermineRaFunc(ieee, ((pPeerHTCap->MCS[1]) != 0)); + + HTFilterMCSRate(ieee, pPeerHTCap->MCS, ieee->dot11ht_oper_rate_set); + + ht_info->peer_mimo_ps = pPeerHTCap->MimoPwrSave; + if (ht_info->peer_mimo_ps == MIMO_PS_STATIC) + pMcsFilter = MCS_FILTER_1SS; + else + pMcsFilter = MCS_FILTER_ALL; + ieee->HTHighestOperaRate = HTGetHighestMCSRate(ieee, + ieee->dot11ht_oper_rate_set, + pMcsFilter); + ieee->HTCurrentOperaRate = ieee->HTHighestOperaRate; + + ht_info->current_op_mode = pPeerHTInfo->OptMode; +} + +void HTInitializeHTInfo(struct rtllib_device *ieee) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + ht_info->bCurrentHTSupport = false; + + ht_info->bCurBW40MHz = false; + ht_info->cur_tx_bw40mhz = false; + + ht_info->bCurShortGI20MHz = false; + ht_info->bCurShortGI40MHz = false; + ht_info->forced_short_gi = false; + + ht_info->bCurSuppCCK = true; + + ht_info->bCurrent_AMSDU_Support = false; + ht_info->nCurrent_AMSDU_MaxSize = ht_info->nAMSDU_MaxSize; + ht_info->current_mpdu_density = ht_info->MPDU_Density; + ht_info->CurrentAMPDUFactor = ht_info->AMPDU_Factor; + + memset((void *)(&ht_info->SelfHTCap), 0, + sizeof(ht_info->SelfHTCap)); + memset((void *)(&ht_info->SelfHTInfo), 0, + sizeof(ht_info->SelfHTInfo)); + memset((void *)(&ht_info->PeerHTCapBuf), 0, + sizeof(ht_info->PeerHTCapBuf)); + memset((void *)(&ht_info->PeerHTInfoBuf), 0, + sizeof(ht_info->PeerHTInfoBuf)); + + ht_info->sw_bw_in_progress = false; + + ht_info->ePeerHTSpecVer = HT_SPEC_VER_IEEE; + + ht_info->current_rt2rt_aggregation = false; + ht_info->current_rt2rt_long_slot_time = false; + ht_info->RT2RT_HT_Mode = (enum rt_ht_capability)0; + + ht_info->IOTPeer = 0; + ht_info->iot_action = 0; + ht_info->iot_ra_func = 0; + + { + u8 *RegHTSuppRateSets = &ieee->reg_ht_supp_rate_set[0]; + + RegHTSuppRateSets[0] = 0xFF; + RegHTSuppRateSets[1] = 0xFF; + RegHTSuppRateSets[4] = 0x01; + } +} + +void HTInitializeBssDesc(struct bss_ht *pBssHT) +{ + pBssHT->bd_support_ht = false; + memset(pBssHT->bd_ht_cap_buf, 0, sizeof(pBssHT->bd_ht_cap_buf)); + pBssHT->bd_ht_cap_len = 0; + memset(pBssHT->bd_ht_info_buf, 0, sizeof(pBssHT->bd_ht_info_buf)); + pBssHT->bd_ht_info_len = 0; + + pBssHT->bd_ht_spec_ver = HT_SPEC_VER_IEEE; + + pBssHT->bd_rt2rt_aggregation = false; + pBssHT->bd_rt2rt_long_slot_time = false; + pBssHT->rt2rt_ht_mode = (enum rt_ht_capability)0; +} + +void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee, + struct rtllib_network *pNetwork) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + u8 bIOTAction = 0; + + /* unmark enable_ht flag here is the same reason why unmarked in + * function rtllib_softmac_new_net. WB 2008.09.10 + */ + if (pNetwork->bssht.bd_support_ht) { + ht_info->bCurrentHTSupport = true; + ht_info->ePeerHTSpecVer = pNetwork->bssht.bd_ht_spec_ver; + + if (pNetwork->bssht.bd_ht_cap_len > 0 && + pNetwork->bssht.bd_ht_cap_len <= sizeof(ht_info->PeerHTCapBuf)) + memcpy(ht_info->PeerHTCapBuf, + pNetwork->bssht.bd_ht_cap_buf, + pNetwork->bssht.bd_ht_cap_len); + + if (pNetwork->bssht.bd_ht_info_len > 0 && + pNetwork->bssht.bd_ht_info_len <= + sizeof(ht_info->PeerHTInfoBuf)) + memcpy(ht_info->PeerHTInfoBuf, + pNetwork->bssht.bd_ht_info_buf, + pNetwork->bssht.bd_ht_info_len); + + if (ht_info->reg_rt2rt_aggregation) { + ht_info->current_rt2rt_aggregation = + pNetwork->bssht.bd_rt2rt_aggregation; + ht_info->current_rt2rt_long_slot_time = + pNetwork->bssht.bd_rt2rt_long_slot_time; + ht_info->RT2RT_HT_Mode = pNetwork->bssht.rt2rt_ht_mode; + } else { + ht_info->current_rt2rt_aggregation = false; + ht_info->current_rt2rt_long_slot_time = false; + ht_info->RT2RT_HT_Mode = (enum rt_ht_capability)0; + } + + HTIOTPeerDetermine(ieee); + + ht_info->iot_action = 0; + bIOTAction = HTIOTActIsDisableMCS14(ieee, pNetwork->bssid); + if (bIOTAction) + ht_info->iot_action |= HT_IOT_ACT_DISABLE_MCS14; + + bIOTAction = HTIOTActIsDisableMCS15(ieee); + if (bIOTAction) + ht_info->iot_action |= HT_IOT_ACT_DISABLE_MCS15; + + bIOTAction = HTIOTActIsDisableMCSTwoSpatialStream(ieee); + if (bIOTAction) + ht_info->iot_action |= HT_IOT_ACT_DISABLE_ALL_2SS; + + bIOTAction = HTIOTActIsDisableEDCATurbo(ieee, pNetwork->bssid); + if (bIOTAction) + ht_info->iot_action |= HT_IOT_ACT_DISABLE_EDCA_TURBO; + + bIOTAction = HTIOTActIsMgntUseCCK6M(ieee, pNetwork); + if (bIOTAction) + ht_info->iot_action |= HT_IOT_ACT_MGNT_USE_CCK_6M; + bIOTAction = HTIOTActIsCCDFsync(ieee); + if (bIOTAction) + ht_info->iot_action |= HT_IOT_ACT_CDD_FSYNC; + } else { + ht_info->bCurrentHTSupport = false; + ht_info->current_rt2rt_aggregation = false; + ht_info->current_rt2rt_long_slot_time = false; + ht_info->RT2RT_HT_Mode = (enum rt_ht_capability)0; + + ht_info->iot_action = 0; + ht_info->iot_ra_func = 0; + } +} + +void HT_update_self_and_peer_setting(struct rtllib_device *ieee, + struct rtllib_network *pNetwork) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + struct ht_info_ele *pPeerHTInfo = + (struct ht_info_ele *)pNetwork->bssht.bd_ht_info_buf; + + if (ht_info->bCurrentHTSupport) { + if (pNetwork->bssht.bd_ht_info_len != 0) + ht_info->current_op_mode = pPeerHTInfo->OptMode; + } +} +EXPORT_SYMBOL(HT_update_self_and_peer_setting); + +void HTUseDefaultSetting(struct rtllib_device *ieee) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + if (ht_info->enable_ht) { + ht_info->bCurrentHTSupport = true; + ht_info->bCurSuppCCK = ht_info->bRegSuppCCK; + + ht_info->bCurBW40MHz = ht_info->bRegBW40MHz; + ht_info->bCurShortGI20MHz = ht_info->bRegShortGI20MHz; + + ht_info->bCurShortGI40MHz = ht_info->bRegShortGI40MHz; + + if (ieee->iw_mode == IW_MODE_ADHOC) + ieee->current_network.qos_data.active = + ieee->current_network.qos_data.supported; + ht_info->bCurrent_AMSDU_Support = ht_info->bAMSDU_Support; + ht_info->nCurrent_AMSDU_MaxSize = ht_info->nAMSDU_MaxSize; + + ht_info->bCurrentAMPDUEnable = ht_info->bAMPDUEnable; + ht_info->CurrentAMPDUFactor = ht_info->AMPDU_Factor; + + ht_info->current_mpdu_density = ht_info->current_mpdu_density; + + HTFilterMCSRate(ieee, ieee->reg_dot11tx_ht_oper_rate_set, + ieee->dot11ht_oper_rate_set); + ieee->HTHighestOperaRate = HTGetHighestMCSRate(ieee, + ieee->dot11ht_oper_rate_set, + MCS_FILTER_ALL); + ieee->HTCurrentOperaRate = ieee->HTHighestOperaRate; + + } else { + ht_info->bCurrentHTSupport = false; + } +} + +u8 HTCCheck(struct rtllib_device *ieee, u8 *pFrame) +{ + if (ieee->ht_info->bCurrentHTSupport) { + if ((IsQoSDataFrame(pFrame) && Frame_Order(pFrame)) == 1) { + netdev_dbg(ieee->dev, "HT CONTROL FILED EXIST!!\n"); + return true; + } + } + return false; +} + +static void HTSetConnectBwModeCallback(struct rtllib_device *ieee) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + if (ht_info->bCurBW40MHz) { + if (ht_info->CurSTAExtChnlOffset == HT_EXTCHNL_OFFSET_UPPER) + ieee->set_chan(ieee->dev, + ieee->current_network.channel + 2); + else if (ht_info->CurSTAExtChnlOffset == + HT_EXTCHNL_OFFSET_LOWER) + ieee->set_chan(ieee->dev, + ieee->current_network.channel - 2); + else + ieee->set_chan(ieee->dev, + ieee->current_network.channel); + + ieee->set_bw_mode_handler(ieee->dev, HT_CHANNEL_WIDTH_20_40, + ht_info->CurSTAExtChnlOffset); + } else { + ieee->set_chan(ieee->dev, ieee->current_network.channel); + ieee->set_bw_mode_handler(ieee->dev, HT_CHANNEL_WIDTH_20, + HT_EXTCHNL_OFFSET_NO_EXT); + } + + ht_info->sw_bw_in_progress = false; +} + +void HTSetConnectBwMode(struct rtllib_device *ieee, + enum ht_channel_width bandwidth, + enum ht_extchnl_offset Offset) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + if (!ht_info->bRegBW40MHz) + return; + + if (ieee->GetHalfNmodeSupportByAPsHandler(ieee->dev)) + bandwidth = HT_CHANNEL_WIDTH_20; + + if (ht_info->sw_bw_in_progress) { + pr_info("%s: sw_bw_in_progress!!\n", __func__); + return; + } + if (bandwidth == HT_CHANNEL_WIDTH_20_40) { + if (ieee->current_network.channel < 2 && + Offset == HT_EXTCHNL_OFFSET_LOWER) + Offset = HT_EXTCHNL_OFFSET_NO_EXT; + if (Offset == HT_EXTCHNL_OFFSET_UPPER || + Offset == HT_EXTCHNL_OFFSET_LOWER) { + ht_info->bCurBW40MHz = true; + ht_info->CurSTAExtChnlOffset = Offset; + } else { + ht_info->bCurBW40MHz = false; + ht_info->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT; + } + } else { + ht_info->bCurBW40MHz = false; + ht_info->CurSTAExtChnlOffset = HT_EXTCHNL_OFFSET_NO_EXT; + } + + netdev_dbg(ieee->dev, "%s():ht_info->bCurBW40MHz:%x\n", __func__, + ht_info->bCurBW40MHz); + + ht_info->sw_bw_in_progress = true; + + HTSetConnectBwModeCallback(ieee); +} diff --git a/drivers/staging/rtl8192e/rtl819x_Qos.h b/drivers/staging/rtl8192e/rtl819x_Qos.h new file mode 100644 index 0000000000..0499711ea2 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl819x_Qos.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef __INC_QOS_TYPE_H +#define __INC_QOS_TYPE_H + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +union qos_tsinfo { + u8 charData[3]; + struct { + u8 ucTrafficType:1; + u8 ucTSID:4; + u8 ucDirection:2; + u8 ucAccessPolicy:2; + u8 ucAggregation:1; + u8 ucPSB:1; + u8 ucUP:3; + u8 ucTSInfoAckPolicy:2; + u8 ucSchedule:1; + u8 ucReserved:7; + } field; +}; + +union tspec_body { + u8 charData[55]; + + struct { + union qos_tsinfo TSInfo; + u16 NominalMSDUsize; + u16 MaxMSDUsize; + u32 MinServiceItv; + u32 MaxServiceItv; + u32 InactivityItv; + u32 SuspenItv; + u32 ServiceStartTime; + u32 MinDataRate; + u32 MeanDataRate; + u32 PeakDataRate; + u32 MaxBurstSize; + u32 DelayBound; + u32 MinPhyRate; + u16 SurplusBandwidthAllowance; + u16 MediumTime; + } f; +}; + +struct octet_string { + u8 *Octet; + u16 Length; +}; + +#define AC0_BE 0 +#define AC1_BK 1 +#define AC2_VI 2 +#define AC3_VO 3 +#define AC_MAX 4 + +enum direction_value { + DIR_UP = 0, + DIR_DOWN = 1, + DIR_DIRECT = 2, + DIR_BI_DIR = 3, +}; + +struct acm { + u64 UsedTime; + u64 MediumTime; + u8 HwAcmCtl; +}; + +union qos_tclas { + struct _TYPE_GENERAL { + u8 Priority; + u8 ClassifierType; + u8 Mask; + } TYPE_GENERAL; + + struct _TYPE0_ETH { + u8 Priority; + u8 ClassifierType; + u8 Mask; + u8 SrcAddr[ETH_ALEN]; + u8 DstAddr[ETH_ALEN]; + u16 Type; + } TYPE0_ETH; + + struct _TYPE1_IPV4 { + u8 Priority; + u8 ClassifierType; + u8 Mask; + u8 Version; + u8 SrcIP[4]; + u8 DstIP[4]; + u16 SrcPort; + u16 DstPort; + u8 DSCP; + u8 Protocol; + u8 Reserved; + } TYPE1_IPV4; + + struct _TYPE1_IPV6 { + u8 Priority; + u8 ClassifierType; + u8 Mask; + u8 Version; + u8 SrcIP[16]; + u8 DstIP[16]; + u16 SrcPort; + u16 DstPort; + u8 FlowLabel[3]; + } TYPE1_IPV6; + + struct _TYPE2_8021Q { + u8 Priority; + u8 ClassifierType; + u8 Mask; + u16 TagType; + } TYPE2_8021Q; +}; + +union aci_aifsn { + u8 charData; + + struct { + u8 AIFSN:4; + u8 acm:1; + u8 ACI:2; + u8 Reserved:1; + } f; +}; + +#endif diff --git a/drivers/staging/rtl8192e/rtl819x_TS.h b/drivers/staging/rtl8192e/rtl819x_TS.h new file mode 100644 index 0000000000..0e851d4221 --- /dev/null +++ b/drivers/staging/rtl8192e/rtl819x_TS.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _TSTYPE_H_ +#define _TSTYPE_H_ +#include "rtl819x_Qos.h" +#define TS_ADDBA_DELAY 60 + +#define TOTAL_TS_NUM 16 +#define TCLAS_NUM 4 + +enum tr_select { + TX_DIR = 0, + RX_DIR = 1, +}; + +struct ts_common_info { + struct list_head List; + u8 Addr[ETH_ALEN]; + union tspec_body TSpec; + union qos_tclas TClass[TCLAS_NUM]; + u8 TClasProc; + u8 TClasNum; +}; + +struct tx_ts_record { + struct ts_common_info TsCommonInfo; + u16 TxCurSeq; + struct ba_record TxPendingBARecord; + struct ba_record TxAdmittedBARecord; + u8 bAddBaReqInProgress; + u8 bAddBaReqDelayed; + u8 bUsingBa; + u8 bDisable_AddBa; + struct timer_list TsAddBaTimer; + u8 num; +}; + +struct rx_ts_record { + struct ts_common_info ts_common_info; + u16 rx_indicate_seq; + u16 rx_timeout_indicate_seq; + struct list_head rx_pending_pkt_list; + struct timer_list rx_pkt_pending_timer; + struct ba_record rx_admitted_ba_record; + u16 rx_last_seq_num; + u8 rx_last_frag_num; + u8 num; +}; + +#endif diff --git a/drivers/staging/rtl8192e/rtl819x_TSProc.c b/drivers/staging/rtl8192e/rtl819x_TSProc.c new file mode 100644 index 0000000000..419ff72f2b --- /dev/null +++ b/drivers/staging/rtl8192e/rtl819x_TSProc.c @@ -0,0 +1,478 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#include "rtllib.h" +#include <linux/etherdevice.h> +#include "rtl819x_TS.h" + +static void RxPktPendingTimeout(struct timer_list *t) +{ + struct rx_ts_record *pRxTs = from_timer(pRxTs, t, + rx_pkt_pending_timer); + struct rtllib_device *ieee = container_of(pRxTs, struct rtllib_device, + RxTsRecord[pRxTs->num]); + + struct rx_reorder_entry *pReorderEntry = NULL; + + unsigned long flags = 0; + u8 index = 0; + bool bPktInBuf = false; + + spin_lock_irqsave(&(ieee->reorder_spinlock), flags); + if (pRxTs->rx_timeout_indicate_seq != 0xffff) { + while (!list_empty(&pRxTs->rx_pending_pkt_list)) { + pReorderEntry = (struct rx_reorder_entry *) + list_entry(pRxTs->rx_pending_pkt_list.prev, + struct rx_reorder_entry, List); + if (index == 0) + pRxTs->rx_indicate_seq = pReorderEntry->SeqNum; + + if (SN_LESS(pReorderEntry->SeqNum, + pRxTs->rx_indicate_seq) || + SN_EQUAL(pReorderEntry->SeqNum, + pRxTs->rx_indicate_seq)) { + list_del_init(&pReorderEntry->List); + + if (SN_EQUAL(pReorderEntry->SeqNum, + pRxTs->rx_indicate_seq)) + pRxTs->rx_indicate_seq = + (pRxTs->rx_indicate_seq + 1) % 4096; + + netdev_dbg(ieee->dev, + "%s(): Indicate SeqNum: %d\n", + __func__, pReorderEntry->SeqNum); + ieee->stats_IndicateArray[index] = + pReorderEntry->prxb; + index++; + + list_add_tail(&pReorderEntry->List, + &ieee->RxReorder_Unused_List); + } else { + bPktInBuf = true; + break; + } + } + } + + if (index > 0) { + pRxTs->rx_timeout_indicate_seq = 0xffff; + + if (index > REORDER_WIN_SIZE) { + netdev_warn(ieee->dev, + "%s(): Rx Reorder struct buffer full\n", + __func__); + spin_unlock_irqrestore(&(ieee->reorder_spinlock), + flags); + return; + } + rtllib_indicate_packets(ieee, ieee->stats_IndicateArray, index); + bPktInBuf = false; + } + + if (bPktInBuf && (pRxTs->rx_timeout_indicate_seq == 0xffff)) { + pRxTs->rx_timeout_indicate_seq = pRxTs->rx_indicate_seq; + mod_timer(&pRxTs->rx_pkt_pending_timer, jiffies + + msecs_to_jiffies(ieee->ht_info->rx_reorder_pending_time) + ); + } + spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags); +} + +static void TsAddBaProcess(struct timer_list *t) +{ + struct tx_ts_record *pTxTs = from_timer(pTxTs, t, TsAddBaTimer); + u8 num = pTxTs->num; + struct rtllib_device *ieee = container_of(pTxTs, struct rtllib_device, + TxTsRecord[num]); + + rtllib_ts_init_add_ba(ieee, pTxTs, BA_POLICY_IMMEDIATE, false); + netdev_dbg(ieee->dev, "%s(): ADDBA Req is started\n", __func__); +} + +static void ResetTsCommonInfo(struct ts_common_info *pTsCommonInfo) +{ + eth_zero_addr(pTsCommonInfo->Addr); + memset(&pTsCommonInfo->TSpec, 0, sizeof(union tspec_body)); + memset(&pTsCommonInfo->TClass, 0, sizeof(union qos_tclas) * TCLAS_NUM); + pTsCommonInfo->TClasProc = 0; + pTsCommonInfo->TClasNum = 0; +} + +static void ResetTxTsEntry(struct tx_ts_record *pTS) +{ + ResetTsCommonInfo(&pTS->TsCommonInfo); + pTS->TxCurSeq = 0; + pTS->bAddBaReqInProgress = false; + pTS->bAddBaReqDelayed = false; + pTS->bUsingBa = false; + pTS->bDisable_AddBa = false; + rtllib_reset_ba_entry(&pTS->TxAdmittedBARecord); + rtllib_reset_ba_entry(&pTS->TxPendingBARecord); +} + +static void ResetRxTsEntry(struct rx_ts_record *pTS) +{ + ResetTsCommonInfo(&pTS->ts_common_info); + pTS->rx_indicate_seq = 0xffff; + pTS->rx_timeout_indicate_seq = 0xffff; + rtllib_reset_ba_entry(&pTS->rx_admitted_ba_record); +} + +void TSInitialize(struct rtllib_device *ieee) +{ + struct tx_ts_record *pTxTS = ieee->TxTsRecord; + struct rx_ts_record *pRxTS = ieee->RxTsRecord; + struct rx_reorder_entry *pRxReorderEntry = ieee->RxReorderEntry; + u8 count = 0; + + INIT_LIST_HEAD(&ieee->Tx_TS_Admit_List); + INIT_LIST_HEAD(&ieee->Tx_TS_Pending_List); + INIT_LIST_HEAD(&ieee->Tx_TS_Unused_List); + + for (count = 0; count < TOTAL_TS_NUM; count++) { + pTxTS->num = count; + timer_setup(&pTxTS->TsAddBaTimer, TsAddBaProcess, 0); + + timer_setup(&pTxTS->TxPendingBARecord.timer, rtllib_ba_setup_timeout, + 0); + timer_setup(&pTxTS->TxAdmittedBARecord.timer, + rtllib_tx_ba_inact_timeout, 0); + + ResetTxTsEntry(pTxTS); + list_add_tail(&pTxTS->TsCommonInfo.List, + &ieee->Tx_TS_Unused_List); + pTxTS++; + } + + INIT_LIST_HEAD(&ieee->Rx_TS_Admit_List); + INIT_LIST_HEAD(&ieee->Rx_TS_Pending_List); + INIT_LIST_HEAD(&ieee->Rx_TS_Unused_List); + for (count = 0; count < TOTAL_TS_NUM; count++) { + pRxTS->num = count; + INIT_LIST_HEAD(&pRxTS->rx_pending_pkt_list); + timer_setup(&pRxTS->rx_admitted_ba_record.timer, + rtllib_rx_ba_inact_timeout, 0); + + timer_setup(&pRxTS->rx_pkt_pending_timer, RxPktPendingTimeout, 0); + + ResetRxTsEntry(pRxTS); + list_add_tail(&pRxTS->ts_common_info.List, + &ieee->Rx_TS_Unused_List); + pRxTS++; + } + INIT_LIST_HEAD(&ieee->RxReorder_Unused_List); + for (count = 0; count < REORDER_ENTRY_NUM; count++) { + list_add_tail(&pRxReorderEntry->List, + &ieee->RxReorder_Unused_List); + if (count == (REORDER_ENTRY_NUM - 1)) + break; + pRxReorderEntry = &ieee->RxReorderEntry[count + 1]; + } +} + +static struct ts_common_info *SearchAdmitTRStream(struct rtllib_device *ieee, + u8 *Addr, u8 TID, + enum tr_select TxRxSelect) +{ + u8 dir; + bool search_dir[4] = {0}; + struct list_head *psearch_list; + struct ts_common_info *pRet = NULL; + + if (ieee->iw_mode == IW_MODE_ADHOC) { + if (TxRxSelect == TX_DIR) + search_dir[DIR_UP] = true; + else + search_dir[DIR_DOWN] = true; + } else { + if (TxRxSelect == TX_DIR) { + search_dir[DIR_UP] = true; + search_dir[DIR_BI_DIR] = true; + search_dir[DIR_DIRECT] = true; + } else { + search_dir[DIR_DOWN] = true; + search_dir[DIR_BI_DIR] = true; + search_dir[DIR_DIRECT] = true; + } + } + + if (TxRxSelect == TX_DIR) + psearch_list = &ieee->Tx_TS_Admit_List; + else + psearch_list = &ieee->Rx_TS_Admit_List; + + for (dir = 0; dir <= DIR_BI_DIR; dir++) { + if (!search_dir[dir]) + continue; + list_for_each_entry(pRet, psearch_list, List) { + if (memcmp(pRet->Addr, Addr, 6) == 0 && + pRet->TSpec.f.TSInfo.field.ucTSID == TID && + pRet->TSpec.f.TSInfo.field.ucDirection == dir) + break; + } + if (&pRet->List != psearch_list) + break; + } + + if (pRet && &pRet->List != psearch_list) + return pRet; + return NULL; +} + +static void MakeTSEntry(struct ts_common_info *pTsCommonInfo, u8 *Addr, + union tspec_body *pTSPEC, union qos_tclas *pTCLAS, + u8 TCLAS_Num, u8 TCLAS_Proc) +{ + u8 count; + + if (!pTsCommonInfo) + return; + + memcpy(pTsCommonInfo->Addr, Addr, 6); + + if (pTSPEC) + memcpy((u8 *)(&(pTsCommonInfo->TSpec)), (u8 *)pTSPEC, + sizeof(union tspec_body)); + + for (count = 0; count < TCLAS_Num; count++) + memcpy((u8 *)(&(pTsCommonInfo->TClass[count])), + (u8 *)pTCLAS, sizeof(union qos_tclas)); + + pTsCommonInfo->TClasProc = TCLAS_Proc; + pTsCommonInfo->TClasNum = TCLAS_Num; +} + +bool GetTs(struct rtllib_device *ieee, struct ts_common_info **ppTS, + u8 *Addr, u8 TID, enum tr_select TxRxSelect, bool bAddNewTs) +{ + u8 UP = 0; + union tspec_body TSpec; + union qos_tsinfo *pTSInfo = &TSpec.f.TSInfo; + struct list_head *pUnusedList; + struct list_head *pAddmitList; + enum direction_value Dir; + + if (is_multicast_ether_addr(Addr)) { + netdev_warn(ieee->dev, "Get TS for Broadcast or Multicast\n"); + return false; + } + if (ieee->current_network.qos_data.supported == 0) { + UP = 0; + } else { + switch (TID) { + case 0: + case 3: + UP = 0; + break; + case 1: + case 2: + UP = 2; + break; + case 4: + case 5: + UP = 5; + break; + case 6: + case 7: + UP = 7; + break; + default: + netdev_warn(ieee->dev, "%s(): TID(%d) is not valid\n", + __func__, TID); + return false; + } + } + + *ppTS = SearchAdmitTRStream(ieee, Addr, UP, TxRxSelect); + if (*ppTS) + return true; + + if (!bAddNewTs) { + netdev_dbg(ieee->dev, "add new TS failed(tid:%d)\n", UP); + return false; + } + + pUnusedList = (TxRxSelect == TX_DIR) ? + (&ieee->Tx_TS_Unused_List) : + (&ieee->Rx_TS_Unused_List); + + pAddmitList = (TxRxSelect == TX_DIR) ? + (&ieee->Tx_TS_Admit_List) : + (&ieee->Rx_TS_Admit_List); + + Dir = ((TxRxSelect == TX_DIR) ? DIR_UP : DIR_DOWN); + + if (!list_empty(pUnusedList)) { + (*ppTS) = list_entry(pUnusedList->next, + struct ts_common_info, List); + list_del_init(&(*ppTS)->List); + if (TxRxSelect == TX_DIR) { + struct tx_ts_record *tmp = + container_of(*ppTS, + struct tx_ts_record, + TsCommonInfo); + ResetTxTsEntry(tmp); + } else { + struct rx_ts_record *tmp = + container_of(*ppTS, + struct rx_ts_record, + ts_common_info); + ResetRxTsEntry(tmp); + } + + netdev_dbg(ieee->dev, + "to init current TS, UP:%d, Dir:%d, addr: %pM ppTs=%p\n", + UP, Dir, Addr, *ppTS); + pTSInfo->field.ucTrafficType = 0; + pTSInfo->field.ucTSID = UP; + pTSInfo->field.ucDirection = Dir; + pTSInfo->field.ucAccessPolicy = 1; + pTSInfo->field.ucAggregation = 0; + pTSInfo->field.ucPSB = 0; + pTSInfo->field.ucUP = UP; + pTSInfo->field.ucTSInfoAckPolicy = 0; + pTSInfo->field.ucSchedule = 0; + + MakeTSEntry(*ppTS, Addr, &TSpec, NULL, 0, 0); + list_add_tail(&((*ppTS)->List), pAddmitList); + + return true; + } + + netdev_warn(ieee->dev, + "There is not enough dir=%d(0=up down=1) TS record to be used!", + Dir); + return false; +} + +static void RemoveTsEntry(struct rtllib_device *ieee, + struct ts_common_info *pTs, enum tr_select TxRxSelect) +{ + rtllib_ts_init_del_ba(ieee, pTs, TxRxSelect); + + if (TxRxSelect == RX_DIR) { + struct rx_reorder_entry *pRxReorderEntry; + struct rx_ts_record *pRxTS = (struct rx_ts_record *)pTs; + + if (timer_pending(&pRxTS->rx_pkt_pending_timer)) + del_timer_sync(&pRxTS->rx_pkt_pending_timer); + + while (!list_empty(&pRxTS->rx_pending_pkt_list)) { + pRxReorderEntry = (struct rx_reorder_entry *) + list_entry(pRxTS->rx_pending_pkt_list.prev, + struct rx_reorder_entry, List); + netdev_dbg(ieee->dev, "%s(): Delete SeqNum %d!\n", + __func__, pRxReorderEntry->SeqNum); + list_del_init(&pRxReorderEntry->List); + { + int i = 0; + struct rtllib_rxb *prxb = pRxReorderEntry->prxb; + + if (unlikely(!prxb)) + return; + for (i = 0; i < prxb->nr_subframes; i++) + dev_kfree_skb(prxb->subframes[i]); + kfree(prxb); + prxb = NULL; + } + list_add_tail(&pRxReorderEntry->List, + &ieee->RxReorder_Unused_List); + } + } else { + struct tx_ts_record *pTxTS = (struct tx_ts_record *)pTs; + + del_timer_sync(&pTxTS->TsAddBaTimer); + } +} + +void RemovePeerTS(struct rtllib_device *ieee, u8 *Addr) +{ + struct ts_common_info *pTS, *pTmpTS; + + netdev_info(ieee->dev, "===========>%s, %pM\n", __func__, Addr); + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List) { + if (memcmp(pTS->Addr, Addr, 6) == 0) { + RemoveTsEntry(ieee, pTS, TX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List); + } + } + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List) { + if (memcmp(pTS->Addr, Addr, 6) == 0) { + netdev_info(ieee->dev, + "====>remove Tx_TS_admin_list\n"); + RemoveTsEntry(ieee, pTS, TX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List); + } + } + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List) { + if (memcmp(pTS->Addr, Addr, 6) == 0) { + RemoveTsEntry(ieee, pTS, RX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List); + } + } + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List) { + if (memcmp(pTS->Addr, Addr, 6) == 0) { + RemoveTsEntry(ieee, pTS, RX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List); + } + } +} +EXPORT_SYMBOL(RemovePeerTS); + +void RemoveAllTS(struct rtllib_device *ieee) +{ + struct ts_common_info *pTS, *pTmpTS; + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List) { + RemoveTsEntry(ieee, pTS, TX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List); + } + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List) { + RemoveTsEntry(ieee, pTS, TX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List); + } + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List) { + RemoveTsEntry(ieee, pTS, RX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List); + } + + list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List) { + RemoveTsEntry(ieee, pTS, RX_DIR); + list_del_init(&pTS->List); + list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List); + } +} + +void TsStartAddBaProcess(struct rtllib_device *ieee, struct tx_ts_record *pTxTS) +{ + if (pTxTS->bAddBaReqInProgress == false) { + pTxTS->bAddBaReqInProgress = true; + + if (pTxTS->bAddBaReqDelayed) { + netdev_dbg(ieee->dev, "Start ADDBA after 60 sec!!\n"); + mod_timer(&pTxTS->TsAddBaTimer, jiffies + + msecs_to_jiffies(TS_ADDBA_DELAY)); + } else { + netdev_dbg(ieee->dev, "Immediately Start ADDBA\n"); + mod_timer(&pTxTS->TsAddBaTimer, jiffies + 10); + } + } else { + netdev_dbg(ieee->dev, "BA timer is already added\n"); + } +} diff --git a/drivers/staging/rtl8192e/rtllib.h b/drivers/staging/rtl8192e/rtllib.h new file mode 100644 index 0000000000..bfa4dbf94d --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib.h @@ -0,0 +1,2051 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Merged with mainline rtllib.h in Aug 2004. Original ieee802_11 + * remains copyright by the original authors + * + * Portions of the merged code are based on Host AP (software wireless + * LAN access point) driver for Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * <jkmaline@cc.hut.fi> + * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * <jketreno@linux.intel.com> + * Copyright (c) 2004, Intel Corporation + * + * Modified for Realtek's wi-fi cards by Andrea Merello + * <andrea.merello@gmail.com> + */ +#ifndef RTLLIB_H +#define RTLLIB_H +#include <linux/if_ether.h> /* ETH_ALEN */ +#include <linux/kernel.h> /* ARRAY_SIZE */ +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/timer.h> +#include <linux/sched.h> +#include <linux/mutex.h> + +#include <linux/delay.h> +#include <linux/wireless.h> + +#include "rtllib_debug.h" +#include "rtl819x_HT.h" +#include "rtl819x_BA.h" +#include "rtl819x_TS.h" + +#include <linux/netdevice.h> +#include <linux/if_arp.h> /* ARPHRD_ETHER */ +#include <net/cfg80211.h> +#include <net/lib80211.h> + +#define MAX_PRECMD_CNT 16 +#define MAX_RFDEPENDCMD_CNT 16 +#define MAX_POSTCMD_CNT 16 + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY +#endif +#include <net/iw_handler.h> + +#ifndef IW_MODE_MONITOR +#define IW_MODE_MONITOR 6 +#endif + +#ifndef IWEVCUSTOM +#define IWEVCUSTOM 0x8c02 +#endif + +#ifndef IW_CUSTOM_MAX +/* Max number of char in custom event - use multiple of them if needed */ +#define IW_CUSTOM_MAX 256 /* In bytes */ +#endif + +#define container_of_dwork_rsl(x, y, z) \ + container_of(to_delayed_work(x), y, z) + +static inline void *netdev_priv_rsl(struct net_device *dev) +{ + return netdev_priv(dev); +} + +#define KEY_TYPE_NA 0x0 +#define KEY_TYPE_WEP40 0x1 +#define KEY_TYPE_TKIP 0x2 +#define KEY_TYPE_CCMP 0x4 +#define KEY_TYPE_WEP104 0x5 +/* added for rtl819x tx procedure */ +#define MAX_QUEUE_SIZE 0x10 + +#define BK_QUEUE 0 +#define BE_QUEUE 1 +#define VI_QUEUE 2 +#define VO_QUEUE 3 +#define HCCA_QUEUE 4 +#define TXCMD_QUEUE 5 +#define MGNT_QUEUE 6 +#define HIGH_QUEUE 7 +#define BEACON_QUEUE 8 + +#define IE_CISCO_FLAG_POSITION 0x08 +#define SUPPORT_CKIP_MIC 0x08 +#define SUPPORT_CKIP_PK 0x10 +#define RT_RF_OFF_LEVL_HALT_NIC BIT3 +#define RT_IN_PS_LEVEL(psc, _PS_FLAG) \ + ((psc->CurPsLevel & _PS_FLAG) ? true : false) +#define RT_CLEAR_PS_LEVEL(psc, _PS_FLAG) \ + (psc->CurPsLevel &= (~(_PS_FLAG))) + +/* defined for skb cb field */ +/* At most 28 byte */ +struct cb_desc { + /* Tx Desc Related flags (8-9) */ + u8 bLastIniPkt:1; + u8 bCmdOrInit:1; + u8 bFirstSeg:1; + u8 bLastSeg:1; + u8 bEncrypt:1; + u8 tx_dis_rate_fallback:1; + u8 tx_use_drv_assinged_rate:1; + u8 bHwSec:1; + + u8 nStuckCount; + + /* Tx Firmware Related flags (10-11)*/ + u8 bCTSEnable:1; + u8 bRTSEnable:1; + u8 bUseShortGI:1; + u8 bUseShortPreamble:1; + u8 bTxEnableFwCalcDur:1; + u8 bAMPDUEnable:1; + u8 bRTSSTBC:1; + u8 RTSSC:1; + + u8 bRTSBW:1; + u8 bPacketBW:1; + u8 bRTSUseShortPreamble:1; + u8 bRTSUseShortGI:1; + u8 bMulticast:1; + u8 bBroadcast:1; + u8 drv_agg_enable:1; + u8 reserved2:1; + + /* Tx Desc related element(12-19) */ + u8 rata_index; + u8 queue_index; + u16 txbuf_size; + u8 ratr_index; + u8 bAMSDU:1; + u8 bFromAggrQ:1; + u8 reserved6:6; + u8 macId; + u8 priority; + + /* Tx firmware related element(20-27) */ + u8 data_rate; + u8 rts_rate; + u8 ampdu_factor; + u8 ampdu_density; + u8 DrvAggrNum; + u8 bdhcp; + u16 pkt_size; + u8 bIsSpecialDataFrame; + + u8 bBTTxPacket; + u8 bIsBTProbRsp; +}; + +enum sw_chnl_cmd_id { + CmdID_End, + CmdID_SetTxPowerLevel, + CmdID_BBRegWrite10, + CmdID_WritePortUlong, + CmdID_WritePortUshort, + CmdID_WritePortUchar, + CmdID_RF_WriteReg, +}; + +struct sw_chnl_cmd { + enum sw_chnl_cmd_id CmdID; + u32 Para1; + u32 Para2; + u32 msDelay; +} __packed; + +/*--------------------------Define -------------------------------------------*/ +#define MGN_1M 0x02 +#define MGN_2M 0x04 +#define MGN_5_5M 0x0b +#define MGN_11M 0x16 + +#define MGN_6M 0x0c +#define MGN_9M 0x12 +#define MGN_12M 0x18 +#define MGN_18M 0x24 +#define MGN_24M 0x30 +#define MGN_36M 0x48 +#define MGN_48M 0x60 +#define MGN_54M 0x6c + +#define MGN_MCS0 0x80 +#define MGN_MCS1 0x81 +#define MGN_MCS2 0x82 +#define MGN_MCS3 0x83 +#define MGN_MCS4 0x84 +#define MGN_MCS5 0x85 +#define MGN_MCS6 0x86 +#define MGN_MCS7 0x87 +#define MGN_MCS8 0x88 +#define MGN_MCS9 0x89 +#define MGN_MCS10 0x8a +#define MGN_MCS11 0x8b +#define MGN_MCS12 0x8c +#define MGN_MCS13 0x8d +#define MGN_MCS14 0x8e +#define MGN_MCS15 0x8f + +enum hw_variables { + HW_VAR_ETHER_ADDR, + HW_VAR_MULTICAST_REG, + HW_VAR_BASIC_RATE, + HW_VAR_BSSID, + HW_VAR_MEDIA_STATUS, + HW_VAR_SECURITY_CONF, + HW_VAR_BEACON_INTERVAL, + HW_VAR_ATIM_WINDOW, + HW_VAR_LISTEN_INTERVAL, + HW_VAR_CS_COUNTER, + HW_VAR_DEFAULTKEY0, + HW_VAR_DEFAULTKEY1, + HW_VAR_DEFAULTKEY2, + HW_VAR_DEFAULTKEY3, + HW_VAR_SIFS, + HW_VAR_DIFS, + HW_VAR_EIFS, + HW_VAR_SLOT_TIME, + HW_VAR_ACK_PREAMBLE, + HW_VAR_CW_CONFIG, + HW_VAR_CW_VALUES, + HW_VAR_RATE_FALLBACK_CONTROL, + HW_VAR_CONTENTION_WINDOW, + HW_VAR_RETRY_COUNT, + HW_VAR_TR_SWITCH, + HW_VAR_COMMAND, + HW_VAR_WPA_CONFIG, + HW_VAR_AMPDU_MIN_SPACE, + HW_VAR_SHORTGI_DENSITY, + HW_VAR_AMPDU_FACTOR, + HW_VAR_MCS_RATE_AVAILABLE, + HW_VAR_AC_PARAM, + HW_VAR_ACM_CTRL, + HW_VAR_DIS_Req_Qsize, + HW_VAR_CCX_CHNL_LOAD, + HW_VAR_CCX_NOISE_HISTOGRAM, + HW_VAR_CCX_CLM_NHM, + HW_VAR_TxOPLimit, + HW_VAR_TURBO_MODE, + HW_VAR_RF_STATE, + HW_VAR_RF_OFF_BY_HW, + HW_VAR_BUS_SPEED, + HW_VAR_SET_DEV_POWER, + + HW_VAR_RCR, + HW_VAR_RATR_0, + HW_VAR_RRSR, + HW_VAR_CPU_RST, + HW_VAR_CECHK_BSSID, + HW_VAR_LBK_MODE, + HW_VAR_AES_11N_FIX, + HW_VAR_USB_RX_AGGR, + HW_VAR_USER_CONTROL_TURBO_MODE, + HW_VAR_RETRY_LIMIT, + HW_VAR_INIT_TX_RATE, + HW_VAR_TX_RATE_REG, + HW_VAR_EFUSE_USAGE, + HW_VAR_EFUSE_BYTES, + HW_VAR_AUTOLOAD_STATUS, + HW_VAR_RF_2R_DISABLE, + HW_VAR_SET_RPWM, + HW_VAR_H2C_FW_PWRMODE, + HW_VAR_H2C_FW_JOINBSSRPT, + HW_VAR_1X1_RECV_COMBINE, + HW_VAR_STOP_SEND_BEACON, + HW_VAR_TSF_TIMER, + HW_VAR_IO_CMD, + + HW_VAR_RF_RECOVERY, + HW_VAR_H2C_FW_UPDATE_GTK, + HW_VAR_WF_MASK, + HW_VAR_WF_CRC, + HW_VAR_WF_IS_MAC_ADDR, + HW_VAR_H2C_FW_OFFLOAD, + HW_VAR_RESET_WFCRC, + + HW_VAR_HANDLE_FW_C2H, + HW_VAR_DL_FW_RSVD_PAGE, + HW_VAR_AID, + HW_VAR_HW_SEQ_ENABLE, + HW_VAR_CORRECT_TSF, + HW_VAR_BCN_VALID, + HW_VAR_FWLPS_RF_ON, + HW_VAR_DUAL_TSF_RST, + HW_VAR_SWITCH_EPHY_WoWLAN, + HW_VAR_INT_MIGRATION, + HW_VAR_INT_AC, + HW_VAR_RF_TIMING, +}; + +enum rt_op_mode { + RT_OP_MODE_AP, + RT_OP_MODE_INFRASTRUCTURE, + RT_OP_MODE_IBSS, + RT_OP_MODE_NO_LINK, +}; + +#define aSifsTime \ + ((priv->rtllib->current_network.mode == WIRELESS_MODE_N_24G) ? 16 : 10) + +#define MGMT_QUEUE_NUM 5 + +#define MAX_IE_LEN 0xff + +#define msleep_interruptible_rsl msleep_interruptible + +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + * 6.2.1.1.2. + * + * The figure in section 7.1.2 suggests a body size of up to 2312 + * bytes is allowed, which is a bit confusing, I suspect this + * represents the 2304 bytes of real data, plus a possible 8 bytes of + * WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) + */ +#define RTLLIB_1ADDR_LEN 10 +#define RTLLIB_2ADDR_LEN 16 +#define RTLLIB_3ADDR_LEN 24 +#define RTLLIB_4ADDR_LEN 30 +#define RTLLIB_FCS_LEN 4 + +#define RTLLIB_SKBBUFFER_SIZE 2500 + +#define MIN_FRAG_THRESHOLD 256U +#define MAX_FRAG_THRESHOLD 2346U + +/* Frame control field constants */ +#define RTLLIB_FCTL_FTYPE 0x000c +#define RTLLIB_FCTL_STYPE 0x00f0 +#define RTLLIB_FCTL_FRAMETYPE 0x00fc +#define RTLLIB_FCTL_TODS 0x0100 +#define RTLLIB_FCTL_FROMDS 0x0200 +#define RTLLIB_FCTL_DSTODS 0x0300 +#define RTLLIB_FCTL_MOREFRAGS 0x0400 +#define RTLLIB_FCTL_RETRY 0x0800 +#define RTLLIB_FCTL_PM 0x1000 +#define RTLLIB_FCTL_MOREDATA 0x2000 +#define RTLLIB_FCTL_WEP 0x4000 +#define RTLLIB_FCTL_ORDER 0x8000 + +#define RTLLIB_FTYPE_MGMT 0x0000 +#define RTLLIB_FTYPE_CTL 0x0004 +#define RTLLIB_FTYPE_DATA 0x0008 + +/* management */ +#define RTLLIB_STYPE_ASSOC_REQ 0x0000 +#define RTLLIB_STYPE_ASSOC_RESP 0x0010 +#define RTLLIB_STYPE_REASSOC_REQ 0x0020 +#define RTLLIB_STYPE_REASSOC_RESP 0x0030 +#define RTLLIB_STYPE_PROBE_REQ 0x0040 +#define RTLLIB_STYPE_PROBE_RESP 0x0050 +#define RTLLIB_STYPE_BEACON 0x0080 +#define RTLLIB_STYPE_ATIM 0x0090 +#define RTLLIB_STYPE_DISASSOC 0x00A0 +#define RTLLIB_STYPE_AUTH 0x00B0 +#define RTLLIB_STYPE_DEAUTH 0x00C0 +#define RTLLIB_STYPE_MANAGE_ACT 0x00D0 + +/* control */ +#define RTLLIB_STYPE_PSPOLL 0x00A0 +#define RTLLIB_STYPE_RTS 0x00B0 +#define RTLLIB_STYPE_CTS 0x00C0 +#define RTLLIB_STYPE_ACK 0x00D0 + +/* data */ +#define RTLLIB_STYPE_DATA 0x0000 +#define RTLLIB_STYPE_DATA_CFACK 0x0010 +#define RTLLIB_STYPE_DATA_CFPOLL 0x0020 +#define RTLLIB_STYPE_DATA_CFACKPOLL 0x0030 +#define RTLLIB_STYPE_NULLFUNC 0x0040 +#define RTLLIB_STYPE_QOS_DATA 0x0080 +#define RTLLIB_STYPE_QOS_NULL 0x00C0 + +#define RTLLIB_SCTL_FRAG 0x000F +#define RTLLIB_SCTL_SEQ 0xFFF0 + +/* QOS control */ +#define RTLLIB_QCTL_TID 0x000F + +#define FC_QOS_BIT BIT7 +#define IsDataFrame(pdu) (((pdu[0] & 0x0C) == 0x08) ? true : false) +#define IsLegacyDataFrame(pdu) (IsDataFrame(pdu) && (!(pdu[0]&FC_QOS_BIT))) +#define IsQoSDataFrame(pframe) \ + ((*(u16 *)pframe&(RTLLIB_STYPE_QOS_DATA|RTLLIB_FTYPE_DATA)) == \ + (RTLLIB_STYPE_QOS_DATA|RTLLIB_FTYPE_DATA)) +#define Frame_Order(pframe) (*(u16 *)pframe&RTLLIB_FCTL_ORDER) +#define SN_LESS(a, b) (((a-b)&0x800) != 0) +#define SN_EQUAL(a, b) (a == b) +#define MAX_DEV_ADDR_SIZE 8 + +enum act_category { + ACT_CAT_QOS = 1, + ACT_CAT_DLS = 2, + ACT_CAT_BA = 3, + ACT_CAT_HT = 7, + ACT_CAT_WMM = 17, +}; + +enum ba_action { + ACT_ADDBAREQ = 0, + ACT_ADDBARSP = 1, + ACT_DELBA = 2, +}; + +enum init_gain_op_type { + IG_Backup = 0, + IG_Restore, + IG_Max +}; + +enum wireless_mode { + WIRELESS_MODE_UNKNOWN = 0x00, + WIRELESS_MODE_A = 0x01, + WIRELESS_MODE_B = 0x02, + WIRELESS_MODE_G = 0x04, + WIRELESS_MODE_AUTO = 0x08, + WIRELESS_MODE_N_24G = 0x10, +}; + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ +#endif /* ETH_P_PAE */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) +#endif + +/* IEEE 802.11 defines */ + +#define P80211_OUI_LEN 3 + +struct rtllib_snap_hdr { + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ + +} __packed; + +enum _REG_PREAMBLE_MODE { + PREAMBLE_LONG = 1, + PREAMBLE_AUTO = 2, + PREAMBLE_SHORT = 3, +}; + +#define SNAP_SIZE sizeof(struct rtllib_snap_hdr) + +#define WLAN_FC_GET_TYPE(fc) ((fc) & RTLLIB_FCTL_FTYPE) +#define WLAN_FC_GET_STYPE(fc) ((fc) & RTLLIB_FCTL_STYPE) +#define WLAN_FC_MORE_DATA(fc) ((fc) & RTLLIB_FCTL_MOREDATA) + +#define WLAN_FC_GET_FRAMETYPE(fc) ((fc) & RTLLIB_FCTL_FRAMETYPE) +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & RTLLIB_SCTL_FRAG) +#define WLAN_GET_SEQ_SEQ(seq) (((seq) & RTLLIB_SCTL_SEQ) >> 4) + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 +#define WLAN_AUTH_LEAP 128 + +#define WLAN_CAPABILITY_ESS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#define WLAN_CAPABILITY_PRIVACY (1<<4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) +#define WLAN_CAPABILITY_SHORT_SLOT_TIME (1<<10) + +#define RTLLIB_STATMASK_SIGNAL (1<<0) +#define RTLLIB_STATMASK_RSSI (1<<1) +#define RTLLIB_STATMASK_NOISE (1<<2) +#define RTLLIB_STATMASK_WEMASK 0x7 + +#define RTLLIB_CCK_MODULATION (1<<0) +#define RTLLIB_OFDM_MODULATION (1<<1) + +#define RTLLIB_CCK_RATE_LEN 4 +#define RTLLIB_CCK_RATE_1MB 0x02 +#define RTLLIB_CCK_RATE_2MB 0x04 +#define RTLLIB_CCK_RATE_5MB 0x0B +#define RTLLIB_CCK_RATE_11MB 0x16 +#define RTLLIB_OFDM_RATE_LEN 8 +#define RTLLIB_OFDM_RATE_6MB 0x0C +#define RTLLIB_OFDM_RATE_9MB 0x12 +#define RTLLIB_OFDM_RATE_12MB 0x18 +#define RTLLIB_OFDM_RATE_18MB 0x24 +#define RTLLIB_OFDM_RATE_24MB 0x30 +#define RTLLIB_OFDM_RATE_36MB 0x48 +#define RTLLIB_OFDM_RATE_48MB 0x60 +#define RTLLIB_OFDM_RATE_54MB 0x6C +#define RTLLIB_BASIC_RATE_MASK 0x80 + +/* this is stolen and modified from the madwifi driver*/ +#define RTLLIB_FC0_TYPE_MASK 0x0c +#define RTLLIB_FC0_TYPE_DATA 0x08 +#define RTLLIB_FC0_SUBTYPE_MASK 0xB0 +#define RTLLIB_FC0_SUBTYPE_QOS 0x80 + +#define RTLLIB_QOS_HAS_SEQ(fc) \ + (((fc) & (RTLLIB_FC0_TYPE_MASK | RTLLIB_FC0_SUBTYPE_MASK)) == \ + (RTLLIB_FC0_TYPE_DATA | RTLLIB_FC0_SUBTYPE_QOS)) + +/* this is stolen from ipw2200 driver */ +#define IEEE_IBSS_MAC_HASH_SIZE 31 +struct ieee_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num[17]; + u16 frag_num[17]; + unsigned long packet_time[17]; + struct list_head list; +}; + +/* NOTE: This data is for statistical purposes; not all hardware provides this + * information for frames received. Not setting these will not cause + * any adverse affects. + */ +struct rtllib_rx_stats { + u64 mac_time; + s8 rssi; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ + u8 control; + u8 mask; + u16 len; + u64 tsf; + u32 beacon_time; + u8 nic_type; + u16 Length; + u8 SignalQuality; + s32 RecvSignalPower; + s8 RxPower; + u8 SignalStrength; + u16 bHwError:1; + u16 bCRC:1; + u16 bICV:1; + u16 bShortPreamble:1; + u16 Antenna:1; + u16 Decrypted:1; + u16 Wakeup:1; + u16 Reserved0:1; + u8 AGC; + u32 TimeStampLow; + u32 TimeStampHigh; + bool bShift; + bool bIsQosData; + + u8 RxDrvInfoSize; + u8 RxBufShift; + bool bIsAMPDU; + bool bFirstMPDU; + bool bContainHTC; + bool RxIs40MHzPacket; + u32 RxPWDBAll; + u8 RxMIMOSignalStrength[4]; + s8 RxMIMOSignalQuality[2]; + bool bPacketMatchBSSID; + bool bIsCCK; + bool bPacketToSelf; + u16 packetlength; + u16 fraglength; + u16 fragoffset; + u16 ntotalfrag; + bool bPacketBeacon; + bool bToSelfBA; + u16 Seq_Num; +}; + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. + */ +#define RTLLIB_FRAG_CACHE_LEN 4 + +struct rtllib_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + +struct rtllib_device; + +#define SEC_ACTIVE_KEY (1<<4) +#define SEC_AUTH_MODE (1<<5) +#define SEC_UNICAST_GROUP (1<<6) +#define SEC_LEVEL (1<<7) +#define SEC_ENABLED (1<<8) + +#define SEC_LEVEL_0 0 /* None */ +#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ +#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ +#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ +#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ + +#define SEC_ALG_NONE 0 +#define SEC_ALG_WEP 1 +#define SEC_ALG_TKIP 2 +#define SEC_ALG_CCMP 4 + +#define WEP_KEY_LEN 13 +#define SCM_KEY_LEN 32 + +struct rtllib_security { + u16 active_key:2, + enabled:1, + auth_mode:2, + auth_algo:4, + unicast_uses_group:1, + encrypt:1; + u8 key_sizes[NUM_WEP_KEYS]; + u8 keys[NUM_WEP_KEYS][SCM_KEY_LEN]; + u8 level; + u16 flags; +} __packed; + +/* 802.11 data frame from AP + * ,-------------------------------------------------------------------. + * Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + * |------|------|---------|---------|---------|------|---------|------| + * Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | + * | | tion | (BSSID) | | | ence | data | | + * `-------------------------------------------------------------------' + * Total: 28-2340 bytes + */ + +/* Management Frame Information Element Types */ +enum rtllib_mfie { + MFIE_TYPE_SSID = 0, + MFIE_TYPE_RATES = 1, + MFIE_TYPE_FH_SET = 2, + MFIE_TYPE_DS_SET = 3, + MFIE_TYPE_CF_SET = 4, + MFIE_TYPE_TIM = 5, + MFIE_TYPE_IBSS_SET = 6, + MFIE_TYPE_COUNTRY = 7, + MFIE_TYPE_HOP_PARAMS = 8, + MFIE_TYPE_HOP_TABLE = 9, + MFIE_TYPE_REQUEST = 10, + MFIE_TYPE_CHALLENGE = 16, + MFIE_TYPE_POWER_CONSTRAINT = 32, + MFIE_TYPE_POWER_CAPABILITY = 33, + MFIE_TYPE_TPC_REQUEST = 34, + MFIE_TYPE_TPC_REPORT = 35, + MFIE_TYPE_SUPP_CHANNELS = 36, + MFIE_TYPE_CSA = 37, + MFIE_TYPE_MEASURE_REQUEST = 38, + MFIE_TYPE_MEASURE_REPORT = 39, + MFIE_TYPE_QUIET = 40, + MFIE_TYPE_IBSS_DFS = 41, + MFIE_TYPE_ERP = 42, + MFIE_TYPE_HT_CAP = 45, + MFIE_TYPE_RSN = 48, + MFIE_TYPE_RATES_EX = 50, + MFIE_TYPE_HT_INFO = 61, + MFIE_TYPE_AIRONET = 133, + MFIE_TYPE_GENERIC = 221, + MFIE_TYPE_QOS_PARAMETER = 222, +}; + +/* Minimal header; can be used for passing 802.11 frames with sufficient + * information to determine what type of underlying data type is actually + * stored in the data. + */ +struct rtllib_pspoll_hdr { + __le16 frame_ctl; + __le16 aid; + u8 bssid[ETH_ALEN]; + u8 ta[ETH_ALEN]; +} __packed; + +struct rtllib_hdr { + __le16 frame_ctl; + __le16 duration_id; + u8 payload[]; +} __packed; + +struct rtllib_hdr_1addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 payload[]; +} __packed; + +struct rtllib_hdr_2addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 payload[]; +} __packed; + +struct rtllib_hdr_3addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 payload[]; +} __packed; + +struct rtllib_hdr_4addr { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 addr4[ETH_ALEN]; + u8 payload[]; +} __packed; + +struct rtllib_hdr_3addrqos { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + __le16 qos_ctl; + u8 payload[]; +} __packed; + +struct rtllib_hdr_4addrqos { + __le16 frame_ctl; + __le16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + __le16 seq_ctl; + u8 addr4[ETH_ALEN]; + __le16 qos_ctl; + u8 payload[]; +} __packed; + +struct rtllib_info_element { + u8 id; + u8 len; + u8 data[]; +} __packed; + +struct rtllib_authentication { + struct rtllib_hdr_3addr header; + __le16 algorithm; + __le16 transaction; + __le16 status; + /*challenge*/ + struct rtllib_info_element info_element[]; +} __packed; + +struct rtllib_disauth { + struct rtllib_hdr_3addr header; + __le16 reason; +} __packed; + +struct rtllib_disassoc { + struct rtllib_hdr_3addr header; + __le16 reason; +} __packed; + +struct rtllib_probe_request { + struct rtllib_hdr_3addr header; + /* SSID, supported rates */ + struct rtllib_info_element info_element[]; +} __packed; + +struct rtllib_probe_response { + struct rtllib_hdr_3addr header; + u32 time_stamp[2]; + __le16 beacon_interval; + __le16 capability; + /* SSID, supported rates, FH params, DS params, + * CF params, IBSS params, TIM (if beacon), RSN + */ + struct rtllib_info_element info_element[]; +} __packed; + +/* Alias beacon for probe_response */ +#define rtllib_beacon rtllib_probe_response + +struct rtllib_assoc_request_frame { + struct rtllib_hdr_3addr header; + __le16 capability; + __le16 listen_interval; + /* SSID, supported rates, RSN */ + struct rtllib_info_element info_element[]; +} __packed; + +struct rtllib_assoc_response_frame { + struct rtllib_hdr_3addr header; + __le16 capability; + __le16 status; + __le16 aid; + struct rtllib_info_element info_element[]; /* supported rates */ +} __packed; + +struct rtllib_txb { + u8 nr_frags; + u8 encrypted; + u8 queue_index; + u8 rts_included; + u16 reserved; + __le16 frag_size; + __le16 payload_size; + struct sk_buff *fragments[] __counted_by(nr_frags); +}; + +#define MAX_SUBFRAME_COUNT 64 +struct rtllib_rxb { + u8 nr_subframes; + struct sk_buff *subframes[MAX_SUBFRAME_COUNT]; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; +} __packed; + +union frameqos { + u16 shortdata; + u8 chardata[2]; + struct { + u16 tid:4; + u16 eosp:1; + u16 ack_policy:2; + u16 reserved:1; + u16 txop:8; + } field; +}; + +/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs + * only use 8, and then use extended rates for the remaining supported + * rates. Other APs, however, stick all of their supported rates on the + * main rates information element... + */ +#define MAX_RATES_LENGTH ((u8)12) +#define MAX_RATES_EX_LENGTH ((u8)16) +#define MAX_NETWORK_COUNT 96 + +#define MAX_CHANNEL_NUMBER 161 +#define RTLLIB_SOFTMAC_SCAN_TIME 100 +#define RTLLIB_SOFTMAC_ASSOC_RETRY_TIME (HZ * 2) + +#define MAX_WPA_IE_LEN 64 +#define MAX_WZC_IE_LEN 256 + +#define NETWORK_EMPTY_ESSID (1<<0) +#define NETWORK_HAS_OFDM (1<<1) +#define NETWORK_HAS_CCK (1<<2) + +/* QoS structure */ +#define NETWORK_HAS_QOS_PARAMETERS (1<<3) +#define NETWORK_HAS_QOS_INFORMATION (1<<4) +#define NETWORK_HAS_QOS_MASK (NETWORK_HAS_QOS_PARAMETERS | \ + NETWORK_HAS_QOS_INFORMATION) +/* 802.11h */ +#define NETWORK_HAS_ERP_VALUE (1<<10) + +#define QOS_QUEUE_NUM 4 +#define QOS_OUI_LEN 3 +#define QOS_OUI_TYPE 2 +#define QOS_ELEMENT_ID 221 +#define QOS_OUI_INFO_SUB_TYPE 0 +#define QOS_OUI_PARAM_SUB_TYPE 1 +#define QOS_VERSION_1 1 + +struct rtllib_qos_information_element { + u8 elementID; + u8 length; + u8 qui[QOS_OUI_LEN]; + u8 qui_type; + u8 qui_subtype; + u8 version; + u8 ac_info; +} __packed; + +struct rtllib_qos_ac_parameter { + u8 aci_aifsn; + u8 ecw_min_max; + __le16 tx_op_limit; +} __packed; + +struct rtllib_qos_parameter_info { + struct rtllib_qos_information_element info_element; + u8 reserved; + struct rtllib_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; +} __packed; + +struct rtllib_qos_parameters { + __le16 cw_min[QOS_QUEUE_NUM]; + __le16 cw_max[QOS_QUEUE_NUM]; + u8 aifs[QOS_QUEUE_NUM]; + u8 flag[QOS_QUEUE_NUM]; + __le16 tx_op_limit[QOS_QUEUE_NUM]; +} __packed; + +struct rtllib_qos_data { + struct rtllib_qos_parameters parameters; + unsigned int wmm_acm; + int active; + int supported; + u8 param_count; + u8 old_param_count; +}; + +struct rtllib_tim_parameters { + u8 tim_count; + u8 tim_period; +} __packed; + +struct rtllib_wmm_ac_param { + u8 ac_aci_acm_aifsn; + u8 ac_ecwmin_ecwmax; + u16 ac_txop_limit; +}; + +enum eap_type { + EAP_PACKET = 0, + EAPOL_START, + EAPOL_LOGOFF, + EAPOL_KEY, + EAPOL_ENCAP_ASF_ALERT +}; + +static const char * const eap_types[] = { + [EAP_PACKET] = "EAP-Packet", + [EAPOL_START] = "EAPOL-Start", + [EAPOL_LOGOFF] = "EAPOL-Logoff", + [EAPOL_KEY] = "EAPOL-Key", + [EAPOL_ENCAP_ASF_ALERT] = "EAPOL-Encap-ASF-Alert" +}; + +static inline const char *eap_get_type(int type) +{ + return ((u32)type >= ARRAY_SIZE(eap_types)) ? "Unknown" : + eap_types[type]; +} + +static inline u8 Frame_QoSTID(u8 *buf) +{ + struct rtllib_hdr_3addr *hdr; + u16 fc; + + hdr = (struct rtllib_hdr_3addr *)buf; + fc = le16_to_cpu(hdr->frame_ctl); + return (u8)((union frameqos *)(buf + (((fc & RTLLIB_FCTL_TODS) && + (fc & RTLLIB_FCTL_FROMDS)) ? 30 : 24)))->field.tid; +} + +struct eapol { + u8 snap[6]; + u16 ethertype; + u8 version; + u8 type; + u16 length; +} __packed; + +struct rtllib_softmac_stats { + unsigned int rx_ass_ok; + unsigned int rx_ass_err; + unsigned int rx_probe_rq; + unsigned int tx_probe_rs; + unsigned int tx_beacons; + unsigned int rx_auth_rq; + unsigned int rx_auth_rs_ok; + unsigned int rx_auth_rs_err; + unsigned int tx_auth_rq; + unsigned int no_auth_rs; + unsigned int no_ass_rs; + unsigned int tx_ass_rq; + unsigned int rx_ass_rq; + unsigned int tx_probe_rq; + unsigned int reassoc; + unsigned int swtxstop; + unsigned int swtxawake; + unsigned char CurrentShowTxate; + unsigned char last_packet_rate; + unsigned int txretrycount; +}; + +/* These are the data types that can make up management packets + * + * u16 auth_algorithm; + * u16 auth_sequence; + * u16 beacon_interval; + * u16 capability; + * u8 current_ap[ETH_ALEN]; + * u16 listen_interval; + * struct { + * u16 association_id:14, reserved:2; + * } __packed; + * u32 time_stamp[2]; + * u16 reason; + * u16 status; + */ + +#define RTLLIB_DEFAULT_TX_ESSID "Penguin" +#define RTLLIB_DEFAULT_BASIC_RATE 2 + +enum {WMM_all_frame, WMM_two_frame, WMM_four_frame, WMM_six_frame}; +#define MAX_SP_Len (WMM_all_frame << 4) +#define RTLLIB_QOS_TID 0x0f +#define QOS_CTL_NOTCONTAIN_ACK (0x01 << 5) + +#define RTLLIB_DTIM_MBCAST 4 +#define RTLLIB_DTIM_UCAST 2 +#define RTLLIB_DTIM_VALID 1 +#define RTLLIB_DTIM_INVALID 0 + +#define RTLLIB_PS_DISABLED 0 +#define RTLLIB_PS_UNICAST RTLLIB_DTIM_UCAST +#define RTLLIB_PS_MBCAST RTLLIB_DTIM_MBCAST + +#define WME_AC_BK 0x00 +#define WME_AC_BE 0x01 +#define WME_AC_VI 0x02 +#define WME_AC_VO 0x03 +#define WME_AC_PRAM_LEN 16 + +#define MAX_RECEIVE_BUFFER_SIZE 9100 + +#define UP2AC(up) ( \ + ((up) < 1) ? WME_AC_BE : \ + ((up) < 3) ? WME_AC_BK : \ + ((up) < 4) ? WME_AC_BE : \ + ((up) < 6) ? WME_AC_VI : \ + WME_AC_VO) + +#define ETHER_ADDR_LEN 6 /* length of an Ethernet address */ +#define ETHERNET_HEADER_SIZE 14 /* length of two Ethernet address + * plus ether type + */ + +enum erp_t { + ERP_NonERPpresent = 0x01, + ERP_UseProtection = 0x02, + ERP_BarkerPreambleMode = 0x04, +}; + +struct rtllib_network { + /* These entries are used to identify a unique network */ + u8 bssid[ETH_ALEN]; + u8 channel; + /* Ensure null-terminated for any debug msgs */ + u8 ssid[IW_ESSID_MAX_SIZE + 1]; + u8 ssid_len; + u8 hidden_ssid[IW_ESSID_MAX_SIZE + 1]; + u8 hidden_ssid_len; + struct rtllib_qos_data qos_data; + + bool bWithAironetIE; + bool bCkipSupported; + bool bCcxRmEnable; + u8 CcxRmState[2]; + bool bMBssidValid; + u8 MBssidMask; + u8 MBssid[ETH_ALEN]; + bool bWithCcxVerNum; + u8 BssCcxVerNumber; + /* These are network statistics */ + struct rtllib_rx_stats stats; + u16 capability; + u8 rates[MAX_RATES_LENGTH]; + u8 rates_len; + u8 rates_ex[MAX_RATES_EX_LENGTH]; + u8 rates_ex_len; + unsigned long last_scanned; + u8 mode; + u32 flags; + u32 time_stamp[2]; + u16 beacon_interval; + u16 listen_interval; + u16 atim_window; + u8 erp_value; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + u8 wzc_ie[MAX_WZC_IE_LEN]; + size_t wzc_ie_len; + + struct rtllib_tim_parameters tim; + u8 dtim_period; + u8 dtim_data; + u64 last_dtim_sta_time; + + u8 wmm_info; + struct rtllib_wmm_ac_param wmm_param[4]; + u8 Turbo_Enable; + u16 CountryIeLen; + u8 CountryIeBuf[MAX_IE_LEN]; + struct bss_ht bssht; + bool broadcom_cap_exist; + bool realtek_cap_exit; + bool marvell_cap_exist; + bool ralink_cap_exist; + bool atheros_cap_exist; + bool cisco_cap_exist; + bool airgo_cap_exist; + bool unknown_cap_exist; + bool berp_info_valid; + bool buseprotection; + u8 SignalStrength; + u8 RSSI; + struct list_head list; +}; + +enum rtl_link_state { + /* the card is not linked at all */ + MAC80211_NOLINK = 0, + + /* RTLLIB_ASSOCIATING* are for BSS client mode + * the driver shall not perform RX filtering unless + * the state is LINKED. + * The driver shall just check for the state LINKED and + * defaults to NOLINK for ALL the other states (including + * LINKED_SCANNING) + */ + + /* the association procedure will start (wq scheduling)*/ + RTLLIB_ASSOCIATING, + RTLLIB_ASSOCIATING_RETRY, + + /* the association procedure is sending AUTH request*/ + RTLLIB_ASSOCIATING_AUTHENTICATING, + + /* the association procedure has successfully authenticated + * and is sending association request + */ + RTLLIB_ASSOCIATING_AUTHENTICATED, + + /* the link is ok. the card associated to a BSS or linked + * to a ibss cell or acting as an AP and creating the bss + */ + MAC80211_LINKED, + + /* same as LINKED, but the driver shall apply RX filter + * rules as we are in NO_LINK mode. As the card is still + * logically linked, but it is doing a syncro site survey + * then it will be back to LINKED state. + */ + MAC80211_LINKED_SCANNING, +}; + +#define DEFAULT_MAX_SCAN_AGE (15 * HZ) +#define DEFAULT_FTS 2346 + +#define CFG_RTLLIB_RESERVE_FCS (1<<0) +#define CFG_RTLLIB_COMPUTE_FCS (1<<1) + +struct tx_pending { + int frag; + struct rtllib_txb *txb; +}; + +struct bandwidth_autoswitch { + long threshold_20Mhzto40Mhz; + long threshold_40Mhzto20Mhz; + bool bforced_tx20Mhz; + bool bautoswitch_enable; +}; + +#define REORDER_WIN_SIZE 128 +#define REORDER_ENTRY_NUM 128 +struct rx_reorder_entry { + struct list_head List; + u16 SeqNum; + struct rtllib_rxb *prxb; +}; + +enum fsync_state { + Default_Fsync, + HW_Fsync, + SW_Fsync +}; + +enum rt_ps_mode { + eActive, + eMaxPs, + eFastPs, + eAutoPs, +}; + +enum ips_callback_function { + IPS_CALLBACK_NONE = 0, + IPS_CALLBACK_MGNT_LINK_REQUEST = 1, + IPS_CALLBACK_JOIN_REQUEST = 2, +}; + +enum rt_rf_power_state { + rf_on, + rf_sleep, + rf_off +}; + +struct rt_pwr_save_ctrl { + bool bSwRfProcessing; + enum rt_rf_power_state eInactivePowerState; + enum ips_callback_function ReturnPoint; + + bool bLeisurePs; + u8 LpsIdleCount; + u8 reg_max_lps_awake_intvl; + u8 LPSAwakeIntvl; + + u32 CurPsLevel; +}; + +#define RT_RF_CHANGE_SOURCE u32 + +#define RF_CHANGE_BY_SW BIT31 +#define RF_CHANGE_BY_HW BIT30 +#define RF_CHANGE_BY_PS BIT29 +#define RF_CHANGE_BY_IPS BIT28 +#define RF_CHANGE_BY_INIT 0 + +enum country_code_type { + COUNTRY_CODE_FCC = 0, + COUNTRY_CODE_IC = 1, + COUNTRY_CODE_ETSI = 2, + COUNTRY_CODE_SPAIN = 3, + COUNTRY_CODE_FRANCE = 4, + COUNTRY_CODE_MKK = 5, + COUNTRY_CODE_MKK1 = 6, + COUNTRY_CODE_ISRAEL = 7, + COUNTRY_CODE_TELEC = 8, + COUNTRY_CODE_MIC = 9, + COUNTRY_CODE_GLOBAL_DOMAIN = 10, + COUNTRY_CODE_WORLD_WIDE_13 = 11, + COUNTRY_CODE_TELEC_NETGEAR = 12, + COUNTRY_CODE_MAX +}; + +enum scan_op_backup_opt { + SCAN_OPT_BACKUP = 0, + SCAN_OPT_RESTORE, + SCAN_OPT_MAX +}; + +enum fw_cmd_io_type { + FW_CMD_DIG_ENABLE = 0, + FW_CMD_DIG_DISABLE = 1, + FW_CMD_DIG_HALT = 2, + FW_CMD_DIG_RESUME = 3, + FW_CMD_HIGH_PWR_ENABLE = 4, + FW_CMD_HIGH_PWR_DISABLE = 5, + FW_CMD_RA_RESET = 6, + FW_CMD_RA_ACTIVE = 7, + FW_CMD_RA_REFRESH_N = 8, + FW_CMD_RA_REFRESH_BG = 9, + FW_CMD_RA_INIT = 10, + FW_CMD_IQK_ENABLE = 11, + FW_CMD_TXPWR_TRACK_ENABLE = 12, + FW_CMD_TXPWR_TRACK_DISABLE = 13, + FW_CMD_TXPWR_TRACK_THERMAL = 14, + FW_CMD_PAUSE_DM_BY_SCAN = 15, + FW_CMD_RESUME_DM_BY_SCAN = 16, + FW_CMD_RA_REFRESH_N_COMB = 17, + FW_CMD_RA_REFRESH_BG_COMB = 18, + FW_CMD_ANTENNA_SW_ENABLE = 19, + FW_CMD_ANTENNA_SW_DISABLE = 20, + FW_CMD_TX_FEEDBACK_CCX_ENABLE = 21, + FW_CMD_LPS_ENTER = 22, + FW_CMD_LPS_LEAVE = 23, +}; + +#define RT_MAX_LD_SLOT_NUM 10 +struct rt_link_detect { + u32 NumRecvBcnInPeriod; + u32 NumRecvDataInPeriod; + + u32 RxBcnNum[RT_MAX_LD_SLOT_NUM]; + u32 RxDataNum[RT_MAX_LD_SLOT_NUM]; + u16 SlotNum; + u16 SlotIndex; + + u32 NumTxOkInPeriod; + u32 NumRxOkInPeriod; + u32 NumRxUnicastOkInPeriod; + bool bBusyTraffic; + bool bHigherBusyTraffic; + bool bHigherBusyRxTraffic; +}; + +struct sw_cam_table { + u8 macaddr[ETH_ALEN]; + bool bused; + u8 key_buf[16]; + u16 key_type; + u8 useDK; + u8 key_index; + +}; + +#define TOTAL_CAM_ENTRY 32 +struct rate_adaptive { + u8 rate_adaptive_disabled; + u8 ratr_state; + u16 reserve; + + u32 high_rssi_thresh_for_ra; + u32 high2low_rssi_thresh_for_ra; + u8 low2high_rssi_thresh_for_ra40M; + u32 low_rssi_thresh_for_ra40M; + u8 low2high_rssi_thresh_for_ra20M; + u32 low_rssi_thresh_for_ra20M; + u32 upper_rssi_threshold_ratr; + u32 middle_rssi_threshold_ratr; + u32 low_rssi_threshold_ratr; + u32 low_rssi_threshold_ratr_40M; + u32 low_rssi_threshold_ratr_20M; + u8 ping_rssi_enable; + u32 ping_rssi_ratr; + u32 ping_rssi_thresh_for_ra; + u32 last_ratr; + u8 PreRATRState; + +}; + +#define NUM_PMKID_CACHE 16 +struct rt_pmkid_list { + u8 Bssid[ETH_ALEN]; + u8 PMKID[16]; + u8 SsidBuf[33]; + u8 bUsed; +}; + +struct rt_intel_promisc_mode { + bool promiscuous_on; + bool fltr_src_sta_frame; +}; + +/*************** DRIVER STATUS *****/ +#define STATUS_SCANNING 0 +/*************** DRIVER STATUS *****/ + +enum { + LPS_IS_WAKE = 0, + LPS_IS_SLEEP = 1, + LPS_WAIT_NULL_DATA_SEND = 2, +}; + +struct rtllib_device { + struct pci_dev *pdev; + struct net_device *dev; + struct rtllib_security sec; + + bool disable_mgnt_queue; + + unsigned long status; + u8 CntAfterLink; + + enum rt_op_mode OpMode; + + /* The last AssocReq/Resp IEs */ + u8 *assocreq_ies, *assocresp_ies; + size_t assocreq_ies_len, assocresp_ies_len; + + bool bForcedBgMode; + + u8 hwsec_active; + bool is_silent_reset; + bool is_roaming; + bool ieee_up; + bool cannot_notify; + bool bSupportRemoteWakeUp; + bool actscanning; + bool FirstIe_InScan; + bool be_scan_inprogress; + bool beinretry; + enum rt_rf_power_state rf_power_state; + RT_RF_CHANGE_SOURCE rf_off_reason; + bool is_set_key; + bool wx_set_enc; + struct rt_hi_throughput *ht_info; + + spinlock_t reorder_spinlock; + u8 reg_dot11ht_oper_rate_set[16]; + u8 reg_dot11tx_ht_oper_rate_set[16]; + u8 dot11ht_oper_rate_set[16]; + u8 reg_ht_supp_rate_set[16]; + u8 HTCurrentOperaRate; + u8 HTHighestOperaRate; + u8 tx_dis_rate_fallback; + u8 tx_use_drv_assinged_rate; + u8 bTxEnableFwCalcDur; + atomic_t atm_swbw; + + struct list_head Tx_TS_Admit_List; + struct list_head Tx_TS_Pending_List; + struct list_head Tx_TS_Unused_List; + struct tx_ts_record TxTsRecord[TOTAL_TS_NUM]; + struct list_head Rx_TS_Admit_List; + struct list_head Rx_TS_Pending_List; + struct list_head Rx_TS_Unused_List; + struct rx_ts_record RxTsRecord[TOTAL_TS_NUM]; + struct rx_reorder_entry RxReorderEntry[128]; + struct list_head RxReorder_Unused_List; + + /* Bookkeeping structures */ + struct net_device_stats stats; + struct rtllib_softmac_stats softmac_stats; + + /* Probe / Beacon management */ + struct list_head network_free_list; + struct list_head network_list; + struct rtllib_network *networks; + int scans; + int scan_age; + + int iw_mode; /* operating mode (IW_MODE_*) */ + bool net_promiscuous_md; + struct rt_intel_promisc_mode intel_promiscuous_md_info; + + spinlock_t lock; + spinlock_t wpax_suitlist_lock; + + int tx_headroom; /* Set to size of any additional room needed at front + * of allocated Tx SKBs + */ + u32 config; + + /* WEP and other encryption related settings at the device level */ + int open_wep; /* Set to 1 to allow unencrypted frames */ + int auth_mode; + int reset_on_keychange; /* Set to 1 if the HW needs to be reset on + * WEP key changes + */ + + int ieee802_1x; /* is IEEE 802.1X used */ + + /* WPA data */ + bool bHalfWirelessN24GMode; + int wpa_enabled; + int drop_unencrypted; + int tkip_countermeasures; + int privacy_invoked; + size_t wpa_ie_len; + u8 *wpa_ie; + size_t wps_ie_len; + u8 *wps_ie; + u8 ap_mac_addr[ETH_ALEN]; + u16 pairwise_key_type; + u16 group_key_type; + + struct lib80211_crypt_info crypt_info; + + struct sw_cam_table swcamtable[TOTAL_CAM_ENTRY]; + + struct rt_pmkid_list PMKIDList[NUM_PMKID_CACHE]; + + /* Fragmentation structures */ + struct rtllib_frag_entry frag_cache[17][RTLLIB_FRAG_CACHE_LEN]; + unsigned int frag_next_idx[17]; + u16 fts; /* Fragmentation Threshold */ +#define DEFAULT_RTS_THRESHOLD 2346U +#define MIN_RTS_THRESHOLD 1 +#define MAX_RTS_THRESHOLD 2346U + u16 rts; /* RTS threshold */ + + /* Association info */ + u8 bssid[ETH_ALEN]; + + /* This stores infos for the current network. + * Either the network we are associated in INFRASTRUCTURE + * or the network that we are creating in MASTER mode. + * ad-hoc is a mixture ;-). + * Note that in infrastructure mode, even when not associated, + * fields bssid and essid may be valid (if wpa_set and essid_set + * are true) as thy carry the value set by the user via iwconfig + */ + struct rtllib_network current_network; + + enum rtl_link_state link_state; + + int mode; /* A, B, G */ + + /* used for forcing the ibss workqueue to terminate + * without wait for the syncro scan to terminate + */ + short sync_scan_hurryup; + u16 scan_watch_dog; + + /* map of allowed channels. 0 is dummy */ + void *dot11d_info; + bool global_domain; + u8 active_channel_map[MAX_CHANNEL_NUMBER+1]; + + u8 bss_start_channel; + u8 ibss_maxjoin_chal; + + int rate; /* current rate */ + int basic_rate; + + short active_scan; + + /* this contains flags for selectively enable softmac support */ + u16 softmac_features; + + /* if the sequence control field is not filled by HW */ + u16 seq_ctrl[5]; + + /* association procedure transaction sequence number */ + u16 associate_seq; + + /* AID for RTXed association responses */ + u16 assoc_id; + + /* power save mode related*/ + u8 ack_tx_to_ieee; + short ps; + short sta_sleep; + int ps_timeout; + int ps_period; + struct work_struct ps_task; + u64 ps_time; + bool polling; + + short raw_tx; + /* used if IEEE_SOFTMAC_TX_QUEUE is set */ + short queue_stop; + short scanning_continue; + short proto_started; + short proto_stoppping; + + struct mutex wx_mutex; + struct mutex scan_mutex; + struct mutex ips_mutex; + + spinlock_t mgmt_tx_lock; + spinlock_t beacon_lock; + + short beacon_txing; + + short wap_set; + short ssid_set; + + /* set on initialization */ + unsigned int wmm_acm; + + /* for discarding duplicated packets in IBSS */ + struct list_head ibss_mac_hash[IEEE_IBSS_MAC_HASH_SIZE]; + + /* for discarding duplicated packets in BSS */ + u16 last_rxseq_num[17]; /* rx seq previous per-tid */ + u16 last_rxfrag_num[17];/* tx frag previous per-tid */ + unsigned long last_packet_time[17]; + + /* for PS mode */ + unsigned long last_rx_ps_time; + bool bAwakePktSent; + u8 LPSDelayCnt; + + /* used if IEEE_SOFTMAC_SINGLE_QUEUE is set */ + struct sk_buff *mgmt_queue_ring[MGMT_QUEUE_NUM]; + int mgmt_queue_head; + int mgmt_queue_tail; + u8 AsocRetryCount; + struct sk_buff_head skb_waitQ[MAX_QUEUE_SIZE]; + + bool bdynamic_txpower_enable; + + bool bCTSToSelfEnable; + + u32 fsync_time_interval; + u32 fsync_rate_bitmap; + u8 fsync_rssi_threshold; + bool bfsync_enable; + + u8 fsync_multiple_timeinterval; + u32 fsync_firstdiff_ratethreshold; + u32 fsync_seconddiff_ratethreshold; + enum fsync_state fsync_state; + bool bis_any_nonbepkts; + struct bandwidth_autoswitch bandwidth_auto_switch; + bool FwRWRF; + + struct rt_link_detect link_detect_info; + bool bIsAggregateFrame; + struct rt_pwr_save_ctrl pwr_save_ctrl; + + /* used if IEEE_SOFTMAC_TX_QUEUE is set */ + struct tx_pending tx_pending; + + /* used if IEEE_SOFTMAC_ASSOCIATE is set */ + struct timer_list associate_timer; + + /* used if IEEE_SOFTMAC_BEACONS is set */ + struct timer_list beacon_timer; + u8 need_sw_enc; + struct work_struct associate_complete_wq; + struct work_struct ips_leave_wq; + struct delayed_work associate_procedure_wq; + struct delayed_work softmac_scan_wq; + struct delayed_work associate_retry_wq; + struct delayed_work start_ibss_wq; + struct delayed_work hw_wakeup_wq; + struct delayed_work hw_sleep_wq; + struct delayed_work link_change_wq; + struct work_struct wx_sync_scan_wq; + + union { + struct rtllib_rxb *RfdArray[REORDER_WIN_SIZE]; + struct rtllib_rxb *stats_IndicateArray[REORDER_WIN_SIZE]; + struct rtllib_rxb *prxbIndicateArray[REORDER_WIN_SIZE]; + struct { + struct sw_chnl_cmd PreCommonCmd[MAX_PRECMD_CNT]; + struct sw_chnl_cmd PostCommonCmd[MAX_POSTCMD_CNT]; + struct sw_chnl_cmd RfDependCmd[MAX_RFDEPENDCMD_CNT]; + }; + }; + + /* Callback functions */ + + /* Softmac-generated frames (management) are TXed via this + * callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is + * not set. As some cards may have different HW queues that + * one might want to use for data and management frames + * the option to have two callbacks might be useful. + * This function can't sleep. + */ + int (*softmac_hard_start_xmit)(struct sk_buff *skb, + struct net_device *dev); + + /* used instead of hard_start_xmit (not softmac_hard_start_xmit) + * if the IEEE_SOFTMAC_TX_QUEUE feature is used to TX data + * frames. If the option IEEE_SOFTMAC_SINGLE_QUEUE is also set + * then also management frames are sent via this callback. + * This function can't sleep. + */ + void (*softmac_data_hard_start_xmit)(struct sk_buff *skb, + struct net_device *dev, int rate); + + /* ask to the driver to retune the radio. + * This function can sleep. the driver should ensure + * the radio has been switched before return. + */ + void (*set_chan)(struct net_device *dev, short ch); + + /* indicate the driver that the link state is changed + * for example it may indicate the card is associated now. + * Driver might be interested in this to apply RX filter + * rules or simply light the LINK led + */ + void (*link_change)(struct net_device *dev); + + /* these two function indicates to the HW when to start + * and stop to send beacons. This is used when the + * IEEE_SOFTMAC_BEACONS is not set. For now the + * stop_send_bacons is NOT guaranteed to be called only + * after start_send_beacons. + */ + void (*start_send_beacons)(struct net_device *dev); + void (*stop_send_beacons)(struct net_device *dev); + + /* power save mode related */ + void (*sta_wake_up)(struct net_device *dev); + void (*enter_sleep_state)(struct net_device *dev, u64 time); + short (*ps_is_queue_empty)(struct net_device *dev); + int (*handle_beacon)(struct net_device *dev, + struct rtllib_beacon *beacon, + struct rtllib_network *network); + int (*handle_assoc_response)(struct net_device *dev, + struct rtllib_assoc_response_frame *resp, + struct rtllib_network *network); + + /* check whether Tx hw resource available */ + short (*check_nic_enough_desc)(struct net_device *dev, int queue_index); + void (*set_bw_mode_handler)(struct net_device *dev, + enum ht_channel_width bandwidth, + enum ht_extchnl_offset Offset); + bool (*GetNmodeSupportBySecCfg)(struct net_device *dev); + void (*set_wireless_mode)(struct net_device *dev, u8 wireless_mode); + bool (*GetHalfNmodeSupportByAPsHandler)(struct net_device *dev); + u8 (*rtllib_ap_sec_type)(struct rtllib_device *ieee); + void (*init_gain_handler)(struct net_device *dev, u8 Operation); + void (*ScanOperationBackupHandler)(struct net_device *dev, + u8 Operation); + void (*SetHwRegHandler)(struct net_device *dev, u8 variable, u8 *val); + + void (*AllowAllDestAddrHandler)(struct net_device *dev, + bool bAllowAllDA, bool WriteIntoReg); + + void (*rtllib_ips_leave_wq)(struct net_device *dev); + void (*rtllib_ips_leave)(struct net_device *dev); + void (*leisure_ps_leave)(struct net_device *dev); + + /* This must be the last item so that it points to the data + * allocated beyond this structure by alloc_rtllib + */ + u8 priv[]; +}; + +#define IEEE_MODE_MASK (WIRELESS_MODE_B | WIRELESS_MODE_G) + +/* Generate a 802.11 header */ + +/* Uses the channel change callback directly + * instead of [start/stop] scan callbacks + */ +#define IEEE_SOFTMAC_SCAN (1<<2) + +/* Perform authentication and association handshake */ +#define IEEE_SOFTMAC_ASSOCIATE (1<<3) + +/* Generate probe requests */ +#define IEEE_SOFTMAC_PROBERQ (1<<4) + +/* Generate response to probe requests */ +#define IEEE_SOFTMAC_PROBERS (1<<5) + +/* The ieee802.11 stack will manage the netif queue + * wake/stop for the driver, taking care of 802.11 + * fragmentation. See softmac.c for details. + */ +#define IEEE_SOFTMAC_TX_QUEUE (1<<7) + +/* Uses only the softmac_data_hard_start_xmit + * even for TX management frames. + */ +#define IEEE_SOFTMAC_SINGLE_QUEUE (1<<8) + +/* Generate beacons. The stack will enqueue beacons + * to the card + */ +#define IEEE_SOFTMAC_BEACONS (1<<6) + +static inline void *rtllib_priv(struct net_device *dev) +{ + return ((struct rtllib_device *)netdev_priv(dev))->priv; +} + +static inline int rtllib_is_empty_essid(const char *essid, int essid_len) +{ + /* Single white space is for Linksys APs */ + if (essid_len == 1 && essid[0] == ' ') + return 1; + + /* Otherwise, if the entire essid is 0, we assume it is hidden */ + while (essid_len) { + essid_len--; + if (essid[essid_len] != '\0') + return 0; + } + + return 1; +} + +static inline int rtllib_get_hdrlen(u16 fc) +{ + int hdrlen = RTLLIB_3ADDR_LEN; + + switch (WLAN_FC_GET_TYPE(fc)) { + case RTLLIB_FTYPE_DATA: + if ((fc & RTLLIB_FCTL_FROMDS) && (fc & RTLLIB_FCTL_TODS)) + hdrlen = RTLLIB_4ADDR_LEN; /* Addr4 */ + if (RTLLIB_QOS_HAS_SEQ(fc)) + hdrlen += 2; /* QOS ctrl*/ + break; + case RTLLIB_FTYPE_CTL: + switch (WLAN_FC_GET_STYPE(fc)) { + case RTLLIB_STYPE_CTS: + case RTLLIB_STYPE_ACK: + hdrlen = RTLLIB_1ADDR_LEN; + break; + default: + hdrlen = RTLLIB_2ADDR_LEN; + break; + } + break; + } + + return hdrlen; +} + +static inline u8 *rtllib_get_payload(struct rtllib_hdr *hdr) +{ + switch (rtllib_get_hdrlen(le16_to_cpu(hdr->frame_ctl))) { + case RTLLIB_1ADDR_LEN: + return ((struct rtllib_hdr_1addr *)hdr)->payload; + case RTLLIB_2ADDR_LEN: + return ((struct rtllib_hdr_2addr *)hdr)->payload; + case RTLLIB_3ADDR_LEN: + return ((struct rtllib_hdr_3addr *)hdr)->payload; + case RTLLIB_4ADDR_LEN: + return ((struct rtllib_hdr_4addr *)hdr)->payload; + } + return NULL; +} + +static inline int rtllib_is_ofdm_rate(u8 rate) +{ + switch (rate & ~RTLLIB_BASIC_RATE_MASK) { + case RTLLIB_OFDM_RATE_6MB: + case RTLLIB_OFDM_RATE_9MB: + case RTLLIB_OFDM_RATE_12MB: + case RTLLIB_OFDM_RATE_18MB: + case RTLLIB_OFDM_RATE_24MB: + case RTLLIB_OFDM_RATE_36MB: + case RTLLIB_OFDM_RATE_48MB: + case RTLLIB_OFDM_RATE_54MB: + return 1; + } + return 0; +} + +static inline int rtllib_is_cck_rate(u8 rate) +{ + switch (rate & ~RTLLIB_BASIC_RATE_MASK) { + case RTLLIB_CCK_RATE_1MB: + case RTLLIB_CCK_RATE_2MB: + case RTLLIB_CCK_RATE_5MB: + case RTLLIB_CCK_RATE_11MB: + return 1; + } + return 0; +} + +/* rtllib.c */ +void free_rtllib(struct net_device *dev); +struct net_device *alloc_rtllib(int sizeof_priv); + +/* rtllib_tx.c */ + +int rtllib_encrypt_fragment(struct rtllib_device *ieee, + struct sk_buff *frag, + int hdr_len); + +netdev_tx_t rtllib_xmit(struct sk_buff *skb, struct net_device *dev); +void rtllib_txb_free(struct rtllib_txb *txb); + +/* rtllib_rx.c */ +int rtllib_rx(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats); +void rtllib_rx_probe_rq(struct rtllib_device *ieee, + struct sk_buff *skb); +int rtllib_legal_channel(struct rtllib_device *rtllib, u8 channel); + +/* rtllib_wx.c */ +int rtllib_wx_get_scan(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +int rtllib_wx_set_encode(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +int rtllib_wx_get_encode(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +int rtllib_wx_set_encode_ext(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int rtllib_wx_set_auth(struct rtllib_device *ieee, + struct iw_request_info *info, + struct iw_param *data, char *extra); +int rtllib_wx_set_mlme(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int rtllib_wx_set_gen_ie(struct rtllib_device *ieee, u8 *ie, size_t len); + +/* rtllib_softmac.c */ +int rtllib_rx_frame_softmac(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats, u16 type, + u16 stype); +void rtllib_softmac_new_net(struct rtllib_device *ieee, + struct rtllib_network *net); + +void SendDisassociation(struct rtllib_device *ieee, bool deauth, u16 asRsn); +void rtllib_softmac_xmit(struct rtllib_txb *txb, struct rtllib_device *ieee); + +void rtllib_start_ibss(struct rtllib_device *ieee); +int rtllib_softmac_init(struct rtllib_device *ieee); +void rtllib_softmac_free(struct rtllib_device *ieee); +void rtllib_disassociate(struct rtllib_device *ieee); +void rtllib_stop_scan(struct rtllib_device *ieee); +bool rtllib_act_scanning(struct rtllib_device *ieee, bool sync_scan); +void rtllib_stop_scan_syncro(struct rtllib_device *ieee); +void rtllib_start_scan_syncro(struct rtllib_device *ieee); +void rtllib_sta_ps_send_null_frame(struct rtllib_device *ieee, short pwr); +void rtllib_sta_ps_send_pspoll_frame(struct rtllib_device *ieee); +void rtllib_start_protocol(struct rtllib_device *ieee); +void rtllib_stop_protocol(struct rtllib_device *ieee, u8 shutdown); + +void rtllib_EnableNetMonitorMode(struct net_device *dev, bool bInitState); +void rtllib_DisableNetMonitorMode(struct net_device *dev, bool bInitState); +void rtllib_EnableIntelPromiscuousMode(struct net_device *dev, bool bInitState); +void rtllib_DisableIntelPromiscuousMode(struct net_device *dev, + bool bInitState); +void rtllib_softmac_stop_protocol(struct rtllib_device *ieee, + u8 mesh_flag, u8 shutdown); +void rtllib_softmac_start_protocol(struct rtllib_device *ieee, u8 mesh_flag); + +void rtllib_reset_queue(struct rtllib_device *ieee); +void rtllib_wake_all_queues(struct rtllib_device *ieee); +void rtllib_stop_all_queues(struct rtllib_device *ieee); +struct sk_buff *rtllib_get_beacon(struct rtllib_device *ieee); +void rtllib_start_send_beacons(struct rtllib_device *ieee); +void rtllib_stop_send_beacons(struct rtllib_device *ieee); + +void notify_wx_assoc_event(struct rtllib_device *ieee); +void rtllib_ps_tx_ack(struct rtllib_device *ieee, short success); + +void softmac_mgmt_xmit(struct sk_buff *skb, struct rtllib_device *ieee); +u8 rtllib_ap_sec_type(struct rtllib_device *ieee); + +/* rtllib_softmac_wx.c */ + +int rtllib_wx_get_wap(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *ext); + +int rtllib_wx_set_wap(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *awrq, char *extra); + +int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +int rtllib_wx_set_rate(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_get_rate(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +int rtllib_wx_set_essid(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); + +int rtllib_wx_get_freq(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b); +void rtllib_wx_sync_scan_wq(void *data); + +int rtllib_wx_set_rawtx(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_get_name(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_set_power(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_get_power(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_set_rts(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); + +int rtllib_wx_get_rts(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +#define MAX_RECEIVE_BUFFER_SIZE 9100 + +void HTSetConnectBwMode(struct rtllib_device *ieee, + enum ht_channel_width bandwidth, + enum ht_extchnl_offset Offset); +void HTUpdateDefaultSetting(struct rtllib_device *ieee); +void HTConstructCapabilityElement(struct rtllib_device *ieee, + u8 *posHTCap, u8 *len, + u8 isEncrypt, bool bAssoc); +void HTConstructInfoElement(struct rtllib_device *ieee, + u8 *posHTInfo, u8 *len, u8 isEncrypt); +void HTConstructRT2RTAggElement(struct rtllib_device *ieee, + u8 *posRT2RTAgg, u8 *len); +void HTOnAssocRsp(struct rtllib_device *ieee); +void HTInitializeHTInfo(struct rtllib_device *ieee); +void HTInitializeBssDesc(struct bss_ht *pBssHT); +void HTResetSelfAndSavePeerSetting(struct rtllib_device *ieee, + struct rtllib_network *pNetwork); +void HT_update_self_and_peer_setting(struct rtllib_device *ieee, + struct rtllib_network *pNetwork); +u8 HTGetHighestMCSRate(struct rtllib_device *ieee, u8 *pMCSRateSet, + u8 *pMCSFilter); +extern u8 MCS_FILTER_ALL[]; +extern u16 MCS_DATA_RATE[2][2][77]; +u8 HTCCheck(struct rtllib_device *ieee, u8 *pFrame); +void HTResetIOTSetting(struct rt_hi_throughput *ht_info); +bool IsHTHalfNmodeAPs(struct rtllib_device *ieee); +u16 TxCountToDataRate(struct rtllib_device *ieee, u8 nDataRate); +int rtllib_rx_ADDBAReq(struct rtllib_device *ieee, struct sk_buff *skb); +int rtllib_rx_ADDBARsp(struct rtllib_device *ieee, struct sk_buff *skb); +int rtllib_rx_DELBA(struct rtllib_device *ieee, struct sk_buff *skb); +void rtllib_ts_init_add_ba(struct rtllib_device *ieee, struct tx_ts_record *pTS, + u8 Policy, u8 bOverwritePending); +void rtllib_ts_init_del_ba(struct rtllib_device *ieee, + struct ts_common_info *pTsCommonInfo, + enum tr_select TxRxSelect); +void rtllib_ba_setup_timeout(struct timer_list *t); +void rtllib_tx_ba_inact_timeout(struct timer_list *t); +void rtllib_rx_ba_inact_timeout(struct timer_list *t); +void rtllib_reset_ba_entry(struct ba_record *pBA); +bool GetTs(struct rtllib_device *ieee, struct ts_common_info **ppTS, u8 *Addr, + u8 TID, enum tr_select TxRxSelect, bool bAddNewTs); +void TSInitialize(struct rtllib_device *ieee); +void TsStartAddBaProcess(struct rtllib_device *ieee, + struct tx_ts_record *pTxTS); +void RemovePeerTS(struct rtllib_device *ieee, u8 *Addr); +void RemoveAllTS(struct rtllib_device *ieee); + +static inline const char *escape_essid(const char *essid, u8 essid_len) +{ + static char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; + + if (rtllib_is_empty_essid(essid, essid_len)) { + memcpy(escaped, "<hidden>", sizeof("<hidden>")); + return escaped; + } + + snprintf(escaped, sizeof(escaped), "%*pE", essid_len, essid); + return escaped; +} + +/* fun with the built-in rtllib stack... */ +bool rtllib_MgntDisconnect(struct rtllib_device *rtllib, u8 asRsn); + +/* For the function is more related to hardware setting, it's better to use the + * ieee handler to refer to it. + */ +void rtllib_FlushRxTsPendingPkts(struct rtllib_device *ieee, + struct rx_ts_record *pTS); +int rtllib_parse_info_param(struct rtllib_device *ieee, + struct rtllib_info_element *info_element, + u16 length, + struct rtllib_network *network, + struct rtllib_rx_stats *stats); + +void rtllib_indicate_packets(struct rtllib_device *ieee, + struct rtllib_rxb **prxbIndicateArray, u8 index); +void HTUseDefaultSetting(struct rtllib_device *ieee); +#define RT_ASOC_RETRY_LIMIT 5 +u8 MgntQuery_TxRateExcludeCCKRates(struct rtllib_device *ieee); + +#endif /* RTLLIB_H */ diff --git a/drivers/staging/rtl8192e/rtllib_crypt_ccmp.c b/drivers/staging/rtl8192e/rtllib_crypt_ccmp.c new file mode 100644 index 0000000000..f88096bcb1 --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_crypt_ccmp.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/if_ether.h> +#include <linux/if_arp.h> +#include <linux/string.h> +#include <linux/wireless.h> +#include "rtllib.h" + +#include <linux/crypto.h> +#include <crypto/aead.h> + +#include <linux/scatterlist.h> + +#define AES_BLOCK_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 + +struct rtllib_ccmp_data { + u8 key[CCMP_TK_LEN]; + int key_set; + + u8 tx_pn[CCMP_PN_LEN]; + u8 rx_pn[CCMP_PN_LEN]; + + u32 dot11rsna_stats_ccmp_format_errors; + u32 dot11rsna_stats_ccmp_replays; + u32 dot11rsna_stats_ccmp_decrypt_errors; + + int key_idx; + + struct crypto_aead *tfm; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 tx_aad[2 * AES_BLOCK_LEN]; + u8 rx_aad[2 * AES_BLOCK_LEN]; +}; + +static void *rtllib_ccmp_init(int key_idx) +{ + struct rtllib_ccmp_data *priv; + + priv = kzalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + priv->key_idx = key_idx; + + priv->tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(priv->tfm)) { + pr_debug("Could not allocate crypto API aes\n"); + priv->tfm = NULL; + goto fail; + } + return priv; + +fail: + if (priv) { + if (priv->tfm) + crypto_free_aead(priv->tfm); + kfree(priv); + } + + return NULL; +} + +static void rtllib_ccmp_deinit(void *priv) +{ + struct rtllib_ccmp_data *_priv = priv; + + if (_priv && _priv->tfm) + crypto_free_aead(_priv->tfm); + kfree(priv); +} + +static int ccmp_init_iv_and_aad(struct rtllib_hdr_4addr *hdr, + u8 *pn, u8 *iv, u8 *aad) +{ + u8 *pos, qc = 0; + size_t aad_len; + u16 fc; + int a4_included, qc_included; + + fc = le16_to_cpu(hdr->frame_ctl); + a4_included = ((fc & (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS)) == + (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS)); + + qc_included = ((WLAN_FC_GET_TYPE(fc) == RTLLIB_FTYPE_DATA) && + (WLAN_FC_GET_STYPE(fc) & 0x80)); + aad_len = 22; + if (a4_included) + aad_len += 6; + if (qc_included) { + pos = (u8 *)&hdr->addr4; + if (a4_included) + pos += 6; + qc = *pos & 0x0f; + aad_len += 2; + } + /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC + * mode authentication are not allowed to collide, yet both are derived + * from the same vector. We only set L := 1 here to indicate that the + * data size can be represented in (L+1) bytes. The CCM layer will take + * care of storing the data length in the top (L+1) bytes and setting + * and clearing the other bits as is required to derive the two IVs. + */ + iv[0] = 0x1; + + /* Nonce: QC | A2 | PN */ + iv[1] = qc; + memcpy(iv + 2, hdr->addr2, ETH_ALEN); + memcpy(iv + 8, pn, CCMP_PN_LEN); + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + pos = (u8 *)hdr; + aad[0] = pos[0] & 0x8f; + aad[1] = pos[1] & 0xc7; + memcpy(&aad[2], &hdr->addr1, ETH_ALEN); + memcpy(&aad[8], &hdr->addr2, ETH_ALEN); + memcpy(&aad[14], &hdr->addr3, ETH_ALEN); + pos = (u8 *)&hdr->seq_ctl; + aad[20] = pos[0] & 0x0f; + aad[21] = 0; /* all bits masked */ + memset(aad + 22, 0, 8); + if (a4_included) + memcpy(aad + 22, hdr->addr4, ETH_ALEN); + if (qc_included) { + aad[a4_included ? 28 : 22] = qc; + /* rest of QC masked */ + } + + return aad_len; +} + +static int rtllib_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct rtllib_ccmp_data *key = priv; + int i; + u8 *pos; + struct rtllib_hdr_4addr *hdr; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + if (skb_headroom(skb) < CCMP_HDR_LEN || + skb_tailroom(skb) < CCMP_MIC_LEN || + skb->len < hdr_len) + return -1; + + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; + + i = CCMP_PN_LEN - 1; + while (i >= 0) { + key->tx_pn[i]++; + if (key->tx_pn[i] != 0) + break; + i--; + } + + *pos++ = key->tx_pn[5]; + *pos++ = key->tx_pn[4]; + *pos++ = 0; + *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = key->tx_pn[3]; + *pos++ = key->tx_pn[2]; + *pos++ = key->tx_pn[1]; + *pos++ = key->tx_pn[0]; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + if (!tcb_desc->bHwSec) { + struct aead_request *req; + struct scatterlist sg[2]; + u8 *aad = key->tx_aad; + u8 iv[AES_BLOCK_LEN]; + int aad_len, ret; + int data_len = skb->len - hdr_len - CCMP_HDR_LEN; + + req = aead_request_alloc(key->tfm, GFP_ATOMIC); + if (!req) + return -ENOMEM; + + aad_len = ccmp_init_iv_and_aad(hdr, key->tx_pn, iv, aad); + + skb_put(skb, CCMP_MIC_LEN); + sg_init_table(sg, 2); + sg_set_buf(&sg[0], aad, aad_len); + sg_set_buf(&sg[1], skb->data + hdr_len + CCMP_HDR_LEN, + data_len + CCMP_MIC_LEN); + + aead_request_set_callback(req, 0, NULL, NULL); + aead_request_set_ad(req, aad_len); + aead_request_set_crypt(req, sg, sg, data_len, iv); + + ret = crypto_aead_encrypt(req); + aead_request_free(req); + + return ret; + } + + return 0; +} + +static int rtllib_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct rtllib_ccmp_data *key = priv; + u8 keyidx, *pos; + struct rtllib_hdr_4addr *hdr; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + u8 pn[6]; + + if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { + key->dot11rsna_stats_ccmp_format_errors++; + return -1; + } + + hdr = (struct rtllib_hdr_4addr *)skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + pr_debug("CCMP: received packet without ExtIV flag from %pM\n", + hdr->addr2); + } + key->dot11rsna_stats_ccmp_format_errors++; + return -2; + } + keyidx >>= 6; + if (key->key_idx != keyidx) { + pr_debug("CCMP: RX tkey->key_idx=%d frame keyidx=%d priv=%p\n", + key->key_idx, keyidx, priv); + return -6; + } + if (!key->key_set) { + if (net_ratelimit()) { + pr_debug("CCMP: received packet from %pM with keyid=%d that does not have a configured key\n", + hdr->addr2, keyidx); + } + return -3; + } + + pn[0] = pos[7]; + pn[1] = pos[6]; + pn[2] = pos[5]; + pn[3] = pos[4]; + pn[4] = pos[1]; + pn[5] = pos[0]; + pos += 8; + if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { + key->dot11rsna_stats_ccmp_replays++; + return -4; + } + if (!tcb_desc->bHwSec) { + size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN; + struct aead_request *req; + struct scatterlist sg[2]; + u8 *aad = key->rx_aad; + u8 iv[AES_BLOCK_LEN]; + int aad_len, ret; + + req = aead_request_alloc(key->tfm, GFP_ATOMIC); + if (!req) + return -ENOMEM; + + aad_len = ccmp_init_iv_and_aad(hdr, pn, iv, aad); + + sg_init_table(sg, 2); + sg_set_buf(&sg[0], aad, aad_len); + sg_set_buf(&sg[1], pos, data_len); + + aead_request_set_callback(req, 0, NULL, NULL); + aead_request_set_ad(req, aad_len); + aead_request_set_crypt(req, sg, sg, data_len, iv); + + ret = crypto_aead_decrypt(req); + aead_request_free(req); + + if (ret) { + if (net_ratelimit()) { + pr_debug("CCMP: decrypt failed: STA= %pM\n", + hdr->addr2); + } + key->dot11rsna_stats_ccmp_decrypt_errors++; + return -5; + } + + memcpy(key->rx_pn, pn, CCMP_PN_LEN); + } + /* Remove hdr and MIC */ + memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); + skb_pull(skb, CCMP_HDR_LEN); + skb_trim(skb, skb->len - CCMP_MIC_LEN); + + return keyidx; +} + +static int rtllib_ccmp_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct rtllib_ccmp_data *data = priv; + int keyidx; + struct crypto_aead *tfm = data->tfm; + + keyidx = data->key_idx; + memset(data, 0, sizeof(*data)); + data->key_idx = keyidx; + data->tfm = tfm; + if (len == CCMP_TK_LEN) { + memcpy(data->key, key, CCMP_TK_LEN); + data->key_set = 1; + if (seq) { + data->rx_pn[0] = seq[5]; + data->rx_pn[1] = seq[4]; + data->rx_pn[2] = seq[3]; + data->rx_pn[3] = seq[2]; + data->rx_pn[4] = seq[1]; + data->rx_pn[5] = seq[0]; + } + if (crypto_aead_setauthsize(data->tfm, CCMP_MIC_LEN) || + crypto_aead_setkey(data->tfm, data->key, CCMP_TK_LEN)) + return -1; + } else if (len == 0) { + data->key_set = 0; + } else { + return -1; + } + + return 0; +} + +static int rtllib_ccmp_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct rtllib_ccmp_data *data = priv; + + if (len < CCMP_TK_LEN) + return -1; + + if (!data->key_set) + return 0; + memcpy(key, data->key, CCMP_TK_LEN); + + if (seq) { + seq[0] = data->tx_pn[5]; + seq[1] = data->tx_pn[4]; + seq[2] = data->tx_pn[3]; + seq[3] = data->tx_pn[2]; + seq[4] = data->tx_pn[1]; + seq[5] = data->tx_pn[0]; + } + + return CCMP_TK_LEN; +} + +static void rtllib_ccmp_print_stats(struct seq_file *m, void *priv) +{ + struct rtllib_ccmp_data *ccmp = priv; + + seq_printf(m, + "key[%d] alg=CCMP key_set=%d tx_pn=%pM rx_pn=%pM format_errors=%d replays=%d decrypt_errors=%d\n", + ccmp->key_idx, ccmp->key_set, + ccmp->tx_pn, ccmp->rx_pn, + ccmp->dot11rsna_stats_ccmp_format_errors, + ccmp->dot11rsna_stats_ccmp_replays, + ccmp->dot11rsna_stats_ccmp_decrypt_errors); +} + +static struct lib80211_crypto_ops rtllib_crypt_ccmp = { + .name = "R-CCMP", + .init = rtllib_ccmp_init, + .deinit = rtllib_ccmp_deinit, + .encrypt_mpdu = rtllib_ccmp_encrypt, + .decrypt_mpdu = rtllib_ccmp_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = rtllib_ccmp_set_key, + .get_key = rtllib_ccmp_get_key, + .print_stats = rtllib_ccmp_print_stats, + .extra_mpdu_prefix_len = CCMP_HDR_LEN, + .extra_mpdu_postfix_len = CCMP_MIC_LEN, + .owner = THIS_MODULE, +}; + +static int __init rtllib_crypto_ccmp_init(void) +{ + return lib80211_register_crypto_ops(&rtllib_crypt_ccmp); +} + +static void __exit rtllib_crypto_ccmp_exit(void) +{ + lib80211_unregister_crypto_ops(&rtllib_crypt_ccmp); +} + +module_init(rtllib_crypto_ccmp_init); +module_exit(rtllib_crypto_ccmp_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/rtl8192e/rtllib_crypt_tkip.c b/drivers/staging/rtl8192e/rtllib_crypt_tkip.c new file mode 100644 index 0000000000..9fdfcc017e --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_crypt_tkip.c @@ -0,0 +1,711 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + */ + +#include <crypto/arc4.h> +#include <crypto/hash.h> +#include <linux/fips.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/if_ether.h> +#include <linux/if_arp.h> +#include <linux/string.h> +#include <linux/crc32.h> +#include <linux/etherdevice.h> + +#include "rtllib.h" + +struct rtllib_tkip_data { +#define TKIP_KEY_LEN 32 + u8 key[TKIP_KEY_LEN]; + int key_set; + + u32 tx_iv32; + u16 tx_iv16; + u16 tx_ttak[5]; + int tx_phase1_done; + + u32 rx_iv32; + u16 rx_iv16; + bool initialized; + u16 rx_ttak[5]; + int rx_phase1_done; + u32 rx_iv32_new; + u16 rx_iv16_new; + + u32 dot11RSNAStatsTKIPReplays; + u32 dot11RSNAStatsTKIPICVErrors; + u32 dot11RSNAStatsTKIPLocalMICFailures; + + int key_idx; + struct arc4_ctx rx_ctx_arc4; + struct arc4_ctx tx_ctx_arc4; + struct crypto_shash *rx_tfm_michael; + struct crypto_shash *tx_tfm_michael; + /* scratch buffers for virt_to_page() (crypto API) */ + u8 rx_hdr[16]; + u8 tx_hdr[16]; +}; + +static void *rtllib_tkip_init(int key_idx) +{ + struct rtllib_tkip_data *priv; + + if (fips_enabled) + return NULL; + + priv = kzalloc(sizeof(*priv), GFP_ATOMIC); + if (!priv) + goto fail; + priv->key_idx = key_idx; + + priv->tx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0); + if (IS_ERR(priv->tx_tfm_michael)) { + pr_debug("Could not allocate crypto API michael_mic\n"); + priv->tx_tfm_michael = NULL; + goto fail; + } + + priv->rx_tfm_michael = crypto_alloc_shash("michael_mic", 0, 0); + if (IS_ERR(priv->rx_tfm_michael)) { + pr_debug("Could not allocate crypto API michael_mic\n"); + priv->rx_tfm_michael = NULL; + goto fail; + } + return priv; + +fail: + if (priv) { + crypto_free_shash(priv->tx_tfm_michael); + crypto_free_shash(priv->rx_tfm_michael); + kfree(priv); + } + + return NULL; +} + +static void rtllib_tkip_deinit(void *priv) +{ + struct rtllib_tkip_data *_priv = priv; + + if (_priv) { + crypto_free_shash(_priv->tx_tfm_michael); + crypto_free_shash(_priv->rx_tfm_michael); + } + kfree_sensitive(priv); +} + +static inline u16 RotR1(u16 val) +{ + return (val >> 1) | (val << 15); +} + +static inline u8 Lo8(u16 val) +{ + return val & 0xff; +} + +static inline u8 Hi8(u16 val) +{ + return val >> 8; +} + +static inline u16 Lo16(u32 val) +{ + return val & 0xffff; +} + +static inline u16 Hi16(u32 val) +{ + return val >> 16; +} + +static inline u16 Mk16(u8 hi, u8 lo) +{ + return lo | (hi << 8); +} + +static inline u16 Mk16_le(u16 *v) +{ + return *v; +} + +static const u16 Sbox[256] = { + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + +static inline u16 _S_(u16 v) +{ + u16 t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + +#define PHASE1_LOOP_COUNT 8 + +static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + +static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK, + u16 IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. + */ + u16 *PPK = (u16 *)&WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *)&TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *)&TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *)&TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *)&TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *)&TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *)&TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *)&TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *)&TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV + */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *)&TK[0])) >> 1); + +#ifdef __BIG_ENDIAN + { + int i; + + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + +static int rtllib_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct rtllib_tkip_data *tkey = priv; + int len; + u8 *pos; + struct rtllib_hdr_4addr *hdr; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + int ret = 0; + u8 rc4key[16], *icv; + u32 crc; + + if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + + if (!tcb_desc->bHwSec) { + if (!tkey->tx_phase1_done) { + tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, + tkey->tx_iv32); + tkey->tx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, + tkey->tx_iv16); + } else { + tkey->tx_phase1_done = 1; + } + + len = skb->len - hdr_len; + pos = skb_push(skb, 8); + memmove(pos, pos + 8, hdr_len); + pos += hdr_len; + + if (tcb_desc->bHwSec) { + *pos++ = Hi8(tkey->tx_iv16); + *pos++ = (Hi8(tkey->tx_iv16) | 0x20) & 0x7F; + *pos++ = Lo8(tkey->tx_iv16); + } else { + *pos++ = rc4key[0]; + *pos++ = rc4key[1]; + *pos++ = rc4key[2]; + } + + *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = tkey->tx_iv32 & 0xff; + *pos++ = (tkey->tx_iv32 >> 8) & 0xff; + *pos++ = (tkey->tx_iv32 >> 16) & 0xff; + *pos++ = (tkey->tx_iv32 >> 24) & 0xff; + + if (!tcb_desc->bHwSec) { + icv = skb_put(skb, 4); + crc = ~crc32_le(~0, pos, len); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + arc4_setkey(&tkey->tx_ctx_arc4, rc4key, 16); + arc4_crypt(&tkey->tx_ctx_arc4, pos, pos, len + 4); + } + + tkey->tx_iv16++; + if (tkey->tx_iv16 == 0) { + tkey->tx_phase1_done = 0; + tkey->tx_iv32++; + } + + if (!tcb_desc->bHwSec) + return ret; + return 0; +} + +static int rtllib_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct rtllib_tkip_data *tkey = priv; + u8 keyidx, *pos; + u32 iv32; + u16 iv16; + struct rtllib_hdr_4addr *hdr; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + u8 rc4key[16]; + u8 icv[4]; + u32 crc; + int plen; + + if (skb->len < hdr_len + 8 + 4) + return -1; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + netdev_dbg(skb->dev, + "Received packet without ExtIV flag from %pM\n", + hdr->addr2); + } + return -2; + } + keyidx >>= 6; + if (tkey->key_idx != keyidx) { + netdev_dbg(skb->dev, + "RX tkey->key_idx=%d frame keyidx=%d priv=%p\n", + tkey->key_idx, keyidx, priv); + return -6; + } + if (!tkey->key_set) { + if (net_ratelimit()) { + netdev_dbg(skb->dev, + "Received packet from %pM with keyid=%d that does not have a configured key\n", + hdr->addr2, keyidx); + } + return -3; + } + iv16 = (pos[0] << 8) | pos[2]; + iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); + pos += 8; + + if (!tcb_desc->bHwSec || (skb->cb[0] == 1)) { + if ((iv32 < tkey->rx_iv32 || + (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) && + tkey->initialized) { + if (net_ratelimit()) { + netdev_dbg(skb->dev, + "Replay detected: STA= %pM previous TSC %08x%04x received TSC %08x%04x\n", + hdr->addr2, tkey->rx_iv32, + tkey->rx_iv16, iv32, iv16); + } + tkey->dot11RSNAStatsTKIPReplays++; + return -4; + } + tkey->initialized = true; + + if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { + tkip_mixing_phase1(tkey->rx_ttak, tkey->key, + hdr->addr2, iv32); + tkey->rx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); + + plen = skb->len - hdr_len - 12; + + arc4_setkey(&tkey->rx_ctx_arc4, rc4key, 16); + arc4_crypt(&tkey->rx_ctx_arc4, pos, pos, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + if (memcmp(icv, pos + plen, 4) != 0) { + if (iv32 != tkey->rx_iv32) { + /* Previously cached Phase1 result was already + * lost, so it needs to be recalculated for the + * next packet. + */ + tkey->rx_phase1_done = 0; + } + if (net_ratelimit()) { + netdev_dbg(skb->dev, + "ICV error detected: STA= %pM\n", + hdr->addr2); + } + tkey->dot11RSNAStatsTKIPICVErrors++; + return -5; + } + } + + /* Update real counters only after Michael MIC verification has + * completed + */ + tkey->rx_iv32_new = iv32; + tkey->rx_iv16_new = iv16; + + /* Remove IV and ICV */ + memmove(skb->data + 8, skb->data, hdr_len); + skb_pull(skb, 8); + skb_trim(skb, skb->len - 4); + + return keyidx; +} + +static int michael_mic(struct crypto_shash *tfm_michael, u8 *key, u8 *hdr, + u8 *data, size_t data_len, u8 *mic) +{ + SHASH_DESC_ON_STACK(desc, tfm_michael); + int err; + + desc->tfm = tfm_michael; + + if (crypto_shash_setkey(tfm_michael, key, 8)) + return -1; + + err = crypto_shash_init(desc); + if (err) + goto out; + err = crypto_shash_update(desc, hdr, 16); + if (err) + goto out; + err = crypto_shash_update(desc, data, data_len); + if (err) + goto out; + err = crypto_shash_final(desc, mic); + +out: + shash_desc_zero(desc); + return err; +} + +static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr) +{ + struct rtllib_hdr_4addr *hdr11; + + hdr11 = (struct rtllib_hdr_4addr *)skb->data; + switch (le16_to_cpu(hdr11->frame_ctl) & + (RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS)) { + case RTLLIB_FCTL_TODS: + ether_addr_copy(hdr, hdr11->addr3); /* DA */ + ether_addr_copy(hdr + ETH_ALEN, hdr11->addr2); /* SA */ + break; + case RTLLIB_FCTL_FROMDS: + ether_addr_copy(hdr, hdr11->addr1); /* DA */ + ether_addr_copy(hdr + ETH_ALEN, hdr11->addr3); /* SA */ + break; + case RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS: + ether_addr_copy(hdr, hdr11->addr3); /* DA */ + ether_addr_copy(hdr + ETH_ALEN, hdr11->addr4); /* SA */ + break; + case 0: + ether_addr_copy(hdr, hdr11->addr1); /* DA */ + ether_addr_copy(hdr + ETH_ALEN, hdr11->addr2); /* SA */ + break; + } + + /* priority */ + hdr[12] = 0; + + /* reserved */ + hdr[13] = 0; + hdr[14] = 0; + hdr[15] = 0; +} + +static int rtllib_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct rtllib_tkip_data *tkey = priv; + u8 *pos; + struct rtllib_hdr_4addr *hdr; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + + if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { + netdev_dbg(skb->dev, + "Invalid packet for Michael MIC add (tailroom=%d hdr_len=%d skb->len=%d)\n", + skb_tailroom(skb), hdr_len, skb->len); + return -1; + } + + michael_mic_hdr(skb, tkey->tx_hdr); + + if (RTLLIB_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl))) + tkey->tx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07; + pos = skb_put(skb, 8); + if (michael_mic(tkey->tx_tfm_michael, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + return -1; + + return 0; +} + +static void rtllib_michael_mic_failure(struct net_device *dev, + struct rtllib_hdr_4addr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + struct iw_michaelmicfailure ev; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + memset(&ev, 0, sizeof(ev)); + ev.flags = keyidx & IW_MICFAILURE_KEY_ID; + if (hdr->addr1[0] & 0x01) + ev.flags |= IW_MICFAILURE_GROUP; + else + ev.flags |= IW_MICFAILURE_PAIRWISE; + ev.src_addr.sa_family = ARPHRD_ETHER; + ether_addr_copy(ev.src_addr.sa_data, hdr->addr2); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(ev); + wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *)&ev); +} + +static int rtllib_michael_mic_verify(struct sk_buff *skb, int keyidx, + int hdr_len, void *priv) +{ + struct rtllib_tkip_data *tkey = priv; + u8 mic[8]; + struct rtllib_hdr_4addr *hdr; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + + if (!tkey->key_set) + return -1; + + michael_mic_hdr(skb, tkey->rx_hdr); + if (RTLLIB_QOS_HAS_SEQ(le16_to_cpu(hdr->frame_ctl))) + tkey->rx_hdr[12] = *(skb->data + hdr_len - 2) & 0x07; + + if (michael_mic(tkey->rx_tfm_michael, &tkey->key[24], tkey->rx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) + return -1; + + if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { + struct rtllib_hdr_4addr *hdr; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + netdev_dbg(skb->dev, + "Michael MIC verification failed for MSDU from %pM keyidx=%d\n", + hdr->addr2, keyidx); + netdev_dbg(skb->dev, "%d\n", + memcmp(mic, skb->data + skb->len - 8, 8) != 0); + if (skb->dev) { + pr_info("skb->dev != NULL\n"); + rtllib_michael_mic_failure(skb->dev, hdr, keyidx); + } + tkey->dot11RSNAStatsTKIPLocalMICFailures++; + return -1; + } + + /* Update TSC counters for RX now that the packet verification has + * completed. + */ + tkey->rx_iv32 = tkey->rx_iv32_new; + tkey->rx_iv16 = tkey->rx_iv16_new; + + skb_trim(skb, skb->len - 8); + + return 0; +} + +static int rtllib_tkip_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct rtllib_tkip_data *tkey = priv; + int keyidx; + struct crypto_shash *tfm = tkey->tx_tfm_michael; + struct crypto_shash *tfm3 = tkey->rx_tfm_michael; + + keyidx = tkey->key_idx; + memset(tkey, 0, sizeof(*tkey)); + tkey->key_idx = keyidx; + tkey->tx_tfm_michael = tfm; + tkey->rx_tfm_michael = tfm3; + + if (len == TKIP_KEY_LEN) { + memcpy(tkey->key, key, TKIP_KEY_LEN); + tkey->key_set = 1; + tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ + if (seq) { + tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | + (seq[3] << 8) | seq[2]; + tkey->rx_iv16 = (seq[1] << 8) | seq[0]; + } + } else if (len == 0) { + tkey->key_set = 0; + } else { + return -1; + } + + return 0; +} + +static int rtllib_tkip_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct rtllib_tkip_data *tkey = priv; + + if (len < TKIP_KEY_LEN) + return -1; + + if (!tkey->key_set) + return 0; + memcpy(key, tkey->key, TKIP_KEY_LEN); + + if (seq) { + /* Return the sequence number of the last transmitted frame. */ + u16 iv16 = tkey->tx_iv16; + u32 iv32 = tkey->tx_iv32; + + if (iv16 == 0) + iv32--; + iv16--; + seq[0] = tkey->tx_iv16; + seq[1] = tkey->tx_iv16 >> 8; + seq[2] = tkey->tx_iv32; + seq[3] = tkey->tx_iv32 >> 8; + seq[4] = tkey->tx_iv32 >> 16; + seq[5] = tkey->tx_iv32 >> 24; + } + + return TKIP_KEY_LEN; +} + +static void rtllib_tkip_print_stats(struct seq_file *m, void *priv) +{ + struct rtllib_tkip_data *tkip = priv; + + seq_printf(m, + "key[%d] alg=TKIP key_set=%d tx_pn=%02x%02x%02x%02x%02x%02x rx_pn=%02x%02x%02x%02x%02x%02x replays=%d icv_errors=%d local_mic_failures=%d\n", + tkip->key_idx, tkip->key_set, + (tkip->tx_iv32 >> 24) & 0xff, + (tkip->tx_iv32 >> 16) & 0xff, + (tkip->tx_iv32 >> 8) & 0xff, + tkip->tx_iv32 & 0xff, + (tkip->tx_iv16 >> 8) & 0xff, + tkip->tx_iv16 & 0xff, + (tkip->rx_iv32 >> 24) & 0xff, + (tkip->rx_iv32 >> 16) & 0xff, + (tkip->rx_iv32 >> 8) & 0xff, + tkip->rx_iv32 & 0xff, + (tkip->rx_iv16 >> 8) & 0xff, + tkip->rx_iv16 & 0xff, + tkip->dot11RSNAStatsTKIPReplays, + tkip->dot11RSNAStatsTKIPICVErrors, + tkip->dot11RSNAStatsTKIPLocalMICFailures); +} + +static struct lib80211_crypto_ops rtllib_crypt_tkip = { + .name = "R-TKIP", + .init = rtllib_tkip_init, + .deinit = rtllib_tkip_deinit, + .encrypt_mpdu = rtllib_tkip_encrypt, + .decrypt_mpdu = rtllib_tkip_decrypt, + .encrypt_msdu = rtllib_michael_mic_add, + .decrypt_msdu = rtllib_michael_mic_verify, + .set_key = rtllib_tkip_set_key, + .get_key = rtllib_tkip_get_key, + .print_stats = rtllib_tkip_print_stats, + .extra_mpdu_prefix_len = 4 + 4, /* IV + ExtIV */ + .extra_mpdu_postfix_len = 4, /* ICV */ + .extra_msdu_postfix_len = 8, /* MIC */ + .owner = THIS_MODULE, +}; + +static int __init rtllib_crypto_tkip_init(void) +{ + return lib80211_register_crypto_ops(&rtllib_crypt_tkip); +} + +static void __exit rtllib_crypto_tkip_exit(void) +{ + lib80211_unregister_crypto_ops(&rtllib_crypt_tkip); +} + +module_init(rtllib_crypto_tkip_init); +module_exit(rtllib_crypto_tkip_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/rtl8192e/rtllib_crypt_wep.c b/drivers/staging/rtl8192e/rtllib_crypt_wep.c new file mode 100644 index 0000000000..062285e4d9 --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_crypt_wep.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Host AP crypt: host-based WEP encryption implementation for Host AP driver + * + * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + */ + +#include <crypto/arc4.h> +#include <linux/fips.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/random.h> +#include <linux/skbuff.h> +#include <linux/string.h> +#include "rtllib.h" + +#include <linux/crc32.h> + +struct prism2_wep_data { + u32 iv; +#define WEP_KEY_LEN 13 + u8 key[WEP_KEY_LEN + 1]; + u8 key_len; + u8 key_idx; + struct arc4_ctx rx_ctx_arc4; + struct arc4_ctx tx_ctx_arc4; +}; + +static void *prism2_wep_init(int keyidx) +{ + struct prism2_wep_data *priv; + + if (fips_enabled) + return NULL; + + priv = kzalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + return NULL; + priv->key_idx = keyidx; + + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; +} + +static void prism2_wep_deinit(void *priv) +{ + kfree_sensitive(priv); +} + +/* Perform WEP encryption on given skb that has at least 4 bytes of headroom + * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, + * so the payload length increases with 8 bytes. + * + * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) + */ +static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 klen, len; + u8 key[WEP_KEY_LEN + 3]; + u8 *pos; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + u32 crc; + u8 *icv; + + if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 || + skb->len < hdr_len){ + pr_err("Error!!! headroom=%d tailroom=%d skblen=%d hdr_len=%d\n", + skb_headroom(skb), skb_tailroom(skb), skb->len, hdr_len); + return -1; + } + len = skb->len - hdr_len; + pos = skb_push(skb, 4); + memmove(pos, pos + 4, hdr_len); + pos += hdr_len; + + klen = 3 + wep->key_len; + + wep->iv++; + + /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key + * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) + * can be used to speedup attacks, so avoid using them. + */ + if ((wep->iv & 0xff00) == 0xff00) { + u8 B = (wep->iv >> 16) & 0xff; + + if (B >= 3 && B < klen) + wep->iv += 0x0100; + } + + /* Prepend 24-bit IV to RC4 key and TX frame */ + *pos++ = key[0] = (wep->iv >> 16) & 0xff; + *pos++ = key[1] = (wep->iv >> 8) & 0xff; + *pos++ = key[2] = wep->iv & 0xff; + *pos++ = wep->key_idx << 6; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + if (!tcb_desc->bHwSec) { + /* Append little-endian CRC32 and encrypt it to produce ICV */ + crc = ~crc32_le(~0, pos, len); + icv = skb_put(skb, 4); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + arc4_setkey(&wep->tx_ctx_arc4, key, klen); + arc4_crypt(&wep->tx_ctx_arc4, pos, pos, len + 4); + } + + return 0; +} + +/* Perform WEP decryption on given struct buffer. Buffer includes whole WEP + * part of the frame: IV (4 bytes), encrypted payload (including SNAP header), + * ICV (4 bytes). len includes both IV and ICV. + * + * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on + * failure. If frame is OK, IV and ICV will be removed. + */ +static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 klen, plen; + u8 key[WEP_KEY_LEN + 3]; + u8 keyidx, *pos; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + + MAX_DEV_ADDR_SIZE); + u32 crc; + u8 icv[4]; + + if (skb->len < hdr_len + 8) + return -1; + + pos = skb->data + hdr_len; + key[0] = *pos++; + key[1] = *pos++; + key[2] = *pos++; + keyidx = *pos++ >> 6; + if (keyidx != wep->key_idx) + return -1; + + klen = 3 + wep->key_len; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + plen = skb->len - hdr_len - 8; + + if (!tcb_desc->bHwSec) { + arc4_setkey(&wep->rx_ctx_arc4, key, klen); + arc4_crypt(&wep->rx_ctx_arc4, pos, pos, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + /* ICV mismatch - drop frame */ + return -2; + } + } + /* Remove IV and ICV */ + memmove(skb->data + 4, skb->data, hdr_len); + skb_pull(skb, 4); + skb_trim(skb, skb->len - 4); + + return 0; +} + +static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < 0 || len > WEP_KEY_LEN) + return -1; + + memcpy(wep->key, key, len); + wep->key_len = len; + + return 0; +} + +static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < wep->key_len) + return -1; + + memcpy(key, wep->key, wep->key_len); + + return wep->key_len; +} + +static void prism2_wep_print_stats(struct seq_file *m, void *priv) +{ + struct prism2_wep_data *wep = priv; + + seq_printf(m, "key[%d] alg=WEP len=%d\n", wep->key_idx, wep->key_len); +} + +static struct lib80211_crypto_ops rtllib_crypt_wep = { + .name = "R-WEP", + .init = prism2_wep_init, + .deinit = prism2_wep_deinit, + .encrypt_mpdu = prism2_wep_encrypt, + .decrypt_mpdu = prism2_wep_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = prism2_wep_set_key, + .get_key = prism2_wep_get_key, + .print_stats = prism2_wep_print_stats, + .extra_mpdu_prefix_len = 4, /* IV */ + .extra_mpdu_postfix_len = 4, /* ICV */ + .owner = THIS_MODULE, +}; + +static int __init rtllib_crypto_wep_init(void) +{ + return lib80211_register_crypto_ops(&rtllib_crypt_wep); +} + +static void __exit rtllib_crypto_wep_exit(void) +{ + lib80211_unregister_crypto_ops(&rtllib_crypt_wep); +} + +module_init(rtllib_crypto_wep_init); +module_exit(rtllib_crypto_wep_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/rtl8192e/rtllib_debug.h b/drivers/staging/rtl8192e/rtllib_debug.h new file mode 100644 index 0000000000..f6b23defe2 --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_debug.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. + * + * Contact Information: wlanfae <wlanfae@realtek.com> + */ +#ifndef _RTL_DEBUG_H +#define _RTL_DEBUG_H + +#include <linux/bits.h> + +/* Allow files to override DRV_NAME */ +#ifndef DRV_NAME +#define DRV_NAME "rtllib_92e" +#endif + +extern u32 rt_global_debug_component; + +/* These are the defines for rt_global_debug_component */ +enum RTL_DEBUG { + COMP_TRACE = BIT(0), + COMP_DBG = BIT(1), + COMP_INIT = BIT(2), + COMP_RECV = BIT(3), + COMP_POWER = BIT(6), + COMP_SWBW = BIT(8), + COMP_SEC = BIT(9), + COMP_LPS = BIT(10), + COMP_QOS = BIT(11), + COMP_RATE = BIT(12), + COMP_RXDESC = BIT(13), + COMP_PHY = BIT(14), + COMP_DIG = BIT(15), + COMP_TXAGC = BIT(16), + COMP_HALDM = BIT(17), + COMP_POWER_TRACKING = BIT(18), + COMP_CH = BIT(19), + COMP_RF = BIT(20), + COMP_FIRMWARE = BIT(21), + COMP_RESET = BIT(23), + COMP_CMDPKT = BIT(24), + COMP_SCAN = BIT(25), + COMP_PS = BIT(26), + COMP_DOWN = BIT(27), + COMP_INTR = BIT(28), + COMP_ERR = BIT(31) +}; + +#endif diff --git a/drivers/staging/rtl8192e/rtllib_module.c b/drivers/staging/rtl8192e/rtllib_module.c new file mode 100644 index 0000000000..2416e0c602 --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_module.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2004 Intel Corporation. All rights reserved. + * + * Portions of this file are based on the WEP enablement code provided by the + * Host AP project hostap-drivers v0.1.3 + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * <jkmaline@cc.hut.fi> + * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + */ + +#include <linux/compiler.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/in6.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/tcp.h> +#include <linux/types.h> +#include <linux/wireless.h> +#include <linux/etherdevice.h> +#include <linux/uaccess.h> +#include <net/arp.h> +#include "rtllib.h" + +u32 rt_global_debug_component = COMP_ERR; +EXPORT_SYMBOL(rt_global_debug_component); + +static inline int rtllib_networks_allocate(struct rtllib_device *ieee) +{ + if (ieee->networks) + return 0; + + ieee->networks = kcalloc(MAX_NETWORK_COUNT, + sizeof(struct rtllib_network), GFP_KERNEL); + if (!ieee->networks) + return -ENOMEM; + + return 0; +} + +static inline void rtllib_networks_free(struct rtllib_device *ieee) +{ + if (!ieee->networks) + return; + kfree(ieee->networks); + ieee->networks = NULL; +} + +static inline void rtllib_networks_initialize(struct rtllib_device *ieee) +{ + int i; + + INIT_LIST_HEAD(&ieee->network_free_list); + INIT_LIST_HEAD(&ieee->network_list); + for (i = 0; i < MAX_NETWORK_COUNT; i++) + list_add_tail(&ieee->networks[i].list, + &ieee->network_free_list); +} + +struct net_device *alloc_rtllib(int sizeof_priv) +{ + struct rtllib_device *ieee = NULL; + struct net_device *dev; + int i, err; + + pr_debug("rtllib: Initializing...\n"); + + dev = alloc_etherdev(sizeof(struct rtllib_device) + sizeof_priv); + if (!dev) { + pr_err("Unable to allocate net_device.\n"); + return NULL; + } + ieee = (struct rtllib_device *)netdev_priv_rsl(dev); + ieee->dev = dev; + + err = rtllib_networks_allocate(ieee); + if (err) { + pr_err("Unable to allocate beacon storage: %d\n", err); + goto free_netdev; + } + rtllib_networks_initialize(ieee); + + /* Default fragmentation threshold is maximum payload size */ + ieee->fts = DEFAULT_FTS; + ieee->scan_age = DEFAULT_MAX_SCAN_AGE; + ieee->open_wep = 1; + + ieee->ieee802_1x = 1; /* Default to supporting 802.1x */ + + ieee->rtllib_ap_sec_type = rtllib_ap_sec_type; + + spin_lock_init(&ieee->lock); + spin_lock_init(&ieee->wpax_suitlist_lock); + spin_lock_init(&ieee->reorder_spinlock); + atomic_set(&ieee->atm_swbw, 0); + + /* SAM FIXME */ + lib80211_crypt_info_init(&ieee->crypt_info, "RTLLIB", &ieee->lock); + + ieee->wpa_enabled = 0; + ieee->tkip_countermeasures = 0; + ieee->drop_unencrypted = 0; + ieee->privacy_invoked = 0; + ieee->ieee802_1x = 1; + ieee->raw_tx = 0; + ieee->hwsec_active = 0; + + memset(ieee->swcamtable, 0, sizeof(struct sw_cam_table) * 32); + err = rtllib_softmac_init(ieee); + if (err) + goto free_crypt_info; + + ieee->ht_info = kzalloc(sizeof(struct rt_hi_throughput), GFP_KERNEL); + if (!ieee->ht_info) + goto free_softmac; + + HTUpdateDefaultSetting(ieee); + HTInitializeHTInfo(ieee); + TSInitialize(ieee); + for (i = 0; i < IEEE_IBSS_MAC_HASH_SIZE; i++) + INIT_LIST_HEAD(&ieee->ibss_mac_hash[i]); + + for (i = 0; i < 17; i++) { + ieee->last_rxseq_num[i] = -1; + ieee->last_rxfrag_num[i] = -1; + ieee->last_packet_time[i] = 0; + } + + return dev; + +free_softmac: + rtllib_softmac_free(ieee); +free_crypt_info: + lib80211_crypt_info_free(&ieee->crypt_info); + rtllib_networks_free(ieee); +free_netdev: + free_netdev(dev); + + return NULL; +} +EXPORT_SYMBOL(alloc_rtllib); + +void free_rtllib(struct net_device *dev) +{ + struct rtllib_device *ieee = (struct rtllib_device *) + netdev_priv_rsl(dev); + + kfree(ieee->ht_info); + rtllib_softmac_free(ieee); + + lib80211_crypt_info_free(&ieee->crypt_info); + + rtllib_networks_free(ieee); + free_netdev(dev); +} +EXPORT_SYMBOL(free_rtllib); + +static int __init rtllib_init(void) +{ + return 0; +} + +static void __exit rtllib_exit(void) +{ +} + +module_init(rtllib_init); +module_exit(rtllib_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/rtl8192e/rtllib_rx.c b/drivers/staging/rtl8192e/rtllib_rx.c new file mode 100644 index 0000000000..40e7bbb17c --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_rx.c @@ -0,0 +1,2683 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Original code based Host AP (software wireless LAN access point) driver + * for Intersil Prism2/2.5/3 - hostap.o module, common routines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * <jkmaline@cc.hut.fi> + * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2004, Intel Corporation + * + * Few modifications for Realtek's Wi-Fi drivers by + * Andrea Merello <andrea.merello@gmail.com> + * + * A special thanks goes to Realtek for their support ! + */ +#include <linux/compiler.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/in6.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/tcp.h> +#include <linux/types.h> +#include <linux/wireless.h> +#include <linux/etherdevice.h> +#include <linux/uaccess.h> +#include <linux/ctype.h> + +#include "rtllib.h" +#include "dot11d.h" + +static void rtllib_rx_mgt(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *stats); + +static inline void rtllib_monitor_rx(struct rtllib_device *ieee, + struct sk_buff *skb, + struct rtllib_rx_stats *rx_status, + size_t hdr_length) +{ + skb->dev = ieee->dev; + skb_reset_mac_header(skb); + skb_pull(skb, hdr_length); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = htons(ETH_P_80211_RAW); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + +/* Called only as a tasklet (software IRQ) */ +static struct rtllib_frag_entry * +rtllib_frag_cache_find(struct rtllib_device *ieee, unsigned int seq, + unsigned int frag, u8 tid, u8 *src, u8 *dst) +{ + struct rtllib_frag_entry *entry; + int i; + + for (i = 0; i < RTLLIB_FRAG_CACHE_LEN; i++) { + entry = &ieee->frag_cache[tid][i]; + if (entry->skb != NULL && + time_after(jiffies, entry->first_frag_time + 2 * HZ)) { + netdev_dbg(ieee->dev, + "expiring fragment cache entry seq=%u last_frag=%u\n", + entry->seq, entry->last_frag); + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; + } + + if (entry->skb != NULL && entry->seq == seq && + (entry->last_frag + 1 == frag || frag == -1) && + memcmp(entry->src_addr, src, ETH_ALEN) == 0 && + memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) + return entry; + } + + return NULL; +} + +/* Called only as a tasklet (software IRQ) */ +static struct sk_buff * +rtllib_frag_cache_get(struct rtllib_device *ieee, + struct rtllib_hdr_4addr *hdr) +{ + struct sk_buff *skb = NULL; + u16 fc = le16_to_cpu(hdr->frame_ctl); + u16 sc = le16_to_cpu(hdr->seq_ctl); + unsigned int frag = WLAN_GET_SEQ_FRAG(sc); + unsigned int seq = WLAN_GET_SEQ_SEQ(sc); + struct rtllib_frag_entry *entry; + struct rtllib_hdr_3addrqos *hdr_3addrqos; + struct rtllib_hdr_4addrqos *hdr_4addrqos; + u8 tid; + + if (((fc & RTLLIB_FCTL_DSTODS) == RTLLIB_FCTL_DSTODS) && + RTLLIB_QOS_HAS_SEQ(fc)) { + hdr_4addrqos = (struct rtllib_hdr_4addrqos *)hdr; + tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & RTLLIB_QCTL_TID; + tid = UP2AC(tid); + tid++; + } else if (RTLLIB_QOS_HAS_SEQ(fc)) { + hdr_3addrqos = (struct rtllib_hdr_3addrqos *)hdr; + tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & RTLLIB_QCTL_TID; + tid = UP2AC(tid); + tid++; + } else { + tid = 0; + } + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(ieee->dev->mtu + + sizeof(struct rtllib_hdr_4addr) + + 8 /* LLC */ + + 2 /* alignment */ + + 8 /* WEP */ + + ETH_ALEN /* WDS */ + + /* QOS Control */ + (RTLLIB_QOS_HAS_SEQ(fc) ? 2 : 0)); + if (!skb) + return NULL; + + entry = &ieee->frag_cache[tid][ieee->frag_next_idx[tid]]; + ieee->frag_next_idx[tid]++; + if (ieee->frag_next_idx[tid] >= RTLLIB_FRAG_CACHE_LEN) + ieee->frag_next_idx[tid] = 0; + + if (entry->skb != NULL) + dev_kfree_skb_any(entry->skb); + + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->last_frag = frag; + entry->skb = skb; + ether_addr_copy(entry->src_addr, hdr->addr2); + ether_addr_copy(entry->dst_addr, hdr->addr1); + } else { + /* received a fragment of a frame for which the head fragment + * should have already been received + */ + entry = rtllib_frag_cache_find(ieee, seq, frag, tid, hdr->addr2, + hdr->addr1); + if (entry != NULL) { + entry->last_frag = frag; + skb = entry->skb; + } + } + + return skb; +} + +/* Called only as a tasklet (software IRQ) */ +static int rtllib_frag_cache_invalidate(struct rtllib_device *ieee, + struct rtllib_hdr_4addr *hdr) +{ + u16 fc = le16_to_cpu(hdr->frame_ctl); + u16 sc = le16_to_cpu(hdr->seq_ctl); + unsigned int seq = WLAN_GET_SEQ_SEQ(sc); + struct rtllib_frag_entry *entry; + struct rtllib_hdr_3addrqos *hdr_3addrqos; + struct rtllib_hdr_4addrqos *hdr_4addrqos; + u8 tid; + + if (((fc & RTLLIB_FCTL_DSTODS) == RTLLIB_FCTL_DSTODS) && + RTLLIB_QOS_HAS_SEQ(fc)) { + hdr_4addrqos = (struct rtllib_hdr_4addrqos *)hdr; + tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & RTLLIB_QCTL_TID; + tid = UP2AC(tid); + tid++; + } else if (RTLLIB_QOS_HAS_SEQ(fc)) { + hdr_3addrqos = (struct rtllib_hdr_3addrqos *)hdr; + tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & RTLLIB_QCTL_TID; + tid = UP2AC(tid); + tid++; + } else { + tid = 0; + } + + entry = rtllib_frag_cache_find(ieee, seq, -1, tid, hdr->addr2, + hdr->addr1); + + if (entry == NULL) { + netdev_dbg(ieee->dev, + "Couldn't invalidate fragment cache entry (seq=%u)\n", + seq); + return -1; + } + + entry->skb = NULL; + return 0; +} + +/* rtllib_rx_frame_mgtmt + * + * Responsible for handling management control frames + * + * Called by rtllib_rx + */ +static inline int +rtllib_rx_frame_mgmt(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats, u16 type, + u16 stype) +{ + /* On the struct stats definition there is written that + * this is not mandatory.... but seems that the probe + * response parser uses it + */ + struct rtllib_hdr_3addr *hdr = (struct rtllib_hdr_3addr *)skb->data; + + rx_stats->len = skb->len; + rtllib_rx_mgt(ieee, skb, rx_stats); + if ((memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN))) { + dev_kfree_skb_any(skb); + return 0; + } + rtllib_rx_frame_softmac(ieee, skb, rx_stats, type, stype); + + dev_kfree_skb_any(skb); + + return 0; +} + +/* No encapsulation header if EtherType < 0x600 (=length) */ + +/* Called by rtllib_rx_frame_decrypt */ +static int rtllib_is_eapol_frame(struct rtllib_device *ieee, + struct sk_buff *skb, size_t hdrlen) +{ + struct net_device *dev = ieee->dev; + u16 fc, ethertype; + struct rtllib_hdr_4addr *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + + /* check that the frame is unicast frame to us */ + if ((fc & (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS)) == + RTLLIB_FCTL_TODS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 && + memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (RTLLIB_FCTL_TODS | RTLLIB_FCTL_FROMDS)) == + RTLLIB_FCTL_FROMDS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + /* FromDS frame with own addr as DA */ + } else { + return 0; + } + + if (skb->len < 24 + 8) + return 0; + + /* check for port access entity Ethernet type */ + pos = skb->data + hdrlen; + ethertype = (pos[6] << 8) | pos[7]; + if (ethertype == ETH_P_PAE) + return 1; + + return 0; +} + +/* Called only as a tasklet (software IRQ), by rtllib_rx */ +static inline int +rtllib_rx_frame_decrypt(struct rtllib_device *ieee, struct sk_buff *skb, + struct lib80211_crypt_data *crypt) +{ + struct rtllib_hdr_4addr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + if (ieee->hwsec_active) { + struct cb_desc *tcb_desc = (struct cb_desc *) + (skb->cb + MAX_DEV_ADDR_SIZE); + + tcb_desc->bHwSec = 1; + + if (ieee->need_sw_enc) + tcb_desc->bHwSec = 0; + } + + hdr = (struct rtllib_hdr_4addr *)skb->data; + hdrlen = rtllib_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + netdev_dbg(ieee->dev, "decryption failed (SA= %pM) res=%d\n", + hdr->addr2, res); + if (res == -2) + netdev_dbg(ieee->dev, + "Decryption failed ICV mismatch (key %d)\n", + skb->data[hdrlen + 3] >> 6); + return -1; + } + + return res; +} + +/* Called only as a tasklet (software IRQ), by rtllib_rx */ +static inline int +rtllib_rx_frame_decrypt_msdu(struct rtllib_device *ieee, struct sk_buff *skb, + int keyidx, struct lib80211_crypt_data *crypt) +{ + struct rtllib_hdr_4addr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + if (ieee->hwsec_active) { + struct cb_desc *tcb_desc = (struct cb_desc *) + (skb->cb + MAX_DEV_ADDR_SIZE); + + tcb_desc->bHwSec = 1; + + if (ieee->need_sw_enc) + tcb_desc->bHwSec = 0; + } + + hdr = (struct rtllib_hdr_4addr *)skb->data; + hdrlen = rtllib_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + netdev_dbg(ieee->dev, + "MSDU decryption/MIC verification failed (SA= %pM keyidx=%d)\n", + hdr->addr2, keyidx); + return -1; + } + + return 0; +} + +/* this function is stolen from ipw2200 driver*/ +#define IEEE_PACKET_RETRY_TIME (5 * HZ) +static int is_duplicate_packet(struct rtllib_device *ieee, + struct rtllib_hdr_4addr *header) +{ + u16 fc = le16_to_cpu(header->frame_ctl); + u16 sc = le16_to_cpu(header->seq_ctl); + u16 seq = WLAN_GET_SEQ_SEQ(sc); + u16 frag = WLAN_GET_SEQ_FRAG(sc); + u16 *last_seq, *last_frag; + unsigned long *last_time; + struct rtllib_hdr_3addrqos *hdr_3addrqos; + struct rtllib_hdr_4addrqos *hdr_4addrqos; + u8 tid; + + if (((fc & RTLLIB_FCTL_DSTODS) == RTLLIB_FCTL_DSTODS) && + RTLLIB_QOS_HAS_SEQ(fc)) { + hdr_4addrqos = (struct rtllib_hdr_4addrqos *)header; + tid = le16_to_cpu(hdr_4addrqos->qos_ctl) & RTLLIB_QCTL_TID; + tid = UP2AC(tid); + tid++; + } else if (RTLLIB_QOS_HAS_SEQ(fc)) { + hdr_3addrqos = (struct rtllib_hdr_3addrqos *)header; + tid = le16_to_cpu(hdr_3addrqos->qos_ctl) & RTLLIB_QCTL_TID; + tid = UP2AC(tid); + tid++; + } else { + tid = 0; + } + + switch (ieee->iw_mode) { + case IW_MODE_ADHOC: + { + struct list_head *p; + struct ieee_ibss_seq *entry = NULL; + u8 *mac = header->addr2; + int index = mac[5] % IEEE_IBSS_MAC_HASH_SIZE; + + list_for_each(p, &ieee->ibss_mac_hash[index]) { + entry = list_entry(p, struct ieee_ibss_seq, list); + if (!memcmp(entry->mac, mac, ETH_ALEN)) + break; + } + if (p == &ieee->ibss_mac_hash[index]) { + entry = kmalloc(sizeof(struct ieee_ibss_seq), + GFP_ATOMIC); + if (!entry) + return 0; + + ether_addr_copy(entry->mac, mac); + entry->seq_num[tid] = seq; + entry->frag_num[tid] = frag; + entry->packet_time[tid] = jiffies; + list_add(&entry->list, &ieee->ibss_mac_hash[index]); + return 0; + } + last_seq = &entry->seq_num[tid]; + last_frag = &entry->frag_num[tid]; + last_time = &entry->packet_time[tid]; + break; + } + + case IW_MODE_INFRA: + last_seq = &ieee->last_rxseq_num[tid]; + last_frag = &ieee->last_rxfrag_num[tid]; + last_time = &ieee->last_packet_time[tid]; + break; + default: + return 0; + } + + if ((*last_seq == seq) && + time_after(*last_time + IEEE_PACKET_RETRY_TIME, jiffies)) { + if (*last_frag == frag) + goto drop; + if (*last_frag + 1 != frag) + /* out-of-order fragment */ + goto drop; + } else { + *last_seq = seq; + } + + *last_frag = frag; + *last_time = jiffies; + return 0; + +drop: + + return 1; +} + +static bool AddReorderEntry(struct rx_ts_record *pTS, + struct rx_reorder_entry *pReorderEntry) +{ + struct list_head *pList = &pTS->rx_pending_pkt_list; + + while (pList->next != &pTS->rx_pending_pkt_list) { + if (SN_LESS(pReorderEntry->SeqNum, ((struct rx_reorder_entry *) + list_entry(pList->next, struct rx_reorder_entry, + List))->SeqNum)) + pList = pList->next; + else if (SN_EQUAL(pReorderEntry->SeqNum, + ((struct rx_reorder_entry *)list_entry(pList->next, + struct rx_reorder_entry, List))->SeqNum)) + return false; + else + break; + } + pReorderEntry->List.next = pList->next; + pReorderEntry->List.next->prev = &pReorderEntry->List; + pReorderEntry->List.prev = pList; + pList->next = &pReorderEntry->List; + + return true; +} + +void rtllib_indicate_packets(struct rtllib_device *ieee, + struct rtllib_rxb **prxbIndicateArray, u8 index) +{ + struct net_device_stats *stats = &ieee->stats; + u8 i = 0, j = 0; + u16 ethertype; + + for (j = 0; j < index; j++) { + struct rtllib_rxb *prxb = prxbIndicateArray[j]; + + for (i = 0; i < prxb->nr_subframes; i++) { + struct sk_buff *sub_skb = prxb->subframes[i]; + + /* convert hdr + possible LLC headers into Ethernet header */ + ethertype = (sub_skb->data[6] << 8) | sub_skb->data[7]; + if (sub_skb->len >= 8 && + ((memcmp(sub_skb->data, rfc1042_header, + SNAP_SIZE) == 0 && + ethertype != ETH_P_AARP && + ethertype != ETH_P_IPX) || + memcmp(sub_skb->data, bridge_tunnel_header, + SNAP_SIZE) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation + * and replace EtherType + */ + skb_pull(sub_skb, SNAP_SIZE); + memcpy(skb_push(sub_skb, ETH_ALEN), prxb->src, ETH_ALEN); + memcpy(skb_push(sub_skb, ETH_ALEN), prxb->dst, ETH_ALEN); + } else { + u16 len; + /* Leave Ethernet header part of hdr and full payload */ + len = sub_skb->len; + memcpy(skb_push(sub_skb, 2), &len, 2); + memcpy(skb_push(sub_skb, ETH_ALEN), prxb->src, ETH_ALEN); + memcpy(skb_push(sub_skb, ETH_ALEN), prxb->dst, ETH_ALEN); + } + + /* Indicate the packets to upper layer */ + if (sub_skb) { + stats->rx_packets++; + stats->rx_bytes += sub_skb->len; + + memset(sub_skb->cb, 0, sizeof(sub_skb->cb)); + sub_skb->protocol = eth_type_trans(sub_skb, + ieee->dev); + sub_skb->dev = ieee->dev; + sub_skb->dev->stats.rx_packets++; + sub_skb->dev->stats.rx_bytes += sub_skb->len; + /* 802.11 crc not sufficient */ + sub_skb->ip_summed = CHECKSUM_NONE; + ieee->last_rx_ps_time = jiffies; + netif_rx(sub_skb); + } + } + kfree(prxb); + prxb = NULL; + } +} + +void rtllib_FlushRxTsPendingPkts(struct rtllib_device *ieee, + struct rx_ts_record *pTS) +{ + struct rx_reorder_entry *pRxReorderEntry; + u8 RfdCnt = 0; + + del_timer_sync(&pTS->rx_pkt_pending_timer); + while (!list_empty(&pTS->rx_pending_pkt_list)) { + if (RfdCnt >= REORDER_WIN_SIZE) { + netdev_info(ieee->dev, + "-------------->%s() error! RfdCnt >= REORDER_WIN_SIZE\n", + __func__); + break; + } + + pRxReorderEntry = (struct rx_reorder_entry *) + list_entry(pTS->rx_pending_pkt_list.prev, + struct rx_reorder_entry, List); + netdev_dbg(ieee->dev, "%s(): Indicate SeqNum %d!\n", __func__, + pRxReorderEntry->SeqNum); + list_del_init(&pRxReorderEntry->List); + + ieee->RfdArray[RfdCnt] = pRxReorderEntry->prxb; + + RfdCnt = RfdCnt + 1; + list_add_tail(&pRxReorderEntry->List, + &ieee->RxReorder_Unused_List); + } + rtllib_indicate_packets(ieee, ieee->RfdArray, RfdCnt); + + pTS->rx_indicate_seq = 0xffff; +} + +static void RxReorderIndicatePacket(struct rtllib_device *ieee, + struct rtllib_rxb *prxb, + struct rx_ts_record *pTS, u16 SeqNum) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + struct rx_reorder_entry *pReorderEntry = NULL; + u8 WinSize = ht_info->rx_reorder_win_size; + u16 WinEnd = 0; + u8 index = 0; + bool bMatchWinStart = false, bPktInBuf = false; + unsigned long flags; + + netdev_dbg(ieee->dev, + "%s(): Seq is %d, pTS->rx_indicate_seq is %d, WinSize is %d\n", + __func__, SeqNum, pTS->rx_indicate_seq, WinSize); + + spin_lock_irqsave(&(ieee->reorder_spinlock), flags); + + WinEnd = (pTS->rx_indicate_seq + WinSize - 1) % 4096; + /* Rx Reorder initialize condition.*/ + if (pTS->rx_indicate_seq == 0xffff) + pTS->rx_indicate_seq = SeqNum; + + /* Drop out the packet which SeqNum is smaller than WinStart */ + if (SN_LESS(SeqNum, pTS->rx_indicate_seq)) { + netdev_dbg(ieee->dev, + "Packet Drop! IndicateSeq: %d, NewSeq: %d\n", + pTS->rx_indicate_seq, SeqNum); + ht_info->rx_reorder_drop_counter++; + { + int i; + + for (i = 0; i < prxb->nr_subframes; i++) + dev_kfree_skb(prxb->subframes[i]); + kfree(prxb); + prxb = NULL; + } + spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags); + return; + } + + /* Sliding window manipulation. Conditions includes: + * 1. Incoming SeqNum is equal to WinStart =>Window shift 1 + * 2. Incoming SeqNum is larger than the WinEnd => Window shift N + */ + if (SN_EQUAL(SeqNum, pTS->rx_indicate_seq)) { + pTS->rx_indicate_seq = (pTS->rx_indicate_seq + 1) % 4096; + bMatchWinStart = true; + } else if (SN_LESS(WinEnd, SeqNum)) { + if (SeqNum >= (WinSize - 1)) + pTS->rx_indicate_seq = SeqNum + 1 - WinSize; + else + pTS->rx_indicate_seq = 4095 - + (WinSize - (SeqNum + 1)) + 1; + netdev_dbg(ieee->dev, + "Window Shift! IndicateSeq: %d, NewSeq: %d\n", + pTS->rx_indicate_seq, SeqNum); + } + + /* Indication process. + * After Packet dropping and Sliding Window shifting as above, we can + * now just indicate the packets with the SeqNum smaller than latest + * WinStart and struct buffer other packets. + * + * For Rx Reorder condition: + * 1. All packets with SeqNum smaller than WinStart => Indicate + * 2. All packets with SeqNum larger than or equal to + * WinStart => Buffer it. + */ + if (bMatchWinStart) { + /* Current packet is going to be indicated.*/ + netdev_dbg(ieee->dev, + "Packets indication! IndicateSeq: %d, NewSeq: %d\n", + pTS->rx_indicate_seq, SeqNum); + ieee->prxbIndicateArray[0] = prxb; + index = 1; + } else { + /* Current packet is going to be inserted into pending list.*/ + if (!list_empty(&ieee->RxReorder_Unused_List)) { + pReorderEntry = (struct rx_reorder_entry *) + list_entry(ieee->RxReorder_Unused_List.next, + struct rx_reorder_entry, List); + list_del_init(&pReorderEntry->List); + + /* Make a reorder entry and insert + * into a the packet list. + */ + pReorderEntry->SeqNum = SeqNum; + pReorderEntry->prxb = prxb; + + if (!AddReorderEntry(pTS, pReorderEntry)) { + int i; + + netdev_dbg(ieee->dev, + "%s(): Duplicate packet is dropped. IndicateSeq: %d, NewSeq: %d\n", + __func__, pTS->rx_indicate_seq, + SeqNum); + list_add_tail(&pReorderEntry->List, + &ieee->RxReorder_Unused_List); + + for (i = 0; i < prxb->nr_subframes; i++) + dev_kfree_skb(prxb->subframes[i]); + kfree(prxb); + prxb = NULL; + } else { + netdev_dbg(ieee->dev, + "Pkt insert into struct buffer. IndicateSeq: %d, NewSeq: %d\n", + pTS->rx_indicate_seq, SeqNum); + } + } else { + /* Packets are dropped if there are not enough reorder + * entries. This part should be modified!! We can just + * indicate all the packets in struct buffer and get + * reorder entries. + */ + netdev_err(ieee->dev, + "%s(): There is no reorder entry! Packet is dropped!\n", + __func__); + { + int i; + + for (i = 0; i < prxb->nr_subframes; i++) + dev_kfree_skb(prxb->subframes[i]); + kfree(prxb); + prxb = NULL; + } + } + } + + /* Check if there is any packet need indicate.*/ + while (!list_empty(&pTS->rx_pending_pkt_list)) { + netdev_dbg(ieee->dev, "%s(): start RREORDER indicate\n", + __func__); + + pReorderEntry = (struct rx_reorder_entry *) + list_entry(pTS->rx_pending_pkt_list.prev, + struct rx_reorder_entry, + List); + if (SN_LESS(pReorderEntry->SeqNum, pTS->rx_indicate_seq) || + SN_EQUAL(pReorderEntry->SeqNum, pTS->rx_indicate_seq)) { + /* This protect struct buffer from overflow. */ + if (index >= REORDER_WIN_SIZE) { + netdev_err(ieee->dev, + "%s(): Buffer overflow!\n", + __func__); + bPktInBuf = true; + break; + } + + list_del_init(&pReorderEntry->List); + + if (SN_EQUAL(pReorderEntry->SeqNum, pTS->rx_indicate_seq)) + pTS->rx_indicate_seq = (pTS->rx_indicate_seq + 1) % + 4096; + + ieee->prxbIndicateArray[index] = pReorderEntry->prxb; + netdev_dbg(ieee->dev, "%s(): Indicate SeqNum %d!\n", + __func__, pReorderEntry->SeqNum); + index++; + + list_add_tail(&pReorderEntry->List, + &ieee->RxReorder_Unused_List); + } else { + bPktInBuf = true; + break; + } + } + + /* Handling pending timer. Set this timer to prevent from long time + * Rx buffering. + */ + if (index > 0) { + if (timer_pending(&pTS->rx_pkt_pending_timer)) + del_timer_sync(&pTS->rx_pkt_pending_timer); + pTS->rx_timeout_indicate_seq = 0xffff; + + if (index > REORDER_WIN_SIZE) { + netdev_err(ieee->dev, + "%s(): Rx Reorder struct buffer full!\n", + __func__); + spin_unlock_irqrestore(&(ieee->reorder_spinlock), + flags); + return; + } + rtllib_indicate_packets(ieee, ieee->prxbIndicateArray, index); + bPktInBuf = false; + } + + if (bPktInBuf && pTS->rx_timeout_indicate_seq == 0xffff) { + netdev_dbg(ieee->dev, "%s(): SET rx timeout timer\n", __func__); + pTS->rx_timeout_indicate_seq = pTS->rx_indicate_seq; + mod_timer(&pTS->rx_pkt_pending_timer, jiffies + + msecs_to_jiffies(ht_info->rx_reorder_pending_time)); + } + spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags); +} + +static u8 parse_subframe(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats, + struct rtllib_rxb *rxb, u8 *src, u8 *dst) +{ + struct rtllib_hdr_3addr *hdr = (struct rtllib_hdr_3addr *)skb->data; + u16 fc = le16_to_cpu(hdr->frame_ctl); + + u16 LLCOffset = sizeof(struct rtllib_hdr_3addr); + u16 ChkLength; + bool bIsAggregateFrame = false; + u16 nSubframe_Length; + u8 nPadding_Length = 0; + u16 SeqNum = 0; + struct sk_buff *sub_skb; + /* just for debug purpose */ + SeqNum = WLAN_GET_SEQ_SEQ(le16_to_cpu(hdr->seq_ctl)); + if ((RTLLIB_QOS_HAS_SEQ(fc)) && + (((union frameqos *)(skb->data + RTLLIB_3ADDR_LEN))->field.reserved)) + bIsAggregateFrame = true; + + if (RTLLIB_QOS_HAS_SEQ(fc)) + LLCOffset += 2; + if (rx_stats->bContainHTC) + LLCOffset += sHTCLng; + + ChkLength = LLCOffset; + + if (skb->len <= ChkLength) + return 0; + + skb_pull(skb, LLCOffset); + ieee->bIsAggregateFrame = bIsAggregateFrame; + if (!bIsAggregateFrame) { + rxb->nr_subframes = 1; + + /* altered by clark 3/30/2010 + * The struct buffer size of the skb indicated to upper layer + * must be less than 5000, or the defraged IP datagram + * in the IP layer will exceed "ipfrag_high_tresh" and be + * discarded. so there must not use the function + * "skb_copy" and "skb_clone" for "skb". + */ + + /* Allocate new skb for releasing to upper layer */ + sub_skb = dev_alloc_skb(RTLLIB_SKBBUFFER_SIZE); + if (!sub_skb) + return 0; + skb_reserve(sub_skb, 12); + skb_put_data(sub_skb, skb->data, skb->len); + sub_skb->dev = ieee->dev; + + rxb->subframes[0] = sub_skb; + + memcpy(rxb->src, src, ETH_ALEN); + memcpy(rxb->dst, dst, ETH_ALEN); + rxb->subframes[0]->dev = ieee->dev; + return 1; + } + + rxb->nr_subframes = 0; + memcpy(rxb->src, src, ETH_ALEN); + memcpy(rxb->dst, dst, ETH_ALEN); + while (skb->len > ETHERNET_HEADER_SIZE) { + /* Offset 12 denote 2 mac address */ + nSubframe_Length = *((u16 *)(skb->data + 12)); + nSubframe_Length = (nSubframe_Length >> 8) + + (nSubframe_Length << 8); + + if (skb->len < (ETHERNET_HEADER_SIZE + nSubframe_Length)) { + netdev_info(ieee->dev, + "%s: A-MSDU parse error!! pRfd->nTotalSubframe : %d\n", + __func__, rxb->nr_subframes); + netdev_info(ieee->dev, + "%s: A-MSDU parse error!! Subframe Length: %d\n", + __func__, nSubframe_Length); + netdev_info(ieee->dev, + "nRemain_Length is %d and nSubframe_Length is : %d\n", + skb->len, nSubframe_Length); + netdev_info(ieee->dev, + "The Packet SeqNum is %d\n", + SeqNum); + return 0; + } + + /* move the data point to data content */ + skb_pull(skb, ETHERNET_HEADER_SIZE); + + /* altered by clark 3/30/2010 + * The struct buffer size of the skb indicated to upper layer + * must be less than 5000, or the defraged IP datagram + * in the IP layer will exceed "ipfrag_high_tresh" and be + * discarded. so there must not use the function + * "skb_copy" and "skb_clone" for "skb". + */ + + /* Allocate new skb for releasing to upper layer */ + sub_skb = dev_alloc_skb(nSubframe_Length + 12); + if (!sub_skb) + return 0; + skb_reserve(sub_skb, 12); + skb_put_data(sub_skb, skb->data, nSubframe_Length); + + sub_skb->dev = ieee->dev; + rxb->subframes[rxb->nr_subframes++] = sub_skb; + if (rxb->nr_subframes >= MAX_SUBFRAME_COUNT) { + netdev_dbg(ieee->dev, + "ParseSubframe(): Too many Subframes! Packets dropped!\n"); + break; + } + skb_pull(skb, nSubframe_Length); + + if (skb->len != 0) { + nPadding_Length = 4 - ((nSubframe_Length + + ETHERNET_HEADER_SIZE) % 4); + if (nPadding_Length == 4) + nPadding_Length = 0; + + if (skb->len < nPadding_Length) + return 0; + + skb_pull(skb, nPadding_Length); + } + } + + return rxb->nr_subframes; +} + +static size_t rtllib_rx_get_hdrlen(struct rtllib_device *ieee, + struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats) +{ + struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data; + u16 fc = le16_to_cpu(hdr->frame_ctl); + size_t hdrlen; + + hdrlen = rtllib_get_hdrlen(fc); + if (HTCCheck(ieee, skb->data)) { + if (net_ratelimit()) + netdev_info(ieee->dev, "%s: find HTCControl!\n", + __func__); + hdrlen += 4; + rx_stats->bContainHTC = true; + } + + if (RTLLIB_QOS_HAS_SEQ(fc)) + rx_stats->bIsQosData = true; + + return hdrlen; +} + +static int rtllib_rx_check_duplicate(struct rtllib_device *ieee, + struct sk_buff *skb, u8 multicast) +{ + struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data; + u16 fc, sc; + u8 frag, type, stype; + + fc = le16_to_cpu(hdr->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + sc = le16_to_cpu(hdr->seq_ctl); + frag = WLAN_GET_SEQ_FRAG(sc); + + if (!ieee->ht_info->cur_rx_reorder_enable || + !ieee->current_network.qos_data.active || + !IsDataFrame(skb->data) || + IsLegacyDataFrame(skb->data)) { + if (!((type == RTLLIB_FTYPE_MGMT) && + (stype == RTLLIB_STYPE_BEACON))) { + if (is_duplicate_packet(ieee, hdr)) + return -1; + } + } else { + struct rx_ts_record *pRxTS = NULL; + + if (GetTs(ieee, (struct ts_common_info **)&pRxTS, hdr->addr2, + (u8)Frame_QoSTID((u8 *)(skb->data)), RX_DIR, true)) { + if ((fc & (1 << 11)) && (frag == pRxTS->rx_last_frag_num) && + (WLAN_GET_SEQ_SEQ(sc) == pRxTS->rx_last_seq_num)) + return -1; + pRxTS->rx_last_frag_num = frag; + pRxTS->rx_last_seq_num = WLAN_GET_SEQ_SEQ(sc); + } else { + netdev_warn(ieee->dev, "%s(): No TS! Skip the check!\n", + __func__); + return -1; + } + } + + return 0; +} + +static void rtllib_rx_extract_addr(struct rtllib_device *ieee, + struct rtllib_hdr_4addr *hdr, u8 *dst, + u8 *src, u8 *bssid) +{ + u16 fc = le16_to_cpu(hdr->frame_ctl); + + switch (fc & (RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS)) { + case RTLLIB_FCTL_FROMDS: + ether_addr_copy(dst, hdr->addr1); + ether_addr_copy(src, hdr->addr3); + ether_addr_copy(bssid, hdr->addr2); + break; + case RTLLIB_FCTL_TODS: + ether_addr_copy(dst, hdr->addr3); + ether_addr_copy(src, hdr->addr2); + ether_addr_copy(bssid, hdr->addr1); + break; + case RTLLIB_FCTL_FROMDS | RTLLIB_FCTL_TODS: + ether_addr_copy(dst, hdr->addr3); + ether_addr_copy(src, hdr->addr4); + ether_addr_copy(bssid, ieee->current_network.bssid); + break; + default: + ether_addr_copy(dst, hdr->addr1); + ether_addr_copy(src, hdr->addr2); + ether_addr_copy(bssid, hdr->addr3); + break; + } +} + +static int rtllib_rx_data_filter(struct rtllib_device *ieee, u16 fc, + u8 *dst, u8 *src, u8 *bssid, u8 *addr2) +{ + u8 type, stype; + + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + /* Filter frames from different BSS */ + if (((fc & RTLLIB_FCTL_DSTODS) != RTLLIB_FCTL_DSTODS) && + !ether_addr_equal(ieee->current_network.bssid, bssid) && + !is_zero_ether_addr(ieee->current_network.bssid)) { + return -1; + } + + /* Filter packets sent by an STA that will be forwarded by AP */ + if (ieee->intel_promiscuous_md_info.promiscuous_on && + ieee->intel_promiscuous_md_info.fltr_src_sta_frame) { + if ((fc & RTLLIB_FCTL_TODS) && !(fc & RTLLIB_FCTL_FROMDS) && + !ether_addr_equal(dst, ieee->current_network.bssid) && + ether_addr_equal(bssid, ieee->current_network.bssid)) { + return -1; + } + } + + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. + */ + if (!ieee->intel_promiscuous_md_info.promiscuous_on) { + if (stype != RTLLIB_STYPE_DATA && + stype != RTLLIB_STYPE_DATA_CFACK && + stype != RTLLIB_STYPE_DATA_CFPOLL && + stype != RTLLIB_STYPE_DATA_CFACKPOLL && + stype != RTLLIB_STYPE_QOS_DATA) { + if (stype != RTLLIB_STYPE_NULLFUNC) + netdev_dbg(ieee->dev, + "RX: dropped data frame with no data (type=0x%02x, subtype=0x%02x)\n", + type, stype); + return -1; + } + } + + /* packets from our adapter are dropped (echo) */ + if (!memcmp(src, ieee->dev->dev_addr, ETH_ALEN)) + return -1; + + /* {broad,multi}cast packets to our BSS go through */ + if (is_multicast_ether_addr(dst)) { + if (memcmp(bssid, ieee->current_network.bssid, + ETH_ALEN)) + return -1; + } + return 0; +} + +static int rtllib_rx_get_crypt(struct rtllib_device *ieee, struct sk_buff *skb, + struct lib80211_crypt_data **crypt, size_t hdrlen) +{ + struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data; + u16 fc = le16_to_cpu(hdr->frame_ctl); + int idx = 0; + + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + + *crypt = ieee->crypt_info.crypt[idx]; + /* allow NULL decrypt to indicate an station specific override + * for default encryption + */ + if (*crypt && ((*crypt)->ops == NULL || + (*crypt)->ops->decrypt_mpdu == NULL)) + *crypt = NULL; + + if (!*crypt && (fc & RTLLIB_FCTL_WEP)) { + /* This seems to be triggered by some (multicast?) + * frames from other than current BSS, so just drop the + * frames silently instead of filling system log with + * these reports. + */ + netdev_dbg(ieee->dev, + "Decryption failed (not set) (SA= %pM)\n", + hdr->addr2); + return -1; + } + + return 0; +} + +static int rtllib_rx_decrypt(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats, + struct lib80211_crypt_data *crypt, size_t hdrlen) +{ + struct rtllib_hdr_4addr *hdr; + int keyidx = 0; + u16 fc, sc; + u8 frag; + + hdr = (struct rtllib_hdr_4addr *)skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + sc = le16_to_cpu(hdr->seq_ctl); + frag = WLAN_GET_SEQ_FRAG(sc); + + if ((!rx_stats->Decrypted)) + ieee->need_sw_enc = 1; + else + ieee->need_sw_enc = 0; + + keyidx = rtllib_rx_frame_decrypt(ieee, skb, crypt); + if ((fc & RTLLIB_FCTL_WEP) && (keyidx < 0)) { + netdev_info(ieee->dev, "%s: decrypt frame error\n", __func__); + return -1; + } + + hdr = (struct rtllib_hdr_4addr *)skb->data; + if ((frag != 0 || (fc & RTLLIB_FCTL_MOREFRAGS))) { + int flen; + struct sk_buff *frag_skb = rtllib_frag_cache_get(ieee, hdr); + + netdev_dbg(ieee->dev, "Rx Fragment received (%u)\n", frag); + + if (!frag_skb) { + netdev_dbg(ieee->dev, + "Rx cannot get skb from fragment cache (morefrag=%d seq=%u frag=%u)\n", + (fc & RTLLIB_FCTL_MOREFRAGS) != 0, + WLAN_GET_SEQ_SEQ(sc), frag); + return -1; + } + flen = skb->len; + if (frag != 0) + flen -= hdrlen; + + if (frag_skb->tail + flen > frag_skb->end) { + netdev_warn(ieee->dev, + "%s: host decrypted and reassembled frame did not fit skb\n", + __func__); + rtllib_frag_cache_invalidate(ieee, hdr); + return -1; + } + + if (frag == 0) { + /* copy first fragment (including full headers) into + * beginning of the fragment cache skb + */ + skb_put_data(frag_skb, skb->data, flen); + } else { + /* append frame payload to the end of the fragment + * cache skb + */ + skb_put_data(frag_skb, skb->data + hdrlen, flen); + } + dev_kfree_skb_any(skb); + skb = NULL; + + if (fc & RTLLIB_FCTL_MOREFRAGS) { + /* more fragments expected - leave the skb in fragment + * cache for now; it will be delivered to upper layers + * after all fragments have been received + */ + return -2; + } + + /* this was the last fragment and the frame will be + * delivered, so remove skb from fragment cache + */ + skb = frag_skb; + hdr = (struct rtllib_hdr_4addr *)skb->data; + rtllib_frag_cache_invalidate(ieee, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated + */ + if ((fc & RTLLIB_FCTL_WEP) && + rtllib_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) { + netdev_info(ieee->dev, "%s: ==>decrypt msdu error\n", __func__); + return -1; + } + + hdr = (struct rtllib_hdr_4addr *)skb->data; + if (crypt && !(fc & RTLLIB_FCTL_WEP) && !ieee->open_wep) { + if (/*ieee->ieee802_1x &&*/ + rtllib_is_eapol_frame(ieee, skb, hdrlen)) { + /* pass unencrypted EAPOL frames even if encryption is + * configured + */ + struct eapol *eap = (struct eapol *)(skb->data + + 24); + netdev_dbg(ieee->dev, + "RX: IEEE 802.1X EAPOL frame: %s\n", + eap_get_type(eap->type)); + } else { + netdev_dbg(ieee->dev, + "encryption configured, but RX frame not encrypted (SA= %pM)\n", + hdr->addr2); + return -1; + } + } + + if (crypt && !(fc & RTLLIB_FCTL_WEP) && + rtllib_is_eapol_frame(ieee, skb, hdrlen)) { + struct eapol *eap = (struct eapol *)(skb->data + 24); + + netdev_dbg(ieee->dev, "RX: IEEE 802.1X EAPOL frame: %s\n", + eap_get_type(eap->type)); + } + + if (crypt && !(fc & RTLLIB_FCTL_WEP) && !ieee->open_wep && + !rtllib_is_eapol_frame(ieee, skb, hdrlen)) { + netdev_dbg(ieee->dev, + "dropped unencrypted RX data frame from %pM (drop_unencrypted=1)\n", + hdr->addr2); + return -1; + } + + return 0; +} + +static void rtllib_rx_check_leave_lps(struct rtllib_device *ieee, u8 unicast, + u8 nr_subframes) +{ + if (unicast) { + if (ieee->link_state == MAC80211_LINKED) { + if (((ieee->link_detect_info.NumRxUnicastOkInPeriod + + ieee->link_detect_info.NumTxOkInPeriod) > 8) || + (ieee->link_detect_info.NumRxUnicastOkInPeriod > 2)) { + ieee->leisure_ps_leave(ieee->dev); + } + } + } + ieee->last_rx_ps_time = jiffies; +} + +static void rtllib_rx_indicate_pkt_legacy(struct rtllib_device *ieee, + struct rtllib_rx_stats *rx_stats, + struct rtllib_rxb *rxb, + u8 *dst, + u8 *src) +{ + struct net_device *dev = ieee->dev; + u16 ethertype; + int i = 0; + + if (rxb == NULL) { + netdev_info(dev, "%s: rxb is NULL!!\n", __func__); + return; + } + + for (i = 0; i < rxb->nr_subframes; i++) { + struct sk_buff *sub_skb = rxb->subframes[i]; + + if (sub_skb) { + /* convert hdr + possible LLC headers + * into Ethernet header + */ + ethertype = (sub_skb->data[6] << 8) | sub_skb->data[7]; + if (sub_skb->len >= 8 && + ((memcmp(sub_skb->data, rfc1042_header, SNAP_SIZE) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(sub_skb->data, bridge_tunnel_header, SNAP_SIZE) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation + * and replace EtherType + */ + skb_pull(sub_skb, SNAP_SIZE); + ether_addr_copy(skb_push(sub_skb, ETH_ALEN), + src); + ether_addr_copy(skb_push(sub_skb, ETH_ALEN), + dst); + } else { + u16 len; + /* Leave Ethernet header part of hdr + * and full payload + */ + len = sub_skb->len; + memcpy(skb_push(sub_skb, 2), &len, 2); + ether_addr_copy(skb_push(sub_skb, ETH_ALEN), + src); + ether_addr_copy(skb_push(sub_skb, ETH_ALEN), + dst); + } + + ieee->stats.rx_packets++; + ieee->stats.rx_bytes += sub_skb->len; + + if (is_multicast_ether_addr(dst)) + ieee->stats.multicast++; + + /* Indicate the packets to upper layer */ + memset(sub_skb->cb, 0, sizeof(sub_skb->cb)); + sub_skb->protocol = eth_type_trans(sub_skb, dev); + sub_skb->dev = dev; + sub_skb->dev->stats.rx_packets++; + sub_skb->dev->stats.rx_bytes += sub_skb->len; + /* 802.11 crc not sufficient */ + sub_skb->ip_summed = CHECKSUM_NONE; + netif_rx(sub_skb); + } + } + kfree(rxb); +} + +static int rtllib_rx_InfraAdhoc(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats) +{ + struct net_device *dev = ieee->dev; + struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data; + struct lib80211_crypt_data *crypt = NULL; + struct rtllib_rxb *rxb = NULL; + struct rx_ts_record *pTS = NULL; + u16 fc, sc, SeqNum = 0; + u8 type, stype, multicast = 0, unicast = 0, nr_subframes = 0, TID = 0; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN] = {0}; + + size_t hdrlen = 0; + bool bToOtherSTA = false; + int ret = 0, i = 0; + + fc = le16_to_cpu(hdr->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + sc = le16_to_cpu(hdr->seq_ctl); + + /*Filter pkt not to me*/ + multicast = is_multicast_ether_addr(hdr->addr1); + unicast = !multicast; + if (unicast && !ether_addr_equal(dev->dev_addr, hdr->addr1)) { + if (ieee->net_promiscuous_md) + bToOtherSTA = true; + else + goto rx_dropped; + } + + /*Filter pkt has too small length */ + hdrlen = rtllib_rx_get_hdrlen(ieee, skb, rx_stats); + if (skb->len < hdrlen) { + netdev_info(dev, + "%s():ERR!!! skb->len is smaller than hdrlen\n", + __func__); + goto rx_dropped; + } + + /* Filter Duplicate pkt */ + ret = rtllib_rx_check_duplicate(ieee, skb, multicast); + if (ret < 0) + goto rx_dropped; + + /* Filter CTRL Frame */ + if (type == RTLLIB_FTYPE_CTL) + goto rx_dropped; + + /* Filter MGNT Frame */ + if (type == RTLLIB_FTYPE_MGMT) { + if (bToOtherSTA) + goto rx_dropped; + if (rtllib_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } + + /* Filter WAPI DATA Frame */ + + /* Update statstics for AP roaming */ + if (!bToOtherSTA) { + ieee->link_detect_info.NumRecvDataInPeriod++; + ieee->link_detect_info.NumRxOkInPeriod++; + } + + /* Data frame - extract src/dst addresses */ + rtllib_rx_extract_addr(ieee, hdr, dst, src, bssid); + + /* Filter Data frames */ + ret = rtllib_rx_data_filter(ieee, fc, dst, src, bssid, hdr->addr2); + if (ret < 0) + goto rx_dropped; + + if (skb->len == hdrlen) + goto rx_dropped; + + /* Send pspoll based on moredata */ + if ((ieee->iw_mode == IW_MODE_INFRA) && + (ieee->sta_sleep == LPS_IS_SLEEP) && + (ieee->polling) && (!bToOtherSTA)) { + if (WLAN_FC_MORE_DATA(fc)) { + /* more data bit is set, let's request a new frame + * from the AP + */ + rtllib_sta_ps_send_pspoll_frame(ieee); + } else { + ieee->polling = false; + } + } + + /* Get crypt if encrypted */ + ret = rtllib_rx_get_crypt(ieee, skb, &crypt, hdrlen); + if (ret == -1) + goto rx_dropped; + + /* Decrypt data frame (including reassemble) */ + ret = rtllib_rx_decrypt(ieee, skb, rx_stats, crypt, hdrlen); + if (ret == -1) + goto rx_dropped; + else if (ret == -2) + goto rx_exit; + + /* Get TS for Rx Reorder */ + hdr = (struct rtllib_hdr_4addr *)skb->data; + if (ieee->current_network.qos_data.active && IsQoSDataFrame(skb->data) + && !is_multicast_ether_addr(hdr->addr1) + && (!bToOtherSTA)) { + TID = Frame_QoSTID(skb->data); + SeqNum = WLAN_GET_SEQ_SEQ(sc); + GetTs(ieee, (struct ts_common_info **)&pTS, hdr->addr2, TID, + RX_DIR, true); + if (TID != 0 && TID != 3) + ieee->bis_any_nonbepkts = true; + } + + /* Parse rx data frame (For AMSDU) */ + /* skb: hdr + (possible reassembled) full plaintext payload */ + rxb = kmalloc(sizeof(struct rtllib_rxb), GFP_ATOMIC); + if (!rxb) + goto rx_dropped; + + /* to parse amsdu packets */ + /* qos data packets & reserved bit is 1 */ + if (parse_subframe(ieee, skb, rx_stats, rxb, src, dst) == 0) { + /* only to free rxb, and not submit the packets + * to upper layer + */ + for (i = 0; i < rxb->nr_subframes; i++) + dev_kfree_skb(rxb->subframes[i]); + kfree(rxb); + rxb = NULL; + goto rx_dropped; + } + + /* Update WAPI PN */ + + /* Check if leave LPS */ + if (!bToOtherSTA) { + if (ieee->bIsAggregateFrame) + nr_subframes = rxb->nr_subframes; + else + nr_subframes = 1; + if (unicast) + ieee->link_detect_info.NumRxUnicastOkInPeriod += nr_subframes; + rtllib_rx_check_leave_lps(ieee, unicast, nr_subframes); + } + + /* Indicate packets to upper layer or Rx Reorder */ + if (!ieee->ht_info->cur_rx_reorder_enable || pTS == NULL || bToOtherSTA) + rtllib_rx_indicate_pkt_legacy(ieee, rx_stats, rxb, dst, src); + else + RxReorderIndicatePacket(ieee, rxb, pTS, SeqNum); + + dev_kfree_skb(skb); + + rx_exit: + return 1; + + rx_dropped: + ieee->stats.rx_dropped++; + + /* Returning 0 indicates to caller that we have not handled the SKB-- + * so it is still allocated and can be used again by underlying + * hardware as a DMA target + */ + return 0; +} + +static int rtllib_rx_Monitor(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats) +{ + struct rtllib_hdr_4addr *hdr = (struct rtllib_hdr_4addr *)skb->data; + u16 fc = le16_to_cpu(hdr->frame_ctl); + size_t hdrlen = rtllib_get_hdrlen(fc); + + if (skb->len < hdrlen) { + netdev_info(ieee->dev, + "%s():ERR!!! skb->len is smaller than hdrlen\n", + __func__); + return 0; + } + + if (HTCCheck(ieee, skb->data)) { + if (net_ratelimit()) + netdev_info(ieee->dev, "%s: Find HTCControl!\n", + __func__); + hdrlen += 4; + } + + ieee->stats.rx_packets++; + ieee->stats.rx_bytes += skb->len; + rtllib_monitor_rx(ieee, skb, rx_stats, hdrlen); + + return 1; +} + +/* All received frames are sent to this function. @skb contains the frame in + * IEEE 802.11 format, i.e., in the format it was sent over air. + * This function is called only as a tasklet (software IRQ). + */ +int rtllib_rx(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats) +{ + int ret = 0; + + if (!ieee || !skb || !rx_stats) { + pr_info("%s: Input parameters NULL!\n", __func__); + goto rx_dropped; + } + if (skb->len < 10) { + netdev_info(ieee->dev, "%s: SKB length < 10\n", __func__); + goto rx_dropped; + } + + switch (ieee->iw_mode) { + case IW_MODE_ADHOC: + case IW_MODE_INFRA: + ret = rtllib_rx_InfraAdhoc(ieee, skb, rx_stats); + break; + case IW_MODE_MONITOR: + ret = rtllib_rx_Monitor(ieee, skb, rx_stats); + break; + default: + netdev_info(ieee->dev, "%s: ERR iw mode!!!\n", __func__); + break; + } + + return ret; + + rx_dropped: + if (ieee) + ieee->stats.rx_dropped++; + return 0; +} +EXPORT_SYMBOL(rtllib_rx); + +static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; + +/* Make ther structure we read from the beacon packet has the right values */ +static int rtllib_verify_qos_info(struct rtllib_qos_information_element + *info_element, int sub_type) +{ + if (info_element->elementID != QOS_ELEMENT_ID) + return -1; + if (info_element->qui_subtype != sub_type) + return -1; + if (memcmp(info_element->qui, qos_oui, QOS_OUI_LEN)) + return -1; + if (info_element->qui_type != QOS_OUI_TYPE) + return -1; + if (info_element->version != QOS_VERSION_1) + return -1; + + return 0; +} + +/* Parse a QoS parameter element */ +static int rtllib_read_qos_param_element( + struct rtllib_qos_parameter_info *element_param, + struct rtllib_info_element *info_element) +{ + size_t size = sizeof(*element_param); + + if (!element_param || !info_element || info_element->len != size - 2) + return -1; + + memcpy(element_param, info_element, size); + return rtllib_verify_qos_info(&element_param->info_element, + QOS_OUI_PARAM_SUB_TYPE); +} + +/* Parse a QoS information element */ +static int rtllib_read_qos_info_element( + struct rtllib_qos_information_element *element_info, + struct rtllib_info_element *info_element) +{ + size_t size = sizeof(*element_info); + + if (!element_info || !info_element || info_element->len != size - 2) + return -1; + + memcpy(element_info, info_element, size); + return rtllib_verify_qos_info(element_info, QOS_OUI_INFO_SUB_TYPE); +} + +/* Write QoS parameters from the ac parameters. */ +static int rtllib_qos_convert_ac_to_parameters(struct rtllib_qos_parameter_info *param_elm, + struct rtllib_qos_data *qos_data) +{ + struct rtllib_qos_ac_parameter *ac_params; + struct rtllib_qos_parameters *qos_param = &(qos_data->parameters); + int i; + u8 aci; + u8 acm; + + qos_data->wmm_acm = 0; + for (i = 0; i < QOS_QUEUE_NUM; i++) { + ac_params = &(param_elm->ac_params_record[i]); + + aci = (ac_params->aci_aifsn & 0x60) >> 5; + acm = (ac_params->aci_aifsn & 0x10) >> 4; + + if (aci >= QOS_QUEUE_NUM) + continue; + switch (aci) { + case 1: + /* BIT(0) | BIT(3) */ + if (acm) + qos_data->wmm_acm |= (0x01 << 0) | (0x01 << 3); + break; + case 2: + /* BIT(4) | BIT(5) */ + if (acm) + qos_data->wmm_acm |= (0x01 << 4) | (0x01 << 5); + break; + case 3: + /* BIT(6) | BIT(7) */ + if (acm) + qos_data->wmm_acm |= (0x01 << 6) | (0x01 << 7); + break; + case 0: + default: + /* BIT(1) | BIT(2) */ + if (acm) + qos_data->wmm_acm |= (0x01 << 1) | (0x01 << 2); + break; + } + + qos_param->aifs[aci] = (ac_params->aci_aifsn) & 0x0f; + + /* WMM spec P.11: The minimum value for AIFSN shall be 2 */ + qos_param->aifs[aci] = max_t(u8, qos_param->aifs[aci], 2); + + qos_param->cw_min[aci] = cpu_to_le16(ac_params->ecw_min_max & + 0x0F); + + qos_param->cw_max[aci] = cpu_to_le16((ac_params->ecw_min_max & + 0xF0) >> 4); + + qos_param->flag[aci] = + (ac_params->aci_aifsn & 0x10) ? 0x01 : 0x00; + qos_param->tx_op_limit[aci] = ac_params->tx_op_limit; + } + return 0; +} + +/* we have a generic data element which it may contain QoS information or + * parameters element. check the information element length to decide + * which type to read + */ +static int rtllib_parse_qos_info_param_IE(struct rtllib_device *ieee, + struct rtllib_info_element + *info_element, + struct rtllib_network *network) +{ + int rc = 0; + struct rtllib_qos_information_element qos_info_element; + + rc = rtllib_read_qos_info_element(&qos_info_element, info_element); + + if (rc == 0) { + network->qos_data.param_count = qos_info_element.ac_info & 0x0F; + network->flags |= NETWORK_HAS_QOS_INFORMATION; + } else { + struct rtllib_qos_parameter_info param_element; + + rc = rtllib_read_qos_param_element(¶m_element, + info_element); + if (rc == 0) { + rtllib_qos_convert_ac_to_parameters(¶m_element, + &(network->qos_data)); + network->flags |= NETWORK_HAS_QOS_PARAMETERS; + network->qos_data.param_count = + param_element.info_element.ac_info & 0x0F; + } + } + + if (rc == 0) { + netdev_dbg(ieee->dev, "QoS is supported\n"); + network->qos_data.supported = 1; + } + return rc; +} + +static const char *get_info_element_string(u16 id) +{ + switch (id) { + case MFIE_TYPE_SSID: + return "SSID"; + case MFIE_TYPE_RATES: + return "RATES"; + case MFIE_TYPE_FH_SET: + return "FH_SET"; + case MFIE_TYPE_DS_SET: + return "DS_SET"; + case MFIE_TYPE_CF_SET: + return "CF_SET"; + case MFIE_TYPE_TIM: + return "TIM"; + case MFIE_TYPE_IBSS_SET: + return "IBSS_SET"; + case MFIE_TYPE_COUNTRY: + return "COUNTRY"; + case MFIE_TYPE_HOP_PARAMS: + return "HOP_PARAMS"; + case MFIE_TYPE_HOP_TABLE: + return "HOP_TABLE"; + case MFIE_TYPE_REQUEST: + return "REQUEST"; + case MFIE_TYPE_CHALLENGE: + return "CHALLENGE"; + case MFIE_TYPE_POWER_CONSTRAINT: + return "POWER_CONSTRAINT"; + case MFIE_TYPE_POWER_CAPABILITY: + return "POWER_CAPABILITY"; + case MFIE_TYPE_TPC_REQUEST: + return "TPC_REQUEST"; + case MFIE_TYPE_TPC_REPORT: + return "TPC_REPORT"; + case MFIE_TYPE_SUPP_CHANNELS: + return "SUPP_CHANNELS"; + case MFIE_TYPE_CSA: + return "CSA"; + case MFIE_TYPE_MEASURE_REQUEST: + return "MEASURE_REQUEST"; + case MFIE_TYPE_MEASURE_REPORT: + return "MEASURE_REPORT"; + case MFIE_TYPE_QUIET: + return "QUIET"; + case MFIE_TYPE_IBSS_DFS: + return "IBSS_DFS"; + case MFIE_TYPE_RSN: + return "RSN"; + case MFIE_TYPE_RATES_EX: + return "RATES_EX"; + case MFIE_TYPE_GENERIC: + return "GENERIC"; + case MFIE_TYPE_QOS_PARAMETER: + return "QOS_PARAMETER"; + default: + return "UNKNOWN"; + } +} + +static inline void rtllib_extract_country_ie( + struct rtllib_device *ieee, + struct rtllib_info_element *info_element, + struct rtllib_network *network, + u8 *addr2) +{ + if (IS_DOT11D_ENABLE(ieee)) { + if (info_element->len != 0) { + memcpy(network->CountryIeBuf, info_element->data, + info_element->len); + network->CountryIeLen = info_element->len; + + if (!IS_COUNTRY_IE_VALID(ieee)) { + if (rtllib_act_scanning(ieee, false) && + ieee->FirstIe_InScan) + netdev_info(ieee->dev, + "Received beacon CountryIE, SSID: <%s>\n", + network->ssid); + dot11d_update_country(ieee, addr2, + info_element->len, + info_element->data); + } + } + + if (IS_EQUAL_CIE_SRC(ieee, addr2)) + UPDATE_CIE_WATCHDOG(ieee); + } +} + +static void rtllib_parse_mife_generic(struct rtllib_device *ieee, + struct rtllib_info_element *info_element, + struct rtllib_network *network, + u16 *tmp_htcap_len, + u16 *tmp_htinfo_len) +{ + u16 ht_realtek_agg_len = 0; + u8 ht_realtek_agg_buf[MAX_IE_LEN]; + + if (!rtllib_parse_qos_info_param_IE(ieee, info_element, network)) + return; + if (info_element->len >= 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x01) { + network->wpa_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->wpa_ie, info_element, network->wpa_ie_len); + return; + } + if (info_element->len == 7 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0xe0 && + info_element->data[2] == 0x4c && + info_element->data[3] == 0x01 && + info_element->data[4] == 0x02) + network->Turbo_Enable = 1; + + if (*tmp_htcap_len == 0) { + if (info_element->len >= 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x90 && + info_element->data[2] == 0x4c && + info_element->data[3] == 0x033) { + *tmp_htcap_len = min_t(u8, info_element->len, + MAX_IE_LEN); + if (*tmp_htcap_len != 0) { + network->bssht.bd_ht_spec_ver = HT_SPEC_VER_EWC; + network->bssht.bd_ht_cap_len = min_t(u16, *tmp_htcap_len, + sizeof(network->bssht.bd_ht_cap_buf)); + memcpy(network->bssht.bd_ht_cap_buf, + info_element->data, + network->bssht.bd_ht_cap_len); + } + } + if (*tmp_htcap_len != 0) { + network->bssht.bd_support_ht = true; + network->bssht.bd_ht_1r = ((((struct ht_capab_ele *)(network->bssht.bd_ht_cap_buf))->MCS[1]) == 0); + } else { + network->bssht.bd_support_ht = false; + network->bssht.bd_ht_1r = false; + } + } + + if (*tmp_htinfo_len == 0) { + if (info_element->len >= 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x90 && + info_element->data[2] == 0x4c && + info_element->data[3] == 0x034) { + *tmp_htinfo_len = min_t(u8, info_element->len, + MAX_IE_LEN); + if (*tmp_htinfo_len != 0) { + network->bssht.bd_ht_spec_ver = HT_SPEC_VER_EWC; + network->bssht.bd_ht_info_len = min_t(u16, *tmp_htinfo_len, + sizeof(network->bssht.bd_ht_info_buf)); + memcpy(network->bssht.bd_ht_info_buf, + info_element->data, + network->bssht.bd_ht_info_len); + } + } + } + + if (network->bssht.bd_support_ht) { + if (info_element->len >= 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0xe0 && + info_element->data[2] == 0x4c && + info_element->data[3] == 0x02) { + ht_realtek_agg_len = min_t(u8, info_element->len, + MAX_IE_LEN); + memcpy(ht_realtek_agg_buf, info_element->data, + info_element->len); + } + if (ht_realtek_agg_len >= 5) { + network->realtek_cap_exit = true; + network->bssht.bd_rt2rt_aggregation = true; + + if ((ht_realtek_agg_buf[4] == 1) && + (ht_realtek_agg_buf[5] & 0x02)) + network->bssht.bd_rt2rt_long_slot_time = true; + + if ((ht_realtek_agg_buf[4] == 1) && + (ht_realtek_agg_buf[5] & RT_HT_CAP_USE_92SE)) + network->bssht.rt2rt_ht_mode |= RT_HT_CAP_USE_92SE; + } + } + if (ht_realtek_agg_len >= 5) { + if ((ht_realtek_agg_buf[5] & RT_HT_CAP_USE_SOFTAP)) + network->bssht.rt2rt_ht_mode |= RT_HT_CAP_USE_SOFTAP; + } + + if ((info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x05 && + info_element->data[2] == 0xb5) || + (info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x0a && + info_element->data[2] == 0xf7) || + (info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x10 && + info_element->data[2] == 0x18)) { + network->broadcom_cap_exist = true; + } + if (info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x0c && + info_element->data[2] == 0x43) + network->ralink_cap_exist = true; + if ((info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x03 && + info_element->data[2] == 0x7f) || + (info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x13 && + info_element->data[2] == 0x74)) + network->atheros_cap_exist = true; + + if ((info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0x43)) + network->marvell_cap_exist = true; + if (info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x40 && + info_element->data[2] == 0x96) + network->cisco_cap_exist = true; + + if (info_element->len >= 3 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x0a && + info_element->data[2] == 0xf5) + network->airgo_cap_exist = true; + + if (info_element->len > 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x40 && + info_element->data[2] == 0x96 && + info_element->data[3] == 0x01) { + if (info_element->len == 6) { + memcpy(network->CcxRmState, &info_element->data[4], 2); + if (network->CcxRmState[0] != 0) + network->bCcxRmEnable = true; + else + network->bCcxRmEnable = false; + network->MBssidMask = network->CcxRmState[1] & 0x07; + if (network->MBssidMask != 0) { + network->bMBssidValid = true; + network->MBssidMask = 0xff << + (network->MBssidMask); + ether_addr_copy(network->MBssid, + network->bssid); + network->MBssid[5] &= network->MBssidMask; + } else { + network->bMBssidValid = false; + } + } else { + network->bCcxRmEnable = false; + } + } + if (info_element->len > 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x40 && + info_element->data[2] == 0x96 && + info_element->data[3] == 0x03) { + if (info_element->len == 5) { + network->bWithCcxVerNum = true; + network->BssCcxVerNumber = info_element->data[4]; + } else { + network->bWithCcxVerNum = false; + network->BssCcxVerNumber = 0; + } + } + if (info_element->len > 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x04) { + netdev_dbg(ieee->dev, "MFIE_TYPE_WZC: %d bytes\n", + info_element->len); + network->wzc_ie_len = min(info_element->len + 2, MAX_WZC_IE_LEN); + memcpy(network->wzc_ie, info_element, network->wzc_ie_len); + } +} + +static void rtllib_parse_mfie_ht_cap(struct rtllib_info_element *info_element, + struct rtllib_network *network, + u16 *tmp_htcap_len) +{ + struct bss_ht *ht = &network->bssht; + + *tmp_htcap_len = min_t(u8, info_element->len, MAX_IE_LEN); + if (*tmp_htcap_len != 0) { + ht->bd_ht_spec_ver = HT_SPEC_VER_EWC; + ht->bd_ht_cap_len = min_t(u16, *tmp_htcap_len, + sizeof(ht->bd_ht_cap_buf)); + memcpy(ht->bd_ht_cap_buf, info_element->data, ht->bd_ht_cap_len); + + ht->bd_support_ht = true; + ht->bd_ht_1r = ((((struct ht_capab_ele *) + ht->bd_ht_cap_buf))->MCS[1]) == 0; + + ht->bd_bandwidth = (enum ht_channel_width) + (((struct ht_capab_ele *) + (ht->bd_ht_cap_buf))->ChlWidth); + } else { + ht->bd_support_ht = false; + ht->bd_ht_1r = false; + ht->bd_bandwidth = HT_CHANNEL_WIDTH_20; + } +} + +int rtllib_parse_info_param(struct rtllib_device *ieee, + struct rtllib_info_element *info_element, + u16 length, + struct rtllib_network *network, + struct rtllib_rx_stats *stats) +{ + u8 i; + short offset; + u16 tmp_htcap_len = 0; + u16 tmp_htinfo_len = 0; + char rates_str[64]; + char *p; + + while (length >= sizeof(*info_element)) { + if (sizeof(*info_element) + info_element->len > length) { + netdev_dbg(ieee->dev, + "Info elem: parse failed: info_element->len + 2 > left : info_element->len+2=%zd left=%d, id=%d.\n", + info_element->len + sizeof(*info_element), + length, info_element->id); + /* We stop processing but don't return an error here + * because some misbehaviour APs break this rule. ie. + * Orinoco AP1000. + */ + break; + } + + switch (info_element->id) { + case MFIE_TYPE_SSID: + if (rtllib_is_empty_essid(info_element->data, + info_element->len)) { + network->flags |= NETWORK_EMPTY_ESSID; + break; + } + + network->ssid_len = min(info_element->len, + (u8)IW_ESSID_MAX_SIZE); + memcpy(network->ssid, info_element->data, + network->ssid_len); + if (network->ssid_len < IW_ESSID_MAX_SIZE) + memset(network->ssid + network->ssid_len, 0, + IW_ESSID_MAX_SIZE - network->ssid_len); + + netdev_dbg(ieee->dev, "MFIE_TYPE_SSID: '%s' len=%d.\n", + network->ssid, network->ssid_len); + break; + + case MFIE_TYPE_RATES: + p = rates_str; + network->rates_len = min(info_element->len, + MAX_RATES_LENGTH); + for (i = 0; i < network->rates_len; i++) { + network->rates[i] = info_element->data[i]; + p += scnprintf(p, sizeof(rates_str) - + (p - rates_str), "%02X ", + network->rates[i]); + if (rtllib_is_ofdm_rate + (info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + RTLLIB_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + + if (rtllib_is_cck_rate + (info_element->data[i])) { + network->flags |= NETWORK_HAS_CCK; + } + } + + netdev_dbg(ieee->dev, "MFIE_TYPE_RATES: '%s' (%d)\n", + rates_str, network->rates_len); + break; + + case MFIE_TYPE_RATES_EX: + p = rates_str; + network->rates_ex_len = min(info_element->len, + MAX_RATES_EX_LENGTH); + for (i = 0; i < network->rates_ex_len; i++) { + network->rates_ex[i] = info_element->data[i]; + p += scnprintf(p, sizeof(rates_str) - + (p - rates_str), "%02X ", + network->rates_ex[i]); + if (rtllib_is_ofdm_rate + (info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + RTLLIB_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + } + + netdev_dbg(ieee->dev, "MFIE_TYPE_RATES_EX: '%s' (%d)\n", + rates_str, network->rates_ex_len); + break; + + case MFIE_TYPE_DS_SET: + netdev_dbg(ieee->dev, "MFIE_TYPE_DS_SET: %d\n", + info_element->data[0]); + network->channel = info_element->data[0]; + break; + + case MFIE_TYPE_FH_SET: + netdev_dbg(ieee->dev, "MFIE_TYPE_FH_SET: ignored\n"); + break; + + case MFIE_TYPE_CF_SET: + netdev_dbg(ieee->dev, "MFIE_TYPE_CF_SET: ignored\n"); + break; + + case MFIE_TYPE_TIM: + if (info_element->len < 4) + break; + + network->tim.tim_count = info_element->data[0]; + network->tim.tim_period = info_element->data[1]; + + network->dtim_period = info_element->data[1]; + if (ieee->link_state != MAC80211_LINKED) + break; + network->last_dtim_sta_time = jiffies; + + network->dtim_data = RTLLIB_DTIM_VALID; + + if (info_element->data[2] & 1) + network->dtim_data |= RTLLIB_DTIM_MBCAST; + + offset = (info_element->data[2] >> 1) * 2; + + if (ieee->assoc_id < 8 * offset || + ieee->assoc_id > 8 * (offset + info_element->len - 3)) + break; + + offset = (ieee->assoc_id / 8) - offset; + if (info_element->data[3 + offset] & + (1 << (ieee->assoc_id % 8))) + network->dtim_data |= RTLLIB_DTIM_UCAST; + + network->listen_interval = network->dtim_period; + break; + + case MFIE_TYPE_ERP: + network->erp_value = info_element->data[0]; + network->flags |= NETWORK_HAS_ERP_VALUE; + netdev_dbg(ieee->dev, "MFIE_TYPE_ERP_SET: %d\n", + network->erp_value); + break; + case MFIE_TYPE_IBSS_SET: + network->atim_window = info_element->data[0]; + netdev_dbg(ieee->dev, "MFIE_TYPE_IBSS_SET: %d\n", + network->atim_window); + break; + + case MFIE_TYPE_CHALLENGE: + netdev_dbg(ieee->dev, "MFIE_TYPE_CHALLENGE: ignored\n"); + break; + + case MFIE_TYPE_GENERIC: + netdev_dbg(ieee->dev, "MFIE_TYPE_GENERIC: %d bytes\n", + info_element->len); + + rtllib_parse_mife_generic(ieee, info_element, network, + &tmp_htcap_len, + &tmp_htinfo_len); + break; + + case MFIE_TYPE_RSN: + netdev_dbg(ieee->dev, "MFIE_TYPE_RSN: %d bytes\n", + info_element->len); + network->rsn_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->rsn_ie, info_element, + network->rsn_ie_len); + break; + + case MFIE_TYPE_HT_CAP: + netdev_dbg(ieee->dev, "MFIE_TYPE_HT_CAP: %d bytes\n", + info_element->len); + + rtllib_parse_mfie_ht_cap(info_element, network, + &tmp_htcap_len); + break; + + case MFIE_TYPE_HT_INFO: + netdev_dbg(ieee->dev, "MFIE_TYPE_HT_INFO: %d bytes\n", + info_element->len); + tmp_htinfo_len = min_t(u8, info_element->len, + MAX_IE_LEN); + if (tmp_htinfo_len) { + network->bssht.bd_ht_spec_ver = HT_SPEC_VER_IEEE; + network->bssht.bd_ht_info_len = tmp_htinfo_len > + sizeof(network->bssht.bd_ht_info_buf) ? + sizeof(network->bssht.bd_ht_info_buf) : + tmp_htinfo_len; + memcpy(network->bssht.bd_ht_info_buf, + info_element->data, + network->bssht.bd_ht_info_len); + } + break; + + case MFIE_TYPE_AIRONET: + netdev_dbg(ieee->dev, "MFIE_TYPE_AIRONET: %d bytes\n", + info_element->len); + if (info_element->len > IE_CISCO_FLAG_POSITION) { + network->bWithAironetIE = true; + + if ((info_element->data[IE_CISCO_FLAG_POSITION] + & SUPPORT_CKIP_MIC) || + (info_element->data[IE_CISCO_FLAG_POSITION] + & SUPPORT_CKIP_PK)) + network->bCkipSupported = true; + else + network->bCkipSupported = false; + } else { + network->bWithAironetIE = false; + network->bCkipSupported = false; + } + break; + case MFIE_TYPE_QOS_PARAMETER: + netdev_err(ieee->dev, + "QoS Error need to parse QOS_PARAMETER IE\n"); + break; + + case MFIE_TYPE_COUNTRY: + netdev_dbg(ieee->dev, "MFIE_TYPE_COUNTRY: %d bytes\n", + info_element->len); + rtllib_extract_country_ie(ieee, info_element, network, + network->bssid); + break; +/* TODO */ + default: + netdev_dbg(ieee->dev, + "Unsupported info element: %s (%d)\n", + get_info_element_string(info_element->id), + info_element->id); + break; + } + + length -= sizeof(*info_element) + info_element->len; + info_element = + (struct rtllib_info_element *)&info_element->data[info_element->len]; + } + + if (!network->atheros_cap_exist && !network->broadcom_cap_exist && + !network->cisco_cap_exist && !network->ralink_cap_exist && + !network->bssht.bd_rt2rt_aggregation) + network->unknown_cap_exist = true; + else + network->unknown_cap_exist = false; + return 0; +} + +static long rtllib_translate_todbm(u8 signal_strength_index) +{ + long signal_power; + + signal_power = (long)((signal_strength_index + 1) >> 1); + signal_power -= 95; + + return signal_power; +} + +static inline int rtllib_network_init( + struct rtllib_device *ieee, + struct rtllib_probe_response *beacon, + struct rtllib_network *network, + struct rtllib_rx_stats *stats) +{ + memset(&network->qos_data, 0, sizeof(struct rtllib_qos_data)); + + /* Pull out fixed field data */ + ether_addr_copy(network->bssid, beacon->header.addr3); + network->capability = le16_to_cpu(beacon->capability); + network->last_scanned = jiffies; + network->time_stamp[0] = beacon->time_stamp[0]; + network->time_stamp[1] = beacon->time_stamp[1]; + network->beacon_interval = le16_to_cpu(beacon->beacon_interval); + /* Where to pull this? beacon->listen_interval;*/ + network->listen_interval = 0x0A; + network->rates_len = network->rates_ex_len = 0; + network->ssid_len = 0; + network->hidden_ssid_len = 0; + memset(network->hidden_ssid, 0, sizeof(network->hidden_ssid)); + network->flags = 0; + network->atim_window = 0; + network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? + 0x3 : 0x0; + network->berp_info_valid = false; + network->broadcom_cap_exist = false; + network->ralink_cap_exist = false; + network->atheros_cap_exist = false; + network->cisco_cap_exist = false; + network->unknown_cap_exist = false; + network->realtek_cap_exit = false; + network->marvell_cap_exist = false; + network->airgo_cap_exist = false; + network->Turbo_Enable = 0; + network->SignalStrength = stats->SignalStrength; + network->RSSI = stats->SignalStrength; + network->CountryIeLen = 0; + memset(network->CountryIeBuf, 0, MAX_IE_LEN); + HTInitializeBssDesc(&network->bssht); + network->flags |= NETWORK_HAS_CCK; + + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; + network->wzc_ie_len = 0; + + if (rtllib_parse_info_param(ieee, + beacon->info_element, + (stats->len - sizeof(*beacon)), + network, + stats)) + return 1; + + network->mode = 0; + + if (network->flags & NETWORK_HAS_OFDM) + network->mode |= WIRELESS_MODE_G; + if (network->flags & NETWORK_HAS_CCK) + network->mode |= WIRELESS_MODE_B; + + if (network->mode == 0) { + netdev_dbg(ieee->dev, "Filtered out '%s (%pM)' network.\n", + escape_essid(network->ssid, network->ssid_len), + network->bssid); + return 1; + } + + if (network->bssht.bd_support_ht) { + if (network->mode & (WIRELESS_MODE_G | WIRELESS_MODE_B)) + network->mode = WIRELESS_MODE_N_24G; + } + if (rtllib_is_empty_essid(network->ssid, network->ssid_len)) + network->flags |= NETWORK_EMPTY_ESSID; + stats->signal = 30 + (stats->SignalStrength * 70) / 100; + stats->noise = rtllib_translate_todbm((u8)(100 - stats->signal)) - 25; + + memcpy(&network->stats, stats, sizeof(network->stats)); + + return 0; +} + +static inline int is_same_network(struct rtllib_network *src, + struct rtllib_network *dst, u8 ssidbroad) +{ + /* A network is only a duplicate if the channel, BSSID, ESSID + * and the capability field (in particular IBSS and BSS) all match. + * We treat all <hidden> with the same BSSID and channel + * as one network + */ + return (((src->ssid_len == dst->ssid_len) || (!ssidbroad)) && + (src->channel == dst->channel) && + !memcmp(src->bssid, dst->bssid, ETH_ALEN) && + (!memcmp(src->ssid, dst->ssid, src->ssid_len) || + (!ssidbroad)) && + ((src->capability & WLAN_CAPABILITY_IBSS) == + (dst->capability & WLAN_CAPABILITY_IBSS)) && + ((src->capability & WLAN_CAPABILITY_ESS) == + (dst->capability & WLAN_CAPABILITY_ESS))); +} + +static inline void update_network(struct rtllib_device *ieee, + struct rtllib_network *dst, + struct rtllib_network *src) +{ + int qos_active; + u8 old_param; + + memcpy(&dst->stats, &src->stats, sizeof(struct rtllib_rx_stats)); + dst->capability = src->capability; + memcpy(dst->rates, src->rates, src->rates_len); + dst->rates_len = src->rates_len; + memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); + dst->rates_ex_len = src->rates_ex_len; + if (src->ssid_len > 0) { + if (dst->ssid_len == 0) { + memset(dst->hidden_ssid, 0, sizeof(dst->hidden_ssid)); + dst->hidden_ssid_len = src->ssid_len; + memcpy(dst->hidden_ssid, src->ssid, src->ssid_len); + } else { + memset(dst->ssid, 0, dst->ssid_len); + dst->ssid_len = src->ssid_len; + memcpy(dst->ssid, src->ssid, src->ssid_len); + } + } + dst->mode = src->mode; + dst->flags = src->flags; + dst->time_stamp[0] = src->time_stamp[0]; + dst->time_stamp[1] = src->time_stamp[1]; + if (src->flags & NETWORK_HAS_ERP_VALUE) { + dst->erp_value = src->erp_value; + dst->berp_info_valid = src->berp_info_valid = true; + } + dst->beacon_interval = src->beacon_interval; + dst->listen_interval = src->listen_interval; + dst->atim_window = src->atim_window; + dst->dtim_period = src->dtim_period; + dst->dtim_data = src->dtim_data; + dst->last_dtim_sta_time = src->last_dtim_sta_time; + memcpy(&dst->tim, &src->tim, sizeof(struct rtllib_tim_parameters)); + + dst->bssht.bd_support_ht = src->bssht.bd_support_ht; + dst->bssht.bd_rt2rt_aggregation = src->bssht.bd_rt2rt_aggregation; + dst->bssht.bd_ht_cap_len = src->bssht.bd_ht_cap_len; + memcpy(dst->bssht.bd_ht_cap_buf, src->bssht.bd_ht_cap_buf, + src->bssht.bd_ht_cap_len); + dst->bssht.bd_ht_info_len = src->bssht.bd_ht_info_len; + memcpy(dst->bssht.bd_ht_info_buf, src->bssht.bd_ht_info_buf, + src->bssht.bd_ht_info_len); + dst->bssht.bd_ht_spec_ver = src->bssht.bd_ht_spec_ver; + dst->bssht.bd_rt2rt_long_slot_time = src->bssht.bd_rt2rt_long_slot_time; + dst->broadcom_cap_exist = src->broadcom_cap_exist; + dst->ralink_cap_exist = src->ralink_cap_exist; + dst->atheros_cap_exist = src->atheros_cap_exist; + dst->realtek_cap_exit = src->realtek_cap_exit; + dst->marvell_cap_exist = src->marvell_cap_exist; + dst->cisco_cap_exist = src->cisco_cap_exist; + dst->airgo_cap_exist = src->airgo_cap_exist; + dst->unknown_cap_exist = src->unknown_cap_exist; + memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); + dst->wpa_ie_len = src->wpa_ie_len; + memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); + dst->rsn_ie_len = src->rsn_ie_len; + memcpy(dst->wzc_ie, src->wzc_ie, src->wzc_ie_len); + dst->wzc_ie_len = src->wzc_ie_len; + + dst->last_scanned = jiffies; + /* qos related parameters */ + qos_active = dst->qos_data.active; + old_param = dst->qos_data.param_count; + dst->qos_data.supported = src->qos_data.supported; + if (dst->flags & NETWORK_HAS_QOS_PARAMETERS) + memcpy(&dst->qos_data, &src->qos_data, + sizeof(struct rtllib_qos_data)); + if (dst->qos_data.supported == 1) { + if (dst->ssid_len) + netdev_dbg(ieee->dev, + "QoS the network %s is QoS supported\n", + dst->ssid); + else + netdev_dbg(ieee->dev, + "QoS the network is QoS supported\n"); + } + dst->qos_data.active = qos_active; + dst->qos_data.old_param_count = old_param; + + dst->wmm_info = src->wmm_info; + if (src->wmm_param[0].ac_aci_acm_aifsn || + src->wmm_param[1].ac_aci_acm_aifsn || + src->wmm_param[2].ac_aci_acm_aifsn || + src->wmm_param[3].ac_aci_acm_aifsn) + memcpy(dst->wmm_param, src->wmm_param, WME_AC_PRAM_LEN); + + dst->SignalStrength = src->SignalStrength; + dst->RSSI = src->RSSI; + dst->Turbo_Enable = src->Turbo_Enable; + + dst->CountryIeLen = src->CountryIeLen; + memcpy(dst->CountryIeBuf, src->CountryIeBuf, src->CountryIeLen); + + dst->bWithAironetIE = src->bWithAironetIE; + dst->bCkipSupported = src->bCkipSupported; + memcpy(dst->CcxRmState, src->CcxRmState, 2); + dst->bCcxRmEnable = src->bCcxRmEnable; + dst->MBssidMask = src->MBssidMask; + dst->bMBssidValid = src->bMBssidValid; + memcpy(dst->MBssid, src->MBssid, 6); + dst->bWithCcxVerNum = src->bWithCcxVerNum; + dst->BssCcxVerNumber = src->BssCcxVerNumber; +} + +static inline int is_beacon(u16 fc) +{ + return (WLAN_FC_GET_STYPE(fc) == RTLLIB_STYPE_BEACON); +} + +static int IsPassiveChannel(struct rtllib_device *rtllib, u8 channel) +{ + if (channel > MAX_CHANNEL_NUMBER) { + netdev_info(rtllib->dev, "%s(): Invalid Channel\n", __func__); + return 0; + } + + if (rtllib->active_channel_map[channel] == 2) + return 1; + + return 0; +} + +int rtllib_legal_channel(struct rtllib_device *rtllib, u8 channel) +{ + if (channel > MAX_CHANNEL_NUMBER) { + netdev_info(rtllib->dev, "%s(): Invalid Channel\n", __func__); + return 0; + } + if (rtllib->active_channel_map[channel] > 0) + return 1; + + return 0; +} +EXPORT_SYMBOL(rtllib_legal_channel); + +static inline void rtllib_process_probe_response( + struct rtllib_device *ieee, + struct rtllib_probe_response *beacon, + struct rtllib_rx_stats *stats) +{ + struct rtllib_network *target; + struct rtllib_network *oldest = NULL; + struct rtllib_info_element *info_element = &beacon->info_element[0]; + unsigned long flags; + short renew; + struct rtllib_network *network = kzalloc(sizeof(struct rtllib_network), + GFP_ATOMIC); + u16 frame_ctl = le16_to_cpu(beacon->header.frame_ctl); + + if (!network) + return; + + netdev_dbg(ieee->dev, + "'%s' ( %pM ): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", + escape_essid(info_element->data, info_element->len), + beacon->header.addr3, + (le16_to_cpu(beacon->capability) & (1 << 0xf)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0xe)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0xd)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0xc)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0xb)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0xa)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x9)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x8)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x7)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x6)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x5)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x4)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x3)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x2)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x1)) ? '1' : '0', + (le16_to_cpu(beacon->capability) & (1 << 0x0)) ? '1' : '0'); + + if (rtllib_network_init(ieee, beacon, network, stats)) { + netdev_dbg(ieee->dev, "Dropped '%s' ( %pM) via %s.\n", + escape_essid(info_element->data, info_element->len), + beacon->header.addr3, + is_beacon(frame_ctl) ? "BEACON" : "PROBE RESPONSE"); + goto free_network; + } + + if (!rtllib_legal_channel(ieee, network->channel)) + goto free_network; + + if (WLAN_FC_GET_STYPE(frame_ctl) == RTLLIB_STYPE_PROBE_RESP) { + if (IsPassiveChannel(ieee, network->channel)) { + netdev_info(ieee->dev, + "GetScanInfo(): For Global Domain, filter probe response at channel(%d).\n", + network->channel); + goto free_network; + } + } + + /* The network parsed correctly -- so now we scan our known networks + * to see if we can find it in our list. + * + * NOTE: This search is definitely not optimized. Once its doing + * the "right thing" we'll optimize it for efficiency if + * necessary + */ + + /* Search for this entry in the list and update it if it is + * already there. + */ + + spin_lock_irqsave(&ieee->lock, flags); + if (is_same_network(&ieee->current_network, network, + (network->ssid_len ? 1 : 0))) { + update_network(ieee, &ieee->current_network, network); + if ((ieee->current_network.mode == WIRELESS_MODE_N_24G || + ieee->current_network.mode == WIRELESS_MODE_G) && + ieee->current_network.berp_info_valid) { + if (ieee->current_network.erp_value & ERP_UseProtection) + ieee->current_network.buseprotection = true; + else + ieee->current_network.buseprotection = false; + } + if (is_beacon(frame_ctl)) { + if (ieee->link_state >= MAC80211_LINKED) + ieee->link_detect_info.NumRecvBcnInPeriod++; + } + } + list_for_each_entry(target, &ieee->network_list, list) { + if (is_same_network(target, network, + (target->ssid_len ? 1 : 0))) + break; + if ((oldest == NULL) || + (target->last_scanned < oldest->last_scanned)) + oldest = target; + } + + /* If we didn't find a match, then get a new network slot to initialize + * with this beacon's information + */ + if (&target->list == &ieee->network_list) { + if (list_empty(&ieee->network_free_list)) { + /* If there are no more slots, expire the oldest */ + list_del(&oldest->list); + target = oldest; + netdev_dbg(ieee->dev, + "Expired '%s' ( %pM) from network list.\n", + escape_essid(target->ssid, target->ssid_len), + target->bssid); + } else { + /* Otherwise just pull from the free list */ + target = list_entry(ieee->network_free_list.next, + struct rtllib_network, list); + list_del(ieee->network_free_list.next); + } + + netdev_dbg(ieee->dev, "Adding '%s' ( %pM) via %s.\n", + escape_essid(network->ssid, network->ssid_len), + network->bssid, + is_beacon(frame_ctl) ? "BEACON" : "PROBE RESPONSE"); + + memcpy(target, network, sizeof(*target)); + list_add_tail(&target->list, &ieee->network_list); + if (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) + rtllib_softmac_new_net(ieee, network); + } else { + netdev_dbg(ieee->dev, "Updating '%s' ( %pM) via %s.\n", + escape_essid(target->ssid, target->ssid_len), + target->bssid, + is_beacon(frame_ctl) ? "BEACON" : "PROBE RESPONSE"); + + /* we have an entry and we are going to update it. But this + * entry may be already expired. In this case we do the same + * as we found a new net and call the new_net handler + */ + renew = !time_after(target->last_scanned + ieee->scan_age, + jiffies); + if ((!target->ssid_len) && + (((network->ssid_len > 0) && (target->hidden_ssid_len == 0)) + || ((ieee->current_network.ssid_len == network->ssid_len) && + (strncmp(ieee->current_network.ssid, network->ssid, + network->ssid_len) == 0) && + (ieee->link_state == MAC80211_NOLINK)))) + renew = 1; + update_network(ieee, target, network); + if (renew && (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE)) + rtllib_softmac_new_net(ieee, network); + } + + spin_unlock_irqrestore(&ieee->lock, flags); + if (is_beacon(frame_ctl) && + is_same_network(&ieee->current_network, network, + (network->ssid_len ? 1 : 0)) && + (ieee->link_state == MAC80211_LINKED)) { + ieee->handle_beacon(ieee->dev, beacon, &ieee->current_network); + } +free_network: + kfree(network); +} + +static void rtllib_rx_mgt(struct rtllib_device *ieee, + struct sk_buff *skb, + struct rtllib_rx_stats *stats) +{ + struct rtllib_hdr_4addr *header = (struct rtllib_hdr_4addr *)skb->data; + + if ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) != + RTLLIB_STYPE_PROBE_RESP) && + (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) != + RTLLIB_STYPE_BEACON)) + ieee->last_rx_ps_time = jiffies; + + switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) { + case RTLLIB_STYPE_BEACON: + netdev_dbg(ieee->dev, "received BEACON (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))); + rtllib_process_probe_response( + ieee, (struct rtllib_probe_response *)header, + stats); + + if (ieee->sta_sleep || (ieee->ps != RTLLIB_PS_DISABLED && + ieee->iw_mode == IW_MODE_INFRA && + ieee->link_state == MAC80211_LINKED)) + schedule_work(&ieee->ps_task); + + break; + + case RTLLIB_STYPE_PROBE_RESP: + netdev_dbg(ieee->dev, "received PROBE RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))); + rtllib_process_probe_response(ieee, + (struct rtllib_probe_response *)header, stats); + break; + case RTLLIB_STYPE_PROBE_REQ: + netdev_dbg(ieee->dev, "received PROBE REQUEST (%d)\n", + WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))); + if ((ieee->softmac_features & IEEE_SOFTMAC_PROBERS) && + (ieee->iw_mode == IW_MODE_ADHOC && + ieee->link_state == MAC80211_LINKED)) + rtllib_rx_probe_rq(ieee, skb); + break; + } +} diff --git a/drivers/staging/rtl8192e/rtllib_softmac.c b/drivers/staging/rtl8192e/rtllib_softmac.c new file mode 100644 index 0000000000..de17024911 --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_softmac.c @@ -0,0 +1,2919 @@ +// SPDX-License-Identifier: GPL-2.0 +/* IEEE 802.11 SoftMAC layer + * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com> + * + * Mostly extracted from the rtl8180-sa2400 driver for the + * in-kernel generic ieee802.11 stack. + * + * Few lines might be stolen from other part of the rtllib + * stack. Copyright who own it's copyright + * + * WPA code stolen from the ipw2200 driver. + * Copyright who own it's copyright. + */ +#include "rtllib.h" + +#include <linux/random.h> +#include <linux/delay.h> +#include <linux/uaccess.h> +#include <linux/etherdevice.h> +#include <linux/ieee80211.h> +#include "dot11d.h" + +static void rtllib_sta_wakeup(struct rtllib_device *ieee, short nl); + +static short rtllib_is_54g(struct rtllib_network *net) +{ + return (net->rates_ex_len > 0) || (net->rates_len > 4); +} + +/* returns the total length needed for placing the RATE MFIE + * tag and the EXTENDED RATE MFIE tag if needed. + * It encludes two bytes per tag for the tag itself and its len + */ +static unsigned int rtllib_MFIE_rate_len(struct rtllib_device *ieee) +{ + unsigned int rate_len = 0; + + rate_len = RTLLIB_CCK_RATE_LEN + 2; + rate_len += RTLLIB_OFDM_RATE_LEN + 2; + + return rate_len; +} + +/* place the MFIE rate, tag to the memory (double) pointed. + * Then it updates the pointer so that + * it points after the new MFIE tag added. + */ +static void rtllib_MFIE_Brate(struct rtllib_device *ieee, u8 **tag_p) +{ + u8 *tag = *tag_p; + + *tag++ = MFIE_TYPE_RATES; + *tag++ = 4; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_1MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_2MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_5MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_11MB; + + /* We may add an option for custom rates that specific HW + * might support + */ + *tag_p = tag; +} + +static void rtllib_MFIE_Grate(struct rtllib_device *ieee, u8 **tag_p) +{ + u8 *tag = *tag_p; + + *tag++ = MFIE_TYPE_RATES_EX; + *tag++ = 8; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_6MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_9MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_12MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_18MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_24MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_36MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_48MB; + *tag++ = RTLLIB_BASIC_RATE_MASK | RTLLIB_OFDM_RATE_54MB; + + /* We may add an option for custom rates that specific HW might + * support + */ + *tag_p = tag; +} + +static void rtllib_WMM_Info(struct rtllib_device *ieee, u8 **tag_p) +{ + u8 *tag = *tag_p; + + *tag++ = MFIE_TYPE_GENERIC; + *tag++ = 7; + *tag++ = 0x00; + *tag++ = 0x50; + *tag++ = 0xf2; + *tag++ = 0x02; + *tag++ = 0x00; + *tag++ = 0x01; + *tag++ = MAX_SP_Len; + *tag_p = tag; +} + +static void rtllib_TURBO_Info(struct rtllib_device *ieee, u8 **tag_p) +{ + u8 *tag = *tag_p; + + *tag++ = MFIE_TYPE_GENERIC; + *tag++ = 7; + *tag++ = 0x00; + *tag++ = 0xe0; + *tag++ = 0x4c; + *tag++ = 0x01; + *tag++ = 0x02; + *tag++ = 0x11; + *tag++ = 0x00; + + *tag_p = tag; + netdev_alert(ieee->dev, "This is enable turbo mode IE process\n"); +} + +static void enqueue_mgmt(struct rtllib_device *ieee, struct sk_buff *skb) +{ + int nh; + + nh = (ieee->mgmt_queue_head + 1) % MGMT_QUEUE_NUM; + +/* if the queue is full but we have newer frames then + * just overwrites the oldest. + * + * if (nh == ieee->mgmt_queue_tail) + * return -1; + */ + ieee->mgmt_queue_head = nh; + ieee->mgmt_queue_ring[nh] = skb; +} + +static void init_mgmt_queue(struct rtllib_device *ieee) +{ + ieee->mgmt_queue_tail = ieee->mgmt_queue_head = 0; +} + +u8 MgntQuery_TxRateExcludeCCKRates(struct rtllib_device *ieee) +{ + u16 i; + u8 QueryRate = 0; + u8 BasicRate; + + for (i = 0; i < ieee->current_network.rates_len; i++) { + BasicRate = ieee->current_network.rates[i] & 0x7F; + if (!rtllib_is_cck_rate(BasicRate)) { + if (QueryRate == 0) { + QueryRate = BasicRate; + } else { + if (BasicRate < QueryRate) + QueryRate = BasicRate; + } + } + } + + if (QueryRate == 0) { + QueryRate = 12; + netdev_info(ieee->dev, "No BasicRate found!!\n"); + } + return QueryRate; +} + +static u8 MgntQuery_MgntFrameTxRate(struct rtllib_device *ieee) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + u8 rate; + + if (ht_info->iot_action & HT_IOT_ACT_MGNT_USE_CCK_6M) + rate = 0x0c; + else + rate = ieee->basic_rate & 0x7f; + + if (rate == 0) { + if (ieee->mode == WIRELESS_MODE_N_24G && !ht_info->bCurSuppCCK) + rate = 0x0c; + else + rate = 0x02; + } + + return rate; +} + +inline void softmac_mgmt_xmit(struct sk_buff *skb, struct rtllib_device *ieee) +{ + unsigned long flags; + short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE; + struct rtllib_hdr_3addr *header = + (struct rtllib_hdr_3addr *)skb->data; + + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + 8); + + spin_lock_irqsave(&ieee->lock, flags); + + /* called with 2nd param 0, no mgmt lock required */ + rtllib_sta_wakeup(ieee, 0); + + if (le16_to_cpu(header->frame_ctl) == RTLLIB_STYPE_BEACON) + tcb_desc->queue_index = BEACON_QUEUE; + else + tcb_desc->queue_index = MGNT_QUEUE; + + if (ieee->disable_mgnt_queue) + tcb_desc->queue_index = HIGH_QUEUE; + + tcb_desc->data_rate = MgntQuery_MgntFrameTxRate(ieee); + tcb_desc->ratr_index = 7; + tcb_desc->tx_dis_rate_fallback = 1; + tcb_desc->tx_use_drv_assinged_rate = 1; + if (single) { + if (ieee->queue_stop) { + enqueue_mgmt(ieee, skb); + } else { + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + /* avoid watchdog triggers */ + ieee->softmac_data_hard_start_xmit(skb, ieee->dev, + ieee->basic_rate); + } + + spin_unlock_irqrestore(&ieee->lock, flags); + } else { + spin_unlock_irqrestore(&ieee->lock, flags); + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags); + + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + /* check whether the managed packet queued greater than 5 */ + if (!ieee->check_nic_enough_desc(ieee->dev, + tcb_desc->queue_index) || + skb_queue_len(&ieee->skb_waitQ[tcb_desc->queue_index]) || + ieee->queue_stop) { + /* insert the skb packet to the management queue + * + * as for the completion function, it does not need + * to check it any more. + */ + netdev_info(ieee->dev, + "%s():insert to waitqueue, queue_index:%d!\n", + __func__, tcb_desc->queue_index); + skb_queue_tail(&ieee->skb_waitQ[tcb_desc->queue_index], + skb); + } else { + ieee->softmac_hard_start_xmit(skb, ieee->dev); + } + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags); + } +} + +static inline void +softmac_ps_mgmt_xmit(struct sk_buff *skb, + struct rtllib_device *ieee) +{ + short single = ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE; + struct rtllib_hdr_3addr *header = + (struct rtllib_hdr_3addr *)skb->data; + u16 fc, type, stype; + struct cb_desc *tcb_desc = (struct cb_desc *)(skb->cb + 8); + + fc = le16_to_cpu(header->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + if (stype != RTLLIB_STYPE_PSPOLL) + tcb_desc->queue_index = MGNT_QUEUE; + else + tcb_desc->queue_index = HIGH_QUEUE; + + if (ieee->disable_mgnt_queue) + tcb_desc->queue_index = HIGH_QUEUE; + + tcb_desc->data_rate = MgntQuery_MgntFrameTxRate(ieee); + tcb_desc->ratr_index = 7; + tcb_desc->tx_dis_rate_fallback = 1; + tcb_desc->tx_use_drv_assinged_rate = 1; + if (single) { + if (type != RTLLIB_FTYPE_CTL) { + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + } + /* avoid watchdog triggers */ + ieee->softmac_data_hard_start_xmit(skb, ieee->dev, + ieee->basic_rate); + + } else { + if (type != RTLLIB_FTYPE_CTL) { + header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + } + ieee->softmac_hard_start_xmit(skb, ieee->dev); + } +} + +static inline struct sk_buff *rtllib_probe_req(struct rtllib_device *ieee) +{ + unsigned int len, rate_len; + u8 *tag; + struct sk_buff *skb; + struct rtllib_probe_request *req; + + len = ieee->current_network.ssid_len; + + rate_len = rtllib_MFIE_rate_len(ieee); + + skb = dev_alloc_skb(sizeof(struct rtllib_probe_request) + + 2 + len + rate_len + ieee->tx_headroom); + + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + req = skb_put(skb, sizeof(struct rtllib_probe_request)); + req->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_PROBE_REQ); + req->header.duration_id = 0; + + eth_broadcast_addr(req->header.addr1); + ether_addr_copy(req->header.addr2, ieee->dev->dev_addr); + eth_broadcast_addr(req->header.addr3); + + tag = skb_put(skb, len + 2 + rate_len); + + *tag++ = MFIE_TYPE_SSID; + *tag++ = len; + memcpy(tag, ieee->current_network.ssid, len); + tag += len; + + rtllib_MFIE_Brate(ieee, &tag); + rtllib_MFIE_Grate(ieee, &tag); + + return skb; +} + +static struct sk_buff *rtllib_get_beacon_(struct rtllib_device *ieee); + +static void rtllib_send_beacon(struct rtllib_device *ieee) +{ + struct sk_buff *skb; + + if (!ieee->ieee_up) + return; + skb = rtllib_get_beacon_(ieee); + + if (skb) { + softmac_mgmt_xmit(skb, ieee); + ieee->softmac_stats.tx_beacons++; + } + + if (ieee->beacon_txing && ieee->ieee_up) + mod_timer(&ieee->beacon_timer, jiffies + + (msecs_to_jiffies(ieee->current_network.beacon_interval - 5))); +} + +static void rtllib_send_beacon_cb(struct timer_list *t) +{ + struct rtllib_device *ieee = + from_timer(ieee, t, beacon_timer); + unsigned long flags; + + spin_lock_irqsave(&ieee->beacon_lock, flags); + rtllib_send_beacon(ieee); + spin_unlock_irqrestore(&ieee->beacon_lock, flags); +} + +/* Enables network monitor mode, all rx packets will be received. */ +void rtllib_EnableNetMonitorMode(struct net_device *dev, + bool bInitState) +{ + struct rtllib_device *ieee = netdev_priv_rsl(dev); + + netdev_info(dev, "========>Enter Monitor Mode\n"); + + ieee->AllowAllDestAddrHandler(dev, true, !bInitState); +} + +/* Disables network monitor mode. Only packets destinated to + * us will be received. + */ +void rtllib_DisableNetMonitorMode(struct net_device *dev, + bool bInitState) +{ + struct rtllib_device *ieee = netdev_priv_rsl(dev); + + netdev_info(dev, "========>Exit Monitor Mode\n"); + + ieee->AllowAllDestAddrHandler(dev, false, !bInitState); +} + +/* Enables the specialized promiscuous mode required by Intel. + * In this mode, Intel intends to hear traffics from/to other STAs in the + * same BSS. Therefore we don't have to disable checking BSSID and we only need + * to allow all dest. BUT: if we enable checking BSSID then we can't recv + * packets from other STA. + */ +void rtllib_EnableIntelPromiscuousMode(struct net_device *dev, + bool bInitState) +{ + bool bFilterOutNonAssociatedBSSID = false; + + struct rtllib_device *ieee = netdev_priv_rsl(dev); + + netdev_info(dev, "========>Enter Intel Promiscuous Mode\n"); + + ieee->AllowAllDestAddrHandler(dev, true, !bInitState); + ieee->SetHwRegHandler(dev, HW_VAR_CECHK_BSSID, + (u8 *)&bFilterOutNonAssociatedBSSID); + + ieee->net_promiscuous_md = true; +} +EXPORT_SYMBOL(rtllib_EnableIntelPromiscuousMode); + +/* Disables the specialized promiscuous mode required by Intel. + * See MgntEnableIntelPromiscuousMode for detail. + */ +void rtllib_DisableIntelPromiscuousMode(struct net_device *dev, + bool bInitState) +{ + bool bFilterOutNonAssociatedBSSID = true; + + struct rtllib_device *ieee = netdev_priv_rsl(dev); + + netdev_info(dev, "========>Exit Intel Promiscuous Mode\n"); + + ieee->AllowAllDestAddrHandler(dev, false, !bInitState); + ieee->SetHwRegHandler(dev, HW_VAR_CECHK_BSSID, + (u8 *)&bFilterOutNonAssociatedBSSID); + + ieee->net_promiscuous_md = false; +} +EXPORT_SYMBOL(rtllib_DisableIntelPromiscuousMode); + +static void rtllib_send_probe(struct rtllib_device *ieee) +{ + struct sk_buff *skb; + + skb = rtllib_probe_req(ieee); + if (skb) { + softmac_mgmt_xmit(skb, ieee); + ieee->softmac_stats.tx_probe_rq++; + } +} + +static void rtllib_send_probe_requests(struct rtllib_device *ieee) +{ + if (ieee->active_scan && (ieee->softmac_features & + IEEE_SOFTMAC_PROBERQ)) { + rtllib_send_probe(ieee); + rtllib_send_probe(ieee); + } +} + +static void rtllib_update_active_chan_map(struct rtllib_device *ieee) +{ + memcpy(ieee->active_channel_map, GET_DOT11D_INFO(ieee)->channel_map, + MAX_CHANNEL_NUMBER + 1); +} + +/* this performs syncro scan blocking the caller until all channels + * in the allowed channel map has been checked. + */ +static void rtllib_softmac_scan_syncro(struct rtllib_device *ieee) +{ + union iwreq_data wrqu; + short ch = 0; + + rtllib_update_active_chan_map(ieee); + + ieee->be_scan_inprogress = true; + + mutex_lock(&ieee->scan_mutex); + + while (1) { + do { + ch++; + if (ch > MAX_CHANNEL_NUMBER) + goto out; /* scan completed */ + } while (!ieee->active_channel_map[ch]); + + /* this function can be called in two situations + * 1- We have switched to ad-hoc mode and we are + * performing a complete syncro scan before conclude + * there are no interesting cell and to create a + * new one. In this case the link state is + * MAC80211_NOLINK until we found an interesting cell. + * If so the ieee8021_new_net, called by the RX path + * will set the state to MAC80211_LINKED, so we stop + * scanning + * 2- We are linked and the root uses run iwlist scan. + * So we switch to MAC80211_LINKED_SCANNING to remember + * that we are still logically linked (not interested in + * new network events, despite for updating the net list, + * but we are temporarly 'unlinked' as the driver shall + * not filter RX frames and the channel is changing. + * So the only situation in which are interested is to check + * if the state become LINKED because of the #1 situation + */ + + if (ieee->link_state == MAC80211_LINKED) + goto out; + if (ieee->sync_scan_hurryup) { + netdev_info(ieee->dev, + "============>sync_scan_hurryup out\n"); + goto out; + } + + ieee->set_chan(ieee->dev, ch); + if (ieee->active_channel_map[ch] == 1) + rtllib_send_probe_requests(ieee); + + /* this prevent excessive time wait when we + * need to wait for a syncro scan to end.. + */ + msleep_interruptible_rsl(RTLLIB_SOFTMAC_SCAN_TIME); + } +out: + ieee->actscanning = false; + ieee->sync_scan_hurryup = 0; + + if (ieee->link_state >= MAC80211_LINKED) { + if (IS_DOT11D_ENABLE(ieee)) + dot11d_scan_complete(ieee); + } + mutex_unlock(&ieee->scan_mutex); + + ieee->be_scan_inprogress = false; + + memset(&wrqu, 0, sizeof(wrqu)); + wireless_send_event(ieee->dev, SIOCGIWSCAN, &wrqu, NULL); +} + +static void rtllib_softmac_scan_wq(void *data) +{ + struct rtllib_device *ieee = container_of_dwork_rsl(data, + struct rtllib_device, softmac_scan_wq); + u8 last_channel = ieee->current_network.channel; + + rtllib_update_active_chan_map(ieee); + + if (!ieee->ieee_up) + return; + if (rtllib_act_scanning(ieee, true)) + return; + + mutex_lock(&ieee->scan_mutex); + + if (ieee->rf_power_state == rf_off) { + netdev_info(ieee->dev, + "======>%s():rf state is rf_off, return\n", + __func__); + goto out1; + } + + do { + ieee->current_network.channel = + (ieee->current_network.channel + 1) % + MAX_CHANNEL_NUMBER; + if (ieee->scan_watch_dog++ > MAX_CHANNEL_NUMBER) { + if (!ieee->active_channel_map[ieee->current_network.channel]) + ieee->current_network.channel = 6; + goto out; /* no good chans */ + } + } while (!ieee->active_channel_map[ieee->current_network.channel]); + + if (ieee->scanning_continue == 0) + goto out; + + ieee->set_chan(ieee->dev, ieee->current_network.channel); + + if (ieee->active_channel_map[ieee->current_network.channel] == 1) + rtllib_send_probe_requests(ieee); + + schedule_delayed_work(&ieee->softmac_scan_wq, + msecs_to_jiffies(RTLLIB_SOFTMAC_SCAN_TIME)); + + mutex_unlock(&ieee->scan_mutex); + return; + +out: + if (IS_DOT11D_ENABLE(ieee)) + dot11d_scan_complete(ieee); + ieee->current_network.channel = last_channel; + +out1: + ieee->actscanning = false; + ieee->scan_watch_dog = 0; + ieee->scanning_continue = 0; + mutex_unlock(&ieee->scan_mutex); +} + +static void rtllib_beacons_start(struct rtllib_device *ieee) +{ + unsigned long flags; + + spin_lock_irqsave(&ieee->beacon_lock, flags); + + ieee->beacon_txing = 1; + rtllib_send_beacon(ieee); + + spin_unlock_irqrestore(&ieee->beacon_lock, flags); +} + +static void rtllib_beacons_stop(struct rtllib_device *ieee) +{ + unsigned long flags; + + spin_lock_irqsave(&ieee->beacon_lock, flags); + + ieee->beacon_txing = 0; + + spin_unlock_irqrestore(&ieee->beacon_lock, flags); + del_timer_sync(&ieee->beacon_timer); +} + +void rtllib_stop_send_beacons(struct rtllib_device *ieee) +{ + ieee->stop_send_beacons(ieee->dev); + if (ieee->softmac_features & IEEE_SOFTMAC_BEACONS) + rtllib_beacons_stop(ieee); +} +EXPORT_SYMBOL(rtllib_stop_send_beacons); + +void rtllib_start_send_beacons(struct rtllib_device *ieee) +{ + ieee->start_send_beacons(ieee->dev); + if (ieee->softmac_features & IEEE_SOFTMAC_BEACONS) + rtllib_beacons_start(ieee); +} +EXPORT_SYMBOL(rtllib_start_send_beacons); + +static void rtllib_softmac_stop_scan(struct rtllib_device *ieee) +{ + mutex_lock(&ieee->scan_mutex); + ieee->scan_watch_dog = 0; + if (ieee->scanning_continue == 1) { + ieee->scanning_continue = 0; + ieee->actscanning = false; + mutex_unlock(&ieee->scan_mutex); + cancel_delayed_work_sync(&ieee->softmac_scan_wq); + } else { + mutex_unlock(&ieee->scan_mutex); + } +} + +void rtllib_stop_scan(struct rtllib_device *ieee) +{ + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) + rtllib_softmac_stop_scan(ieee); +} +EXPORT_SYMBOL(rtllib_stop_scan); + +void rtllib_stop_scan_syncro(struct rtllib_device *ieee) +{ + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) + ieee->sync_scan_hurryup = 1; +} +EXPORT_SYMBOL(rtllib_stop_scan_syncro); + +bool rtllib_act_scanning(struct rtllib_device *ieee, bool sync_scan) +{ + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) { + if (sync_scan) + return ieee->be_scan_inprogress; + else + return ieee->actscanning || ieee->be_scan_inprogress; + } else { + return test_bit(STATUS_SCANNING, &ieee->status); + } +} +EXPORT_SYMBOL(rtllib_act_scanning); + +/* called with ieee->lock held */ +static void rtllib_start_scan(struct rtllib_device *ieee) +{ + ieee->rtllib_ips_leave_wq(ieee->dev); + + if (IS_DOT11D_ENABLE(ieee)) { + if (IS_COUNTRY_IE_VALID(ieee)) + RESET_CIE_WATCHDOG(ieee); + } + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) { + if (ieee->scanning_continue == 0) { + ieee->actscanning = true; + ieee->scanning_continue = 1; + schedule_delayed_work(&ieee->softmac_scan_wq, 0); + } + } +} + +/* called with wx_mutex held */ +void rtllib_start_scan_syncro(struct rtllib_device *ieee) +{ + if (IS_DOT11D_ENABLE(ieee)) { + if (IS_COUNTRY_IE_VALID(ieee)) + RESET_CIE_WATCHDOG(ieee); + } + ieee->sync_scan_hurryup = 0; + if (ieee->softmac_features & IEEE_SOFTMAC_SCAN) + rtllib_softmac_scan_syncro(ieee); +} +EXPORT_SYMBOL(rtllib_start_scan_syncro); + +static inline struct sk_buff * +rtllib_authentication_req(struct rtllib_network *beacon, + struct rtllib_device *ieee, + int challengelen, u8 *daddr) +{ + struct sk_buff *skb; + struct rtllib_authentication *auth; + int len; + + len = sizeof(struct rtllib_authentication) + challengelen + + ieee->tx_headroom + 4; + skb = dev_alloc_skb(len); + + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + auth = skb_put(skb, sizeof(struct rtllib_authentication)); + + auth->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_AUTH); + if (challengelen) + auth->header.frame_ctl |= cpu_to_le16(RTLLIB_FCTL_WEP); + + auth->header.duration_id = cpu_to_le16(0x013a); + ether_addr_copy(auth->header.addr1, beacon->bssid); + ether_addr_copy(auth->header.addr2, ieee->dev->dev_addr); + ether_addr_copy(auth->header.addr3, beacon->bssid); + if (ieee->auth_mode == 0) + auth->algorithm = WLAN_AUTH_OPEN; + else if (ieee->auth_mode == 1) + auth->algorithm = cpu_to_le16(WLAN_AUTH_SHARED_KEY); + else if (ieee->auth_mode == 2) + auth->algorithm = WLAN_AUTH_OPEN; + auth->transaction = cpu_to_le16(ieee->associate_seq); + ieee->associate_seq++; + + auth->status = cpu_to_le16(WLAN_STATUS_SUCCESS); + + return skb; +} + +static struct sk_buff *rtllib_probe_resp(struct rtllib_device *ieee, + const u8 *dest) +{ + u8 *tag; + int beacon_size; + struct rtllib_probe_response *beacon_buf; + struct sk_buff *skb = NULL; + int encrypt; + int atim_len, erp_len; + struct lib80211_crypt_data *crypt; + + char *ssid = ieee->current_network.ssid; + int ssid_len = ieee->current_network.ssid_len; + int rate_len = ieee->current_network.rates_len + 2; + int rate_ex_len = ieee->current_network.rates_ex_len; + int wpa_ie_len = ieee->wpa_ie_len; + u8 erpinfo_content = 0; + + u8 *tmp_ht_cap_buf = NULL; + u8 tmp_ht_cap_len = 0; + u8 *tmp_ht_info_buf = NULL; + u8 tmp_ht_info_len = 0; + struct rt_hi_throughput *ht_info = ieee->ht_info; + u8 *tmp_generic_ie_buf = NULL; + u8 tmp_generic_ie_len = 0; + + if (rate_ex_len > 0) + rate_ex_len += 2; + + if (ieee->current_network.capability & WLAN_CAPABILITY_IBSS) + atim_len = 4; + else + atim_len = 0; + + if ((ieee->current_network.mode == WIRELESS_MODE_G) || + (ieee->current_network.mode == WIRELESS_MODE_N_24G && + ieee->ht_info->bCurSuppCCK)) { + erp_len = 3; + erpinfo_content = 0; + if (ieee->current_network.buseprotection) + erpinfo_content |= ERP_UseProtection; + } else { + erp_len = 0; + } + + crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + encrypt = crypt && crypt->ops && + ((strcmp(crypt->ops->name, "R-WEP") == 0 || wpa_ie_len)); + if (ieee->ht_info->bCurrentHTSupport) { + tmp_ht_cap_buf = (u8 *)&(ieee->ht_info->SelfHTCap); + tmp_ht_cap_len = sizeof(ieee->ht_info->SelfHTCap); + tmp_ht_info_buf = (u8 *)&(ieee->ht_info->SelfHTInfo); + tmp_ht_info_len = sizeof(ieee->ht_info->SelfHTInfo); + HTConstructCapabilityElement(ieee, tmp_ht_cap_buf, + &tmp_ht_cap_len, encrypt, false); + HTConstructInfoElement(ieee, tmp_ht_info_buf, &tmp_ht_info_len, + encrypt); + + if (ht_info->reg_rt2rt_aggregation) { + tmp_generic_ie_buf = ieee->ht_info->sz_rt2rt_agg_buf; + tmp_generic_ie_len = + sizeof(ieee->ht_info->sz_rt2rt_agg_buf); + HTConstructRT2RTAggElement(ieee, tmp_generic_ie_buf, + &tmp_generic_ie_len); + } + } + + beacon_size = sizeof(struct rtllib_probe_response) + 2 + + ssid_len + 3 + rate_len + rate_ex_len + atim_len + erp_len + + wpa_ie_len + ieee->tx_headroom; + skb = dev_alloc_skb(beacon_size); + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + beacon_buf = skb_put(skb, (beacon_size - ieee->tx_headroom)); + ether_addr_copy(beacon_buf->header.addr1, dest); + ether_addr_copy(beacon_buf->header.addr2, ieee->dev->dev_addr); + ether_addr_copy(beacon_buf->header.addr3, ieee->current_network.bssid); + + beacon_buf->header.duration_id = 0; + beacon_buf->beacon_interval = + cpu_to_le16(ieee->current_network.beacon_interval); + beacon_buf->capability = + cpu_to_le16(ieee->current_network.capability & + WLAN_CAPABILITY_IBSS); + beacon_buf->capability |= + cpu_to_le16(ieee->current_network.capability & + WLAN_CAPABILITY_SHORT_PREAMBLE); + + if (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) + beacon_buf->capability |= + cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME); + + crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + if (encrypt) + beacon_buf->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + + beacon_buf->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_PROBE_RESP); + beacon_buf->info_element[0].id = MFIE_TYPE_SSID; + beacon_buf->info_element[0].len = ssid_len; + + tag = (u8 *)beacon_buf->info_element[0].data; + + memcpy(tag, ssid, ssid_len); + + tag += ssid_len; + + *(tag++) = MFIE_TYPE_RATES; + *(tag++) = rate_len - 2; + memcpy(tag, ieee->current_network.rates, rate_len - 2); + tag += rate_len - 2; + + *(tag++) = MFIE_TYPE_DS_SET; + *(tag++) = 1; + *(tag++) = ieee->current_network.channel; + + if (atim_len) { + u16 val16; + *(tag++) = MFIE_TYPE_IBSS_SET; + *(tag++) = 2; + val16 = ieee->current_network.atim_window; + memcpy((u8 *)tag, (u8 *)&val16, 2); + tag += 2; + } + + if (erp_len) { + *(tag++) = MFIE_TYPE_ERP; + *(tag++) = 1; + *(tag++) = erpinfo_content; + } + if (rate_ex_len) { + *(tag++) = MFIE_TYPE_RATES_EX; + *(tag++) = rate_ex_len - 2; + memcpy(tag, ieee->current_network.rates_ex, rate_ex_len - 2); + tag += rate_ex_len - 2; + } + + if (wpa_ie_len) { + if (ieee->iw_mode == IW_MODE_ADHOC) + memcpy(&ieee->wpa_ie[14], &ieee->wpa_ie[8], 4); + memcpy(tag, ieee->wpa_ie, ieee->wpa_ie_len); + tag += ieee->wpa_ie_len; + } + return skb; +} + +static struct sk_buff *rtllib_null_func(struct rtllib_device *ieee, short pwr) +{ + struct sk_buff *skb; + struct rtllib_hdr_3addr *hdr; + + skb = dev_alloc_skb(sizeof(struct rtllib_hdr_3addr) + ieee->tx_headroom); + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + hdr = skb_put(skb, sizeof(struct rtllib_hdr_3addr)); + + ether_addr_copy(hdr->addr1, ieee->current_network.bssid); + ether_addr_copy(hdr->addr2, ieee->dev->dev_addr); + ether_addr_copy(hdr->addr3, ieee->current_network.bssid); + + hdr->frame_ctl = cpu_to_le16(RTLLIB_FTYPE_DATA | + RTLLIB_STYPE_NULLFUNC | RTLLIB_FCTL_TODS | + (pwr ? RTLLIB_FCTL_PM : 0)); + + return skb; +} + +static struct sk_buff *rtllib_pspoll_func(struct rtllib_device *ieee) +{ + struct sk_buff *skb; + struct rtllib_pspoll_hdr *hdr; + + skb = dev_alloc_skb(sizeof(struct rtllib_pspoll_hdr) + ieee->tx_headroom); + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + hdr = skb_put(skb, sizeof(struct rtllib_pspoll_hdr)); + + ether_addr_copy(hdr->bssid, ieee->current_network.bssid); + ether_addr_copy(hdr->ta, ieee->dev->dev_addr); + + hdr->aid = cpu_to_le16(ieee->assoc_id | 0xc000); + hdr->frame_ctl = cpu_to_le16(RTLLIB_FTYPE_CTL | RTLLIB_STYPE_PSPOLL | + RTLLIB_FCTL_PM); + + return skb; +} + +static void rtllib_resp_to_probe(struct rtllib_device *ieee, u8 *dest) +{ + struct sk_buff *buf = rtllib_probe_resp(ieee, dest); + + if (buf) + softmac_mgmt_xmit(buf, ieee); +} + +static inline int SecIsInPMKIDList(struct rtllib_device *ieee, u8 *bssid) +{ + int i = 0; + + do { + if ((ieee->PMKIDList[i].bUsed) && + (memcmp(ieee->PMKIDList[i].Bssid, bssid, ETH_ALEN) == 0)) + break; + i++; + } while (i < NUM_PMKID_CACHE); + + if (i == NUM_PMKID_CACHE) + i = -1; + return i; +} + +static inline struct sk_buff * +rtllib_association_req(struct rtllib_network *beacon, + struct rtllib_device *ieee) +{ + struct sk_buff *skb; + struct rtllib_assoc_request_frame *hdr; + u8 *tag, *ies; + int i; + u8 *ht_cap_buf = NULL; + u8 ht_cap_len = 0; + u8 *realtek_ie_buf = NULL; + u8 realtek_ie_len = 0; + int wpa_ie_len = ieee->wpa_ie_len; + int wps_ie_len = ieee->wps_ie_len; + unsigned int ckip_ie_len = 0; + unsigned int ccxrm_ie_len = 0; + unsigned int cxvernum_ie_len = 0; + struct lib80211_crypt_data *crypt; + int encrypt; + int PMKCacheIdx; + + unsigned int rate_len = (beacon->rates_len ? + (beacon->rates_len + 2) : 0) + + (beacon->rates_ex_len ? (beacon->rates_ex_len) + + 2 : 0); + + unsigned int wmm_info_len = beacon->qos_data.supported ? 9 : 0; + unsigned int turbo_info_len = beacon->Turbo_Enable ? 9 : 0; + + int len = 0; + + crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + if (crypt != NULL) + encrypt = crypt && crypt->ops && + ((strcmp(crypt->ops->name, "R-WEP") == 0 || + wpa_ie_len)); + else + encrypt = 0; + + if ((ieee->rtllib_ap_sec_type && + (ieee->rtllib_ap_sec_type(ieee) & SEC_ALG_TKIP)) || + ieee->bForcedBgMode) { + ieee->ht_info->enable_ht = 0; + ieee->mode = WIRELESS_MODE_G; + } + + if (ieee->ht_info->bCurrentHTSupport && ieee->ht_info->enable_ht) { + ht_cap_buf = (u8 *)&(ieee->ht_info->SelfHTCap); + ht_cap_len = sizeof(ieee->ht_info->SelfHTCap); + HTConstructCapabilityElement(ieee, ht_cap_buf, &ht_cap_len, + encrypt, true); + if (ieee->ht_info->current_rt2rt_aggregation) { + realtek_ie_buf = ieee->ht_info->sz_rt2rt_agg_buf; + realtek_ie_len = + sizeof(ieee->ht_info->sz_rt2rt_agg_buf); + HTConstructRT2RTAggElement(ieee, realtek_ie_buf, + &realtek_ie_len); + } + } + + if (beacon->bCkipSupported) + ckip_ie_len = 30 + 2; + if (beacon->bCcxRmEnable) + ccxrm_ie_len = 6 + 2; + if (beacon->BssCcxVerNumber >= 2) + cxvernum_ie_len = 5 + 2; + + PMKCacheIdx = SecIsInPMKIDList(ieee, ieee->current_network.bssid); + if (PMKCacheIdx >= 0) { + wpa_ie_len += 18; + netdev_info(ieee->dev, "[PMK cache]: WPA2 IE length: %x\n", + wpa_ie_len); + } + len = sizeof(struct rtllib_assoc_request_frame) + 2 + + beacon->ssid_len + + rate_len + + wpa_ie_len + + wps_ie_len + + wmm_info_len + + turbo_info_len + + ht_cap_len + + realtek_ie_len + + ckip_ie_len + + ccxrm_ie_len + + cxvernum_ie_len + + ieee->tx_headroom; + + skb = dev_alloc_skb(len); + + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + hdr = skb_put(skb, sizeof(struct rtllib_assoc_request_frame) + 2); + + hdr->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_ASSOC_REQ); + hdr->header.duration_id = cpu_to_le16(37); + ether_addr_copy(hdr->header.addr1, beacon->bssid); + ether_addr_copy(hdr->header.addr2, ieee->dev->dev_addr); + ether_addr_copy(hdr->header.addr3, beacon->bssid); + + ether_addr_copy(ieee->ap_mac_addr, beacon->bssid); + + hdr->capability = cpu_to_le16(WLAN_CAPABILITY_ESS); + if (beacon->capability & WLAN_CAPABILITY_PRIVACY) + hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_PRIVACY); + + if (beacon->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE); + + if (beacon->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) + hdr->capability |= cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME); + + hdr->listen_interval = cpu_to_le16(beacon->listen_interval); + + hdr->info_element[0].id = MFIE_TYPE_SSID; + + hdr->info_element[0].len = beacon->ssid_len; + skb_put_data(skb, beacon->ssid, beacon->ssid_len); + + tag = skb_put(skb, rate_len); + + if (beacon->rates_len) { + *tag++ = MFIE_TYPE_RATES; + *tag++ = beacon->rates_len; + for (i = 0; i < beacon->rates_len; i++) + *tag++ = beacon->rates[i]; + } + + if (beacon->rates_ex_len) { + *tag++ = MFIE_TYPE_RATES_EX; + *tag++ = beacon->rates_ex_len; + for (i = 0; i < beacon->rates_ex_len; i++) + *tag++ = beacon->rates_ex[i]; + } + + if (beacon->bCkipSupported) { + static const u8 AironetIeOui[] = {0x00, 0x01, 0x66}; + u8 CcxAironetBuf[30]; + struct octet_string osCcxAironetIE; + + memset(CcxAironetBuf, 0, 30); + osCcxAironetIE.Octet = CcxAironetBuf; + osCcxAironetIE.Length = sizeof(CcxAironetBuf); + memcpy(osCcxAironetIE.Octet, AironetIeOui, + sizeof(AironetIeOui)); + + osCcxAironetIE.Octet[IE_CISCO_FLAG_POSITION] |= + (SUPPORT_CKIP_PK | SUPPORT_CKIP_MIC); + tag = skb_put(skb, ckip_ie_len); + *tag++ = MFIE_TYPE_AIRONET; + *tag++ = osCcxAironetIE.Length; + memcpy(tag, osCcxAironetIE.Octet, osCcxAironetIE.Length); + tag += osCcxAironetIE.Length; + } + + if (beacon->bCcxRmEnable) { + static const u8 CcxRmCapBuf[] = {0x00, 0x40, 0x96, 0x01, 0x01, + 0x00}; + struct octet_string osCcxRmCap; + + osCcxRmCap.Octet = (u8 *)CcxRmCapBuf; + osCcxRmCap.Length = sizeof(CcxRmCapBuf); + tag = skb_put(skb, ccxrm_ie_len); + *tag++ = MFIE_TYPE_GENERIC; + *tag++ = osCcxRmCap.Length; + memcpy(tag, osCcxRmCap.Octet, osCcxRmCap.Length); + tag += osCcxRmCap.Length; + } + + if (beacon->BssCcxVerNumber >= 2) { + u8 CcxVerNumBuf[] = {0x00, 0x40, 0x96, 0x03, 0x00}; + struct octet_string osCcxVerNum; + + CcxVerNumBuf[4] = beacon->BssCcxVerNumber; + osCcxVerNum.Octet = CcxVerNumBuf; + osCcxVerNum.Length = sizeof(CcxVerNumBuf); + tag = skb_put(skb, cxvernum_ie_len); + *tag++ = MFIE_TYPE_GENERIC; + *tag++ = osCcxVerNum.Length; + memcpy(tag, osCcxVerNum.Octet, osCcxVerNum.Length); + tag += osCcxVerNum.Length; + } + if (ieee->ht_info->bCurrentHTSupport && ieee->ht_info->enable_ht) { + if (ieee->ht_info->ePeerHTSpecVer != HT_SPEC_VER_EWC) { + tag = skb_put(skb, ht_cap_len); + *tag++ = MFIE_TYPE_HT_CAP; + *tag++ = ht_cap_len - 2; + memcpy(tag, ht_cap_buf, ht_cap_len - 2); + tag += ht_cap_len - 2; + } + } + + if (wpa_ie_len) { + skb_put_data(skb, ieee->wpa_ie, ieee->wpa_ie_len); + + if (PMKCacheIdx >= 0) { + tag = skb_put(skb, 18); + *tag = 1; + *(tag + 1) = 0; + memcpy((tag + 2), &ieee->PMKIDList[PMKCacheIdx].PMKID, + 16); + } + } + if (wmm_info_len) { + tag = skb_put(skb, wmm_info_len); + rtllib_WMM_Info(ieee, &tag); + } + + if (wps_ie_len && ieee->wps_ie) + skb_put_data(skb, ieee->wps_ie, wps_ie_len); + + if (turbo_info_len) { + tag = skb_put(skb, turbo_info_len); + rtllib_TURBO_Info(ieee, &tag); + } + + if (ieee->ht_info->bCurrentHTSupport && ieee->ht_info->enable_ht) { + if (ieee->ht_info->ePeerHTSpecVer == HT_SPEC_VER_EWC) { + tag = skb_put(skb, ht_cap_len); + *tag++ = MFIE_TYPE_GENERIC; + *tag++ = ht_cap_len - 2; + memcpy(tag, ht_cap_buf, ht_cap_len - 2); + tag += ht_cap_len - 2; + } + + if (ieee->ht_info->current_rt2rt_aggregation) { + tag = skb_put(skb, realtek_ie_len); + *tag++ = MFIE_TYPE_GENERIC; + *tag++ = realtek_ie_len - 2; + memcpy(tag, realtek_ie_buf, realtek_ie_len - 2); + } + } + + kfree(ieee->assocreq_ies); + ieee->assocreq_ies = NULL; + ies = &(hdr->info_element[0].id); + ieee->assocreq_ies_len = (skb->data + skb->len) - ies; + ieee->assocreq_ies = kmemdup(ies, ieee->assocreq_ies_len, GFP_ATOMIC); + if (!ieee->assocreq_ies) + ieee->assocreq_ies_len = 0; + + return skb; +} + +static void rtllib_associate_abort(struct rtllib_device *ieee) +{ + unsigned long flags; + + spin_lock_irqsave(&ieee->lock, flags); + + ieee->associate_seq++; + + /* don't scan, and avoid to have the RX path possibily + * try again to associate. Even do not react to AUTH or + * ASSOC response. Just wait for the retry wq to be scheduled. + * Here we will check if there are good nets to associate + * with, so we retry or just get back to NO_LINK and scanning + */ + if (ieee->link_state == RTLLIB_ASSOCIATING_AUTHENTICATING) { + netdev_dbg(ieee->dev, "Authentication failed\n"); + ieee->softmac_stats.no_auth_rs++; + } else { + netdev_dbg(ieee->dev, "Association failed\n"); + ieee->softmac_stats.no_ass_rs++; + } + + ieee->link_state = RTLLIB_ASSOCIATING_RETRY; + + schedule_delayed_work(&ieee->associate_retry_wq, + RTLLIB_SOFTMAC_ASSOC_RETRY_TIME); + + spin_unlock_irqrestore(&ieee->lock, flags); +} + +static void rtllib_associate_abort_cb(struct timer_list *t) +{ + struct rtllib_device *dev = from_timer(dev, t, associate_timer); + + rtllib_associate_abort(dev); +} + +static void rtllib_associate_step1(struct rtllib_device *ieee, u8 *daddr) +{ + struct rtllib_network *beacon = &ieee->current_network; + struct sk_buff *skb; + + netdev_dbg(ieee->dev, "Stopping scan\n"); + + ieee->softmac_stats.tx_auth_rq++; + + skb = rtllib_authentication_req(beacon, ieee, 0, daddr); + + if (!skb) { + rtllib_associate_abort(ieee); + } else { + ieee->link_state = RTLLIB_ASSOCIATING_AUTHENTICATING; + netdev_dbg(ieee->dev, "Sending authentication request\n"); + softmac_mgmt_xmit(skb, ieee); + if (!timer_pending(&ieee->associate_timer)) { + ieee->associate_timer.expires = jiffies + (HZ / 2); + add_timer(&ieee->associate_timer); + } + } +} + +static void rtllib_auth_challenge(struct rtllib_device *ieee, u8 *challenge, + int chlen) +{ + u8 *c; + struct sk_buff *skb; + struct rtllib_network *beacon = &ieee->current_network; + + ieee->associate_seq++; + ieee->softmac_stats.tx_auth_rq++; + + skb = rtllib_authentication_req(beacon, ieee, chlen + 2, beacon->bssid); + + if (!skb) { + rtllib_associate_abort(ieee); + } else { + c = skb_put(skb, chlen + 2); + *(c++) = MFIE_TYPE_CHALLENGE; + *(c++) = chlen; + memcpy(c, challenge, chlen); + + netdev_dbg(ieee->dev, + "Sending authentication challenge response\n"); + + rtllib_encrypt_fragment(ieee, skb, + sizeof(struct rtllib_hdr_3addr)); + + softmac_mgmt_xmit(skb, ieee); + mod_timer(&ieee->associate_timer, jiffies + (HZ / 2)); + } + kfree(challenge); +} + +static void rtllib_associate_step2(struct rtllib_device *ieee) +{ + struct sk_buff *skb; + struct rtllib_network *beacon = &ieee->current_network; + + del_timer_sync(&ieee->associate_timer); + + netdev_dbg(ieee->dev, "Sending association request\n"); + + ieee->softmac_stats.tx_ass_rq++; + skb = rtllib_association_req(beacon, ieee); + if (!skb) { + rtllib_associate_abort(ieee); + } else { + softmac_mgmt_xmit(skb, ieee); + mod_timer(&ieee->associate_timer, jiffies + (HZ / 2)); + } +} + +static void rtllib_associate_complete_wq(void *data) +{ + struct rtllib_device *ieee = (struct rtllib_device *) + container_of(data, + struct rtllib_device, + associate_complete_wq); + struct rt_pwr_save_ctrl *psc = &ieee->pwr_save_ctrl; + + netdev_info(ieee->dev, "Associated successfully with %pM\n", + ieee->current_network.bssid); + if (!ieee->is_silent_reset) { + netdev_info(ieee->dev, "normal associate\n"); + notify_wx_assoc_event(ieee); + } + + netif_carrier_on(ieee->dev); + ieee->is_roaming = false; + if (rtllib_is_54g(&ieee->current_network)) { + ieee->rate = 108; + netdev_info(ieee->dev, "Using G rates:%d\n", ieee->rate); + } else { + ieee->rate = 22; + ieee->set_wireless_mode(ieee->dev, WIRELESS_MODE_B); + netdev_info(ieee->dev, "Using B rates:%d\n", ieee->rate); + } + if (ieee->ht_info->bCurrentHTSupport && ieee->ht_info->enable_ht) { + netdev_info(ieee->dev, "Successfully associated, ht enabled\n"); + HTOnAssocRsp(ieee); + } else { + netdev_info(ieee->dev, + "Successfully associated, ht not enabled(%d, %d)\n", + ieee->ht_info->bCurrentHTSupport, + ieee->ht_info->enable_ht); + memset(ieee->dot11ht_oper_rate_set, 0, 16); + } + ieee->link_detect_info.SlotNum = 2 * (1 + + ieee->current_network.beacon_interval / + 500); + if (ieee->link_detect_info.NumRecvBcnInPeriod == 0 || + ieee->link_detect_info.NumRecvDataInPeriod == 0) { + ieee->link_detect_info.NumRecvBcnInPeriod = 1; + ieee->link_detect_info.NumRecvDataInPeriod = 1; + } + psc->LpsIdleCount = 0; + ieee->link_change(ieee->dev); + + if (ieee->is_silent_reset) { + netdev_info(ieee->dev, "silent reset associate\n"); + ieee->is_silent_reset = false; + } +} + +static void rtllib_sta_send_associnfo(struct rtllib_device *ieee) +{ +} + +static void rtllib_associate_complete(struct rtllib_device *ieee) +{ + del_timer_sync(&ieee->associate_timer); + + ieee->link_state = MAC80211_LINKED; + rtllib_sta_send_associnfo(ieee); + + schedule_work(&ieee->associate_complete_wq); +} + +static void rtllib_associate_procedure_wq(void *data) +{ + struct rtllib_device *ieee = container_of_dwork_rsl(data, + struct rtllib_device, + associate_procedure_wq); + rtllib_stop_scan_syncro(ieee); + ieee->rtllib_ips_leave(ieee->dev); + mutex_lock(&ieee->wx_mutex); + + rtllib_stop_scan(ieee); + HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT); + if (ieee->rf_power_state == rf_off) { + ieee->rtllib_ips_leave_wq(ieee->dev); + mutex_unlock(&ieee->wx_mutex); + return; + } + ieee->associate_seq = 1; + + rtllib_associate_step1(ieee, ieee->current_network.bssid); + + mutex_unlock(&ieee->wx_mutex); +} + +inline void rtllib_softmac_new_net(struct rtllib_device *ieee, + struct rtllib_network *net) +{ + u8 tmp_ssid[IW_ESSID_MAX_SIZE + 1]; + int tmp_ssid_len = 0; + + short apset, ssidset, ssidbroad, apmatch, ssidmatch; + + /* we are interested in new only if we are not associated + * and we are not associating / authenticating + */ + if (ieee->link_state != MAC80211_NOLINK) + return; + + if ((ieee->iw_mode == IW_MODE_INFRA) && !(net->capability & + WLAN_CAPABILITY_ESS)) + return; + + if ((ieee->iw_mode == IW_MODE_ADHOC) && !(net->capability & + WLAN_CAPABILITY_IBSS)) + return; + + if ((ieee->iw_mode == IW_MODE_ADHOC) && + (net->channel > ieee->ibss_maxjoin_chal)) + return; + if (ieee->iw_mode == IW_MODE_INFRA || ieee->iw_mode == IW_MODE_ADHOC) { + /* if the user specified the AP MAC, we need also the essid + * This could be obtained by beacons or, if the network does not + * broadcast it, it can be put manually. + */ + apset = ieee->wap_set; + ssidset = ieee->ssid_set; + ssidbroad = !(net->ssid_len == 0 || net->ssid[0] == '\0'); + apmatch = (memcmp(ieee->current_network.bssid, net->bssid, + ETH_ALEN) == 0); + if (!ssidbroad) { + ssidmatch = (ieee->current_network.ssid_len == + net->hidden_ssid_len) && + (!strncmp(ieee->current_network.ssid, + net->hidden_ssid, net->hidden_ssid_len)); + if (net->hidden_ssid_len > 0) { + strncpy(net->ssid, net->hidden_ssid, + net->hidden_ssid_len); + net->ssid_len = net->hidden_ssid_len; + ssidbroad = 1; + } + } else { + ssidmatch = + (ieee->current_network.ssid_len == net->ssid_len) && + (!strncmp(ieee->current_network.ssid, net->ssid, + net->ssid_len)); + } + + /* if the user set the AP check if match. + * if the network does not broadcast essid we check the + * user supplied ANY essid + * if the network does broadcast and the user does not set + * essid it is OK + * if the network does broadcast and the user did set essid + * check if essid match + * if the ap is not set, check that the user set the bssid + * and the network does broadcast and that those two bssid match + */ + if ((apset && apmatch && + ((ssidset && ssidbroad && ssidmatch) || + (ssidbroad && !ssidset) || (!ssidbroad && ssidset))) || + (!apset && ssidset && ssidbroad && ssidmatch) || + (ieee->is_roaming && ssidset && ssidbroad && ssidmatch)) { + /* Save the essid so that if it is hidden, it is + * replaced with the essid provided by the user. + */ + if (!ssidbroad) { + memcpy(tmp_ssid, ieee->current_network.ssid, + ieee->current_network.ssid_len); + tmp_ssid_len = ieee->current_network.ssid_len; + } + memcpy(&ieee->current_network, net, + sizeof(ieee->current_network)); + if (!ssidbroad) { + memcpy(ieee->current_network.ssid, tmp_ssid, + tmp_ssid_len); + ieee->current_network.ssid_len = tmp_ssid_len; + } + netdev_info(ieee->dev, + "Linking with %s,channel:%d, qos:%d, myHT:%d, networkHT:%d, mode:%x cur_net.flags:0x%x\n", + ieee->current_network.ssid, + ieee->current_network.channel, + ieee->current_network.qos_data.supported, + ieee->ht_info->enable_ht, + ieee->current_network.bssht.bd_support_ht, + ieee->current_network.mode, + ieee->current_network.flags); + + if ((rtllib_act_scanning(ieee, false)) && + !(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) + rtllib_stop_scan_syncro(ieee); + + HTResetIOTSetting(ieee->ht_info); + ieee->wmm_acm = 0; + if (ieee->iw_mode == IW_MODE_INFRA) { + /* Join the network for the first time */ + ieee->AsocRetryCount = 0; + if ((ieee->current_network.qos_data.supported == 1) && + ieee->current_network.bssht.bd_support_ht) + HTResetSelfAndSavePeerSetting(ieee, + &(ieee->current_network)); + else + ieee->ht_info->bCurrentHTSupport = + false; + + ieee->link_state = RTLLIB_ASSOCIATING; + schedule_delayed_work( + &ieee->associate_procedure_wq, 0); + } else { + if (rtllib_is_54g(&ieee->current_network)) { + ieee->rate = 108; + ieee->set_wireless_mode(ieee->dev, WIRELESS_MODE_G); + netdev_info(ieee->dev, + "Using G rates\n"); + } else { + ieee->rate = 22; + ieee->set_wireless_mode(ieee->dev, WIRELESS_MODE_B); + netdev_info(ieee->dev, + "Using B rates\n"); + } + memset(ieee->dot11ht_oper_rate_set, 0, 16); + ieee->link_state = MAC80211_LINKED; + } + } + } +} + +static void rtllib_softmac_check_all_nets(struct rtllib_device *ieee) +{ + unsigned long flags; + struct rtllib_network *target; + + spin_lock_irqsave(&ieee->lock, flags); + + list_for_each_entry(target, &ieee->network_list, list) { + /* if the state become different that NOLINK means + * we had found what we are searching for + */ + + if (ieee->link_state != MAC80211_NOLINK) + break; + + if (ieee->scan_age == 0 || time_after(target->last_scanned + + ieee->scan_age, jiffies)) + rtllib_softmac_new_net(ieee, target); + } + spin_unlock_irqrestore(&ieee->lock, flags); +} + +static inline int auth_parse(struct net_device *dev, struct sk_buff *skb, + u8 **challenge, int *chlen) +{ + struct rtllib_authentication *a; + u8 *t; + + if (skb->len < (sizeof(struct rtllib_authentication) - + sizeof(struct rtllib_info_element))) { + netdev_dbg(dev, "invalid len in auth resp: %d\n", skb->len); + return -EINVAL; + } + *challenge = NULL; + a = (struct rtllib_authentication *)skb->data; + if (skb->len > (sizeof(struct rtllib_authentication) + 3)) { + t = skb->data + sizeof(struct rtllib_authentication); + + if (*(t++) == MFIE_TYPE_CHALLENGE) { + *chlen = *(t++); + *challenge = kmemdup(t, *chlen, GFP_ATOMIC); + if (!*challenge) + return -ENOMEM; + } + } + + if (a->status) { + netdev_dbg(dev, "auth_parse() failed\n"); + return -EINVAL; + } + + return 0; +} + +static short probe_rq_parse(struct rtllib_device *ieee, struct sk_buff *skb, + u8 *src) +{ + u8 *tag; + u8 *skbend; + u8 *ssid = NULL; + u8 ssidlen = 0; + struct rtllib_hdr_3addr *header = + (struct rtllib_hdr_3addr *)skb->data; + bool bssid_match; + + if (skb->len < sizeof(struct rtllib_hdr_3addr)) + return -1; /* corrupted */ + + bssid_match = + (!ether_addr_equal(header->addr3, ieee->current_network.bssid)) && + (!is_broadcast_ether_addr(header->addr3)); + if (bssid_match) + return -1; + + ether_addr_copy(src, header->addr2); + + skbend = (u8 *)skb->data + skb->len; + + tag = skb->data + sizeof(struct rtllib_hdr_3addr); + + while (tag + 1 < skbend) { + if (*tag == 0) { + ssid = tag + 2; + ssidlen = *(tag + 1); + break; + } + tag++; /* point to the len field */ + tag = tag + *(tag); /* point to the last data byte of the tag */ + tag++; /* point to the next tag */ + } + + if (ssidlen == 0) + return 1; + + if (!ssid) + return 1; /* ssid not found in tagged param */ + + return !strncmp(ssid, ieee->current_network.ssid, ssidlen); +} + +static inline u16 assoc_parse(struct rtllib_device *ieee, struct sk_buff *skb, + int *aid) +{ + struct rtllib_assoc_response_frame *response_head; + u16 status_code; + + if (skb->len < sizeof(struct rtllib_assoc_response_frame)) { + netdev_dbg(ieee->dev, "Invalid len in auth resp: %d\n", + skb->len); + return 0xcafe; + } + + response_head = (struct rtllib_assoc_response_frame *)skb->data; + *aid = le16_to_cpu(response_head->aid) & 0x3fff; + + status_code = le16_to_cpu(response_head->status); + if ((status_code == WLAN_STATUS_ASSOC_DENIED_RATES || + status_code == WLAN_STATUS_CAPS_UNSUPPORTED) && + ((ieee->mode == WIRELESS_MODE_G) && + (ieee->current_network.mode == WIRELESS_MODE_N_24G) && + (ieee->AsocRetryCount++ < (RT_ASOC_RETRY_LIMIT - 1)))) { + ieee->ht_info->iot_action |= HT_IOT_ACT_PURE_N_MODE; + } else { + ieee->AsocRetryCount = 0; + } + + return le16_to_cpu(response_head->status); +} + +void rtllib_rx_probe_rq(struct rtllib_device *ieee, struct sk_buff *skb) +{ + u8 dest[ETH_ALEN]; + + ieee->softmac_stats.rx_probe_rq++; + if (probe_rq_parse(ieee, skb, dest) > 0) { + ieee->softmac_stats.tx_probe_rs++; + rtllib_resp_to_probe(ieee, dest); + } +} + +void rtllib_sta_ps_send_null_frame(struct rtllib_device *ieee, short pwr) +{ + struct sk_buff *buf = rtllib_null_func(ieee, pwr); + + if (buf) + softmac_ps_mgmt_xmit(buf, ieee); +} +EXPORT_SYMBOL(rtllib_sta_ps_send_null_frame); + +void rtllib_sta_ps_send_pspoll_frame(struct rtllib_device *ieee) +{ + struct sk_buff *buf = rtllib_pspoll_func(ieee); + + if (buf) + softmac_ps_mgmt_xmit(buf, ieee); +} + +static short rtllib_sta_ps_sleep(struct rtllib_device *ieee, u64 *time) +{ + int timeout; + u8 dtim; + struct rt_pwr_save_ctrl *psc = &ieee->pwr_save_ctrl; + + if (ieee->LPSDelayCnt) { + ieee->LPSDelayCnt--; + return 0; + } + + dtim = ieee->current_network.dtim_data; + if (!(dtim & RTLLIB_DTIM_VALID)) + return 0; + timeout = ieee->current_network.beacon_interval; + ieee->current_network.dtim_data = RTLLIB_DTIM_INVALID; + /* there's no need to nofity AP that I find you buffered + * with broadcast packet + */ + if (dtim & (RTLLIB_DTIM_UCAST & ieee->ps)) + return 2; + + if (!time_after(jiffies, + dev_trans_start(ieee->dev) + msecs_to_jiffies(timeout))) + return 0; + if (!time_after(jiffies, + ieee->last_rx_ps_time + msecs_to_jiffies(timeout))) + return 0; + if ((ieee->softmac_features & IEEE_SOFTMAC_SINGLE_QUEUE) && + (ieee->mgmt_queue_tail != ieee->mgmt_queue_head)) + return 0; + + if (time) { + if (ieee->bAwakePktSent) { + psc->LPSAwakeIntvl = 1; + } else { + u8 MaxPeriod = 1; + + if (psc->LPSAwakeIntvl == 0) + psc->LPSAwakeIntvl = 1; + if (psc->reg_max_lps_awake_intvl == 0) + MaxPeriod = 1; + else if (psc->reg_max_lps_awake_intvl == 0xFF) + MaxPeriod = ieee->current_network.dtim_period; + else + MaxPeriod = psc->reg_max_lps_awake_intvl; + psc->LPSAwakeIntvl = (psc->LPSAwakeIntvl >= + MaxPeriod) ? MaxPeriod : + (psc->LPSAwakeIntvl + 1); + } + { + u8 LPSAwakeIntvl_tmp = 0; + u8 period = ieee->current_network.dtim_period; + u8 count = ieee->current_network.tim.tim_count; + + if (count == 0) { + if (psc->LPSAwakeIntvl > period) + LPSAwakeIntvl_tmp = period + + (psc->LPSAwakeIntvl - + period) - + ((psc->LPSAwakeIntvl - period) % + period); + else + LPSAwakeIntvl_tmp = psc->LPSAwakeIntvl; + + } else { + if (psc->LPSAwakeIntvl > + ieee->current_network.tim.tim_count) + LPSAwakeIntvl_tmp = count + + (psc->LPSAwakeIntvl - count) - + ((psc->LPSAwakeIntvl - count) % period); + else + LPSAwakeIntvl_tmp = psc->LPSAwakeIntvl; + } + + *time = ieee->current_network.last_dtim_sta_time + + msecs_to_jiffies(ieee->current_network.beacon_interval * + LPSAwakeIntvl_tmp); + } + } + + return 1; +} + +static inline void rtllib_sta_ps(struct work_struct *work) +{ + struct rtllib_device *ieee; + u64 time; + short sleep; + unsigned long flags, flags2; + + ieee = container_of(work, struct rtllib_device, ps_task); + + spin_lock_irqsave(&ieee->lock, flags); + + if ((ieee->ps == RTLLIB_PS_DISABLED || + ieee->iw_mode != IW_MODE_INFRA || + ieee->link_state != MAC80211_LINKED)) { + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + rtllib_sta_wakeup(ieee, 1); + + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + } + sleep = rtllib_sta_ps_sleep(ieee, &time); + /* 2 wake, 1 sleep, 0 do nothing */ + if (sleep == 0) + goto out; + if (sleep == 1) { + if (ieee->sta_sleep == LPS_IS_SLEEP) { + ieee->enter_sleep_state(ieee->dev, time); + } else if (ieee->sta_sleep == LPS_IS_WAKE) { + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + + if (ieee->ps_is_queue_empty(ieee->dev)) { + ieee->sta_sleep = LPS_WAIT_NULL_DATA_SEND; + ieee->ack_tx_to_ieee = 1; + rtllib_sta_ps_send_null_frame(ieee, 1); + ieee->ps_time = time; + } + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + } + + ieee->bAwakePktSent = false; + + } else if (sleep == 2) { + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + + rtllib_sta_wakeup(ieee, 1); + + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + } + +out: + spin_unlock_irqrestore(&ieee->lock, flags); +} + +static void rtllib_sta_wakeup(struct rtllib_device *ieee, short nl) +{ + if (ieee->sta_sleep == LPS_IS_WAKE) { + if (nl) { + if (ieee->ht_info->iot_action & + HT_IOT_ACT_NULL_DATA_POWER_SAVING) { + ieee->ack_tx_to_ieee = 1; + rtllib_sta_ps_send_null_frame(ieee, 0); + } else { + ieee->ack_tx_to_ieee = 1; + rtllib_sta_ps_send_pspoll_frame(ieee); + } + } + return; + } + + if (ieee->sta_sleep == LPS_IS_SLEEP) + ieee->sta_wake_up(ieee->dev); + if (nl) { + if (ieee->ht_info->iot_action & + HT_IOT_ACT_NULL_DATA_POWER_SAVING) { + ieee->ack_tx_to_ieee = 1; + rtllib_sta_ps_send_null_frame(ieee, 0); + } else { + ieee->ack_tx_to_ieee = 1; + ieee->polling = true; + rtllib_sta_ps_send_pspoll_frame(ieee); + } + + } else { + ieee->sta_sleep = LPS_IS_WAKE; + ieee->polling = false; + } +} + +void rtllib_ps_tx_ack(struct rtllib_device *ieee, short success) +{ + unsigned long flags, flags2; + + spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->sta_sleep == LPS_WAIT_NULL_DATA_SEND) { + /* Null frame with PS bit set */ + if (success) { + ieee->sta_sleep = LPS_IS_SLEEP; + ieee->enter_sleep_state(ieee->dev, ieee->ps_time); + } + /* if the card report not success we can't be sure the AP + * has not RXed so we can't assume the AP believe us awake + */ + } else {/* 21112005 - tx again null without PS bit if lost */ + + if ((ieee->sta_sleep == LPS_IS_WAKE) && !success) { + spin_lock_irqsave(&ieee->mgmt_tx_lock, flags2); + if (ieee->ht_info->iot_action & + HT_IOT_ACT_NULL_DATA_POWER_SAVING) + rtllib_sta_ps_send_null_frame(ieee, 0); + else + rtllib_sta_ps_send_pspoll_frame(ieee); + spin_unlock_irqrestore(&ieee->mgmt_tx_lock, flags2); + } + } + spin_unlock_irqrestore(&ieee->lock, flags); +} +EXPORT_SYMBOL(rtllib_ps_tx_ack); + +static void rtllib_process_action(struct rtllib_device *ieee, + struct sk_buff *skb) +{ + struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *)skb->data; + u8 *act = rtllib_get_payload((struct rtllib_hdr *)header); + u8 category = 0; + + if (act == NULL) { + netdev_warn(ieee->dev, + "Error getting payload of action frame\n"); + return; + } + + category = *act; + act++; + switch (category) { + case ACT_CAT_BA: + switch (*act) { + case ACT_ADDBAREQ: + rtllib_rx_ADDBAReq(ieee, skb); + break; + case ACT_ADDBARSP: + rtllib_rx_ADDBARsp(ieee, skb); + break; + case ACT_DELBA: + rtllib_rx_DELBA(ieee, skb); + break; + } + break; + default: + break; + } +} + +static inline int +rtllib_rx_assoc_resp(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats) +{ + u16 errcode; + int aid; + u8 *ies; + struct rtllib_assoc_response_frame *assoc_resp; + struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *)skb->data; + u16 frame_ctl = le16_to_cpu(header->frame_ctl); + + netdev_dbg(ieee->dev, "received [RE]ASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(frame_ctl)); + + if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) && + ieee->link_state == RTLLIB_ASSOCIATING_AUTHENTICATED && + (ieee->iw_mode == IW_MODE_INFRA)) { + errcode = assoc_parse(ieee, skb, &aid); + if (!errcode) { + struct rtllib_network *network = + kzalloc(sizeof(struct rtllib_network), + GFP_ATOMIC); + + if (!network) + return 1; + ieee->link_state = MAC80211_LINKED; + ieee->assoc_id = aid; + ieee->softmac_stats.rx_ass_ok++; + /* station support qos */ + /* Let the register setting default with Legacy station */ + assoc_resp = (struct rtllib_assoc_response_frame *)skb->data; + if (ieee->current_network.qos_data.supported == 1) { + if (rtllib_parse_info_param(ieee, assoc_resp->info_element, + rx_stats->len - sizeof(*assoc_resp), + network, rx_stats)) { + kfree(network); + return 1; + } + memcpy(ieee->ht_info->PeerHTCapBuf, + network->bssht.bd_ht_cap_buf, + network->bssht.bd_ht_cap_len); + memcpy(ieee->ht_info->PeerHTInfoBuf, + network->bssht.bd_ht_info_buf, + network->bssht.bd_ht_info_len); + ieee->handle_assoc_response(ieee->dev, + (struct rtllib_assoc_response_frame *)header, network); + } + kfree(network); + + kfree(ieee->assocresp_ies); + ieee->assocresp_ies = NULL; + ies = &(assoc_resp->info_element[0].id); + ieee->assocresp_ies_len = (skb->data + skb->len) - ies; + ieee->assocresp_ies = kmemdup(ies, + ieee->assocresp_ies_len, + GFP_ATOMIC); + if (!ieee->assocresp_ies) + ieee->assocresp_ies_len = 0; + + rtllib_associate_complete(ieee); + } else { + /* aid could not been allocated */ + ieee->softmac_stats.rx_ass_err++; + netdev_info(ieee->dev, + "Association response status code 0x%x\n", + errcode); + if (ieee->AsocRetryCount < RT_ASOC_RETRY_LIMIT) + schedule_delayed_work( + &ieee->associate_procedure_wq, 0); + else + rtllib_associate_abort(ieee); + } + } + return 0; +} + +static void rtllib_rx_auth_resp(struct rtllib_device *ieee, struct sk_buff *skb) +{ + int errcode; + u8 *challenge; + int chlen = 0; + bool bSupportNmode = true, bHalfSupportNmode = false; + + errcode = auth_parse(ieee->dev, skb, &challenge, &chlen); + + if (errcode) { + ieee->softmac_stats.rx_auth_rs_err++; + netdev_info(ieee->dev, + "Authentication response status code %d", errcode); + rtllib_associate_abort(ieee); + return; + } + + if (ieee->open_wep || !challenge) { + ieee->link_state = RTLLIB_ASSOCIATING_AUTHENTICATED; + ieee->softmac_stats.rx_auth_rs_ok++; + if (!(ieee->ht_info->iot_action & HT_IOT_ACT_PURE_N_MODE)) { + if (!ieee->GetNmodeSupportBySecCfg(ieee->dev)) { + if (IsHTHalfNmodeAPs(ieee)) { + bSupportNmode = true; + bHalfSupportNmode = true; + } else { + bSupportNmode = false; + bHalfSupportNmode = false; + } + } + } + /* Dummy wirless mode setting to avoid encryption issue */ + if (bSupportNmode) { + ieee->set_wireless_mode(ieee->dev, + ieee->current_network.mode); + } else { + /*TODO*/ + ieee->set_wireless_mode(ieee->dev, WIRELESS_MODE_G); + } + + if ((ieee->current_network.mode == WIRELESS_MODE_N_24G) && + bHalfSupportNmode) { + netdev_info(ieee->dev, "======>enter half N mode\n"); + ieee->bHalfWirelessN24GMode = true; + } else { + ieee->bHalfWirelessN24GMode = false; + } + rtllib_associate_step2(ieee); + } else { + rtllib_auth_challenge(ieee, challenge, chlen); + } +} + +static inline int +rtllib_rx_auth(struct rtllib_device *ieee, struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats) +{ + if (ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) { + if (ieee->link_state == RTLLIB_ASSOCIATING_AUTHENTICATING && + (ieee->iw_mode == IW_MODE_INFRA)) { + netdev_dbg(ieee->dev, + "Received authentication response"); + rtllib_rx_auth_resp(ieee, skb); + } + } + return 0; +} + +static inline int +rtllib_rx_deauth(struct rtllib_device *ieee, struct sk_buff *skb) +{ + struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *)skb->data; + u16 frame_ctl; + + if (memcmp(header->addr3, ieee->current_network.bssid, ETH_ALEN) != 0) + return 0; + + /* FIXME for now repeat all the association procedure + * both for disassociation and deauthentication + */ + if ((ieee->softmac_features & IEEE_SOFTMAC_ASSOCIATE) && + ieee->link_state == MAC80211_LINKED && + (ieee->iw_mode == IW_MODE_INFRA)) { + frame_ctl = le16_to_cpu(header->frame_ctl); + netdev_info(ieee->dev, + "==========>received disassoc/deauth(%x) frame, reason code:%x\n", + WLAN_FC_GET_STYPE(frame_ctl), + ((struct rtllib_disassoc *)skb->data)->reason); + ieee->link_state = RTLLIB_ASSOCIATING; + ieee->softmac_stats.reassoc++; + ieee->is_roaming = true; + ieee->link_detect_info.bBusyTraffic = false; + rtllib_disassociate(ieee); + RemovePeerTS(ieee, header->addr2); + if (!(ieee->rtllib_ap_sec_type(ieee) & + (SEC_ALG_CCMP | SEC_ALG_TKIP))) + schedule_delayed_work( + &ieee->associate_procedure_wq, 5); + } + return 0; +} + +inline int rtllib_rx_frame_softmac(struct rtllib_device *ieee, + struct sk_buff *skb, + struct rtllib_rx_stats *rx_stats, u16 type, + u16 stype) +{ + struct rtllib_hdr_3addr *header = (struct rtllib_hdr_3addr *)skb->data; + u16 frame_ctl; + + if (!ieee->proto_started) + return 0; + + frame_ctl = le16_to_cpu(header->frame_ctl); + switch (WLAN_FC_GET_STYPE(frame_ctl)) { + case RTLLIB_STYPE_ASSOC_RESP: + case RTLLIB_STYPE_REASSOC_RESP: + if (rtllib_rx_assoc_resp(ieee, skb, rx_stats) == 1) + return 1; + break; + case RTLLIB_STYPE_ASSOC_REQ: + case RTLLIB_STYPE_REASSOC_REQ: + break; + case RTLLIB_STYPE_AUTH: + rtllib_rx_auth(ieee, skb, rx_stats); + break; + case RTLLIB_STYPE_DISASSOC: + case RTLLIB_STYPE_DEAUTH: + rtllib_rx_deauth(ieee, skb); + break; + case RTLLIB_STYPE_MANAGE_ACT: + rtllib_process_action(ieee, skb); + break; + default: + return -1; + } + return 0; +} + +/* following are for a simpler TX queue management. + * Instead of using netif_[stop/wake]_queue the driver + * will use these two functions (plus a reset one), that + * will internally use the kernel netif_* and takes + * care of the ieee802.11 fragmentation. + * So the driver receives a fragment per time and might + * call the stop function when it wants to not + * have enough room to TX an entire packet. + * This might be useful if each fragment needs it's own + * descriptor, thus just keep a total free memory > than + * the max fragmentation threshold is not enough.. If the + * ieee802.11 stack passed a TXB struct then you need + * to keep N free descriptors where + * N = MAX_PACKET_SIZE / MIN_FRAG_TRESHOLD + * In this way you need just one and the 802.11 stack + * will take care of buffering fragments and pass them to + * the driver later, when it wakes the queue. + */ +void rtllib_softmac_xmit(struct rtllib_txb *txb, struct rtllib_device *ieee) +{ + unsigned int queue_index = txb->queue_index; + unsigned long flags; + int i; + struct cb_desc *tcb_desc = NULL; + unsigned long queue_len = 0; + + spin_lock_irqsave(&ieee->lock, flags); + + /* called with 2nd parm 0, no tx mgmt lock required */ + rtllib_sta_wakeup(ieee, 0); + + /* update the tx status */ + tcb_desc = (struct cb_desc *)(txb->fragments[0]->cb + + MAX_DEV_ADDR_SIZE); + if (tcb_desc->bMulticast) + ieee->stats.multicast++; + + /* if xmit available, just xmit it immediately, else just insert it to + * the wait queue + */ + for (i = 0; i < txb->nr_frags; i++) { + queue_len = skb_queue_len(&ieee->skb_waitQ[queue_index]); + if ((queue_len != 0) || + (!ieee->check_nic_enough_desc(ieee->dev, queue_index)) || + (ieee->queue_stop)) { + /* insert the skb packet to the wait queue + * as for the completion function, it does not need + * to check it any more. + */ + if (queue_len < 200) + skb_queue_tail(&ieee->skb_waitQ[queue_index], + txb->fragments[i]); + else + kfree_skb(txb->fragments[i]); + } else { + ieee->softmac_data_hard_start_xmit( + txb->fragments[i], + ieee->dev, ieee->rate); + } + } + + rtllib_txb_free(txb); + + spin_unlock_irqrestore(&ieee->lock, flags); +} + +void rtllib_reset_queue(struct rtllib_device *ieee) +{ + unsigned long flags; + + spin_lock_irqsave(&ieee->lock, flags); + init_mgmt_queue(ieee); + if (ieee->tx_pending.txb) { + rtllib_txb_free(ieee->tx_pending.txb); + ieee->tx_pending.txb = NULL; + } + ieee->queue_stop = 0; + spin_unlock_irqrestore(&ieee->lock, flags); +} +EXPORT_SYMBOL(rtllib_reset_queue); + +void rtllib_stop_all_queues(struct rtllib_device *ieee) +{ + unsigned int i; + + for (i = 0; i < ieee->dev->num_tx_queues; i++) + txq_trans_cond_update(netdev_get_tx_queue(ieee->dev, i)); + + netif_tx_stop_all_queues(ieee->dev); +} + +void rtllib_wake_all_queues(struct rtllib_device *ieee) +{ + netif_tx_wake_all_queues(ieee->dev); +} + +static void rtllib_start_monitor_mode(struct rtllib_device *ieee) +{ + /* reset hardware status */ + if (ieee->raw_tx) + netif_carrier_on(ieee->dev); +} + +static void rtllib_start_ibss_wq(void *data) +{ + struct rtllib_device *ieee = container_of_dwork_rsl(data, + struct rtllib_device, start_ibss_wq); + /* iwconfig mode ad-hoc will schedule this and return + * on the other hand this will block further iwconfig SET + * operations because of the wx_mutex hold. + * Anyway some most set operations set a flag to speed-up + * (abort) this wq (when syncro scanning) before sleeping + * on the mutex + */ + if (!ieee->proto_started) { + netdev_info(ieee->dev, "==========oh driver down return\n"); + return; + } + mutex_lock(&ieee->wx_mutex); + + if (ieee->current_network.ssid_len == 0) { + strscpy(ieee->current_network.ssid, RTLLIB_DEFAULT_TX_ESSID, + sizeof(ieee->current_network.ssid)); + ieee->current_network.ssid_len = strlen(RTLLIB_DEFAULT_TX_ESSID); + ieee->ssid_set = 1; + } + + ieee->link_state = MAC80211_NOLINK; + ieee->mode = WIRELESS_MODE_G; + /* check if we have this cell in our network list */ + rtllib_softmac_check_all_nets(ieee); + + /* if not then the state is not linked. Maybe the user switched to + * ad-hoc mode just after being in monitor mode, or just after + * being very few time in managed mode (so the card have had no + * time to scan all the chans..) or we have just run up the iface + * after setting ad-hoc mode. So we have to give another try.. + * Here, in ibss mode, should be safe to do this without extra care + * (in bss mode we had to make sure no-one tried to associate when + * we had just checked the ieee->link_state and we was going to start the + * scan) because in ibss mode the rtllib_new_net function, when + * finds a good net, just set the ieee->link_state to MAC80211_LINKED, + * so, at worst, we waste a bit of time to initiate an unneeded syncro + * scan, that will stop at the first round because it sees the state + * associated. + */ + if (ieee->link_state == MAC80211_NOLINK) + rtllib_start_scan_syncro(ieee); + + /* the network definitively is not here.. create a new cell */ + if (ieee->link_state == MAC80211_NOLINK) { + netdev_info(ieee->dev, "creating new IBSS cell\n"); + ieee->current_network.channel = ieee->bss_start_channel; + if (!ieee->wap_set) + eth_random_addr(ieee->current_network.bssid); + + ieee->current_network.rates_len = 4; + ieee->current_network.rates[0] = + RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_1MB; + ieee->current_network.rates[1] = + RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_2MB; + ieee->current_network.rates[2] = + RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_5MB; + ieee->current_network.rates[3] = + RTLLIB_BASIC_RATE_MASK | RTLLIB_CCK_RATE_11MB; + + ieee->current_network.rates_ex_len = 8; + ieee->current_network.rates_ex[0] = + RTLLIB_OFDM_RATE_6MB; + ieee->current_network.rates_ex[1] = + RTLLIB_OFDM_RATE_9MB; + ieee->current_network.rates_ex[2] = + RTLLIB_OFDM_RATE_12MB; + ieee->current_network.rates_ex[3] = + RTLLIB_OFDM_RATE_18MB; + ieee->current_network.rates_ex[4] = + RTLLIB_OFDM_RATE_24MB; + ieee->current_network.rates_ex[5] = + RTLLIB_OFDM_RATE_36MB; + ieee->current_network.rates_ex[6] = + RTLLIB_OFDM_RATE_48MB; + ieee->current_network.rates_ex[7] = + RTLLIB_OFDM_RATE_54MB; + ieee->rate = 108; + + ieee->current_network.qos_data.supported = 0; + ieee->set_wireless_mode(ieee->dev, WIRELESS_MODE_G); + ieee->current_network.mode = ieee->mode; + ieee->current_network.atim_window = 0; + ieee->current_network.capability = WLAN_CAPABILITY_IBSS; + } + + netdev_info(ieee->dev, "%s(): ieee->mode = %d\n", __func__, ieee->mode); + if (ieee->mode == WIRELESS_MODE_N_24G) + HTUseDefaultSetting(ieee); + else + ieee->ht_info->bCurrentHTSupport = false; + + ieee->SetHwRegHandler(ieee->dev, HW_VAR_MEDIA_STATUS, + (u8 *)(&ieee->link_state)); + + ieee->link_state = MAC80211_LINKED; + ieee->link_change(ieee->dev); + + HTSetConnectBwMode(ieee, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT); + rtllib_start_send_beacons(ieee); + + notify_wx_assoc_event(ieee); + netif_carrier_on(ieee->dev); + + mutex_unlock(&ieee->wx_mutex); +} + +inline void rtllib_start_ibss(struct rtllib_device *ieee) +{ + schedule_delayed_work(&ieee->start_ibss_wq, msecs_to_jiffies(150)); +} + +/* this is called only in user context, with wx_mutex held */ +static void rtllib_start_bss(struct rtllib_device *ieee) +{ + unsigned long flags; + + if (IS_DOT11D_ENABLE(ieee) && !IS_COUNTRY_IE_VALID(ieee)) { + if (!ieee->global_domain) + return; + } + /* check if we have already found the net we + * are interested in (if any). + * if not (we are disassociated and we are not + * in associating / authenticating phase) start the background scanning. + */ + rtllib_softmac_check_all_nets(ieee); + + /* ensure no-one start an associating process (thus setting + * the ieee->link_state to rtllib_ASSOCIATING) while we + * have just checked it and we are going to enable scan. + * The rtllib_new_net function is always called with + * lock held (from both rtllib_softmac_check_all_nets and + * the rx path), so we cannot be in the middle of such function + */ + spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->link_state == MAC80211_NOLINK) + rtllib_start_scan(ieee); + spin_unlock_irqrestore(&ieee->lock, flags); +} + +static void rtllib_link_change_wq(void *data) +{ + struct rtllib_device *ieee = container_of_dwork_rsl(data, + struct rtllib_device, link_change_wq); + ieee->link_change(ieee->dev); +} +/* called only in userspace context */ +void rtllib_disassociate(struct rtllib_device *ieee) +{ + netif_carrier_off(ieee->dev); + if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE) + rtllib_reset_queue(ieee); + + if (IS_DOT11D_ENABLE(ieee)) + dot11d_reset(ieee); + ieee->link_state = MAC80211_NOLINK; + ieee->is_set_key = false; + ieee->wap_set = 0; + + schedule_delayed_work(&ieee->link_change_wq, 0); + + notify_wx_assoc_event(ieee); +} + +static void rtllib_associate_retry_wq(void *data) +{ + struct rtllib_device *ieee = container_of_dwork_rsl(data, + struct rtllib_device, associate_retry_wq); + unsigned long flags; + + mutex_lock(&ieee->wx_mutex); + if (!ieee->proto_started) + goto exit; + + if (ieee->link_state != RTLLIB_ASSOCIATING_RETRY) + goto exit; + + /* until we do not set the state to MAC80211_NOLINK + * there are no possibility to have someone else trying + * to start an association procedure (we get here with + * ieee->link_state = RTLLIB_ASSOCIATING). + * When we set the state to MAC80211_NOLINK it is possible + * that the RX path run an attempt to associate, but + * both rtllib_softmac_check_all_nets and the + * RX path works with ieee->lock held so there are no + * problems. If we are still disassociated then start a scan. + * the lock here is necessary to ensure no one try to start + * an association procedure when we have just checked the + * state and we are going to start the scan. + */ + ieee->beinretry = true; + ieee->link_state = MAC80211_NOLINK; + + rtllib_softmac_check_all_nets(ieee); + + spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->link_state == MAC80211_NOLINK) + rtllib_start_scan(ieee); + spin_unlock_irqrestore(&ieee->lock, flags); + + ieee->beinretry = false; +exit: + mutex_unlock(&ieee->wx_mutex); +} + +static struct sk_buff *rtllib_get_beacon_(struct rtllib_device *ieee) +{ + static const u8 broadcast_addr[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + struct sk_buff *skb; + struct rtllib_probe_response *b; + + skb = rtllib_probe_resp(ieee, broadcast_addr); + + if (!skb) + return NULL; + + b = (struct rtllib_probe_response *)skb->data; + b->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_BEACON); + + return skb; +} + +struct sk_buff *rtllib_get_beacon(struct rtllib_device *ieee) +{ + struct sk_buff *skb; + struct rtllib_probe_response *b; + + skb = rtllib_get_beacon_(ieee); + if (!skb) + return NULL; + + b = (struct rtllib_probe_response *)skb->data; + b->header.seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + + return skb; +} +EXPORT_SYMBOL(rtllib_get_beacon); + +void rtllib_softmac_stop_protocol(struct rtllib_device *ieee, u8 mesh_flag, + u8 shutdown) +{ + rtllib_stop_scan_syncro(ieee); + mutex_lock(&ieee->wx_mutex); + rtllib_stop_protocol(ieee, shutdown); + mutex_unlock(&ieee->wx_mutex); +} +EXPORT_SYMBOL(rtllib_softmac_stop_protocol); + +void rtllib_stop_protocol(struct rtllib_device *ieee, u8 shutdown) +{ + if (!ieee->proto_started) + return; + + if (shutdown) { + ieee->proto_started = 0; + ieee->proto_stoppping = 1; + ieee->rtllib_ips_leave(ieee->dev); + } + + rtllib_stop_send_beacons(ieee); + del_timer_sync(&ieee->associate_timer); + cancel_delayed_work_sync(&ieee->associate_retry_wq); + cancel_delayed_work_sync(&ieee->start_ibss_wq); + cancel_delayed_work_sync(&ieee->link_change_wq); + rtllib_stop_scan(ieee); + + if (ieee->link_state <= RTLLIB_ASSOCIATING_AUTHENTICATED) + ieee->link_state = MAC80211_NOLINK; + + if (ieee->link_state == MAC80211_LINKED) { + if (ieee->iw_mode == IW_MODE_INFRA) + SendDisassociation(ieee, 1, WLAN_REASON_DEAUTH_LEAVING); + rtllib_disassociate(ieee); + } + + if (shutdown) { + RemoveAllTS(ieee); + ieee->proto_stoppping = 0; + } + kfree(ieee->assocreq_ies); + ieee->assocreq_ies = NULL; + ieee->assocreq_ies_len = 0; + kfree(ieee->assocresp_ies); + ieee->assocresp_ies = NULL; + ieee->assocresp_ies_len = 0; +} + +void rtllib_softmac_start_protocol(struct rtllib_device *ieee, u8 mesh_flag) +{ + mutex_lock(&ieee->wx_mutex); + rtllib_start_protocol(ieee); + mutex_unlock(&ieee->wx_mutex); +} +EXPORT_SYMBOL(rtllib_softmac_start_protocol); + +void rtllib_start_protocol(struct rtllib_device *ieee) +{ + short ch = 0; + int i = 0; + + rtllib_update_active_chan_map(ieee); + + if (ieee->proto_started) + return; + + ieee->proto_started = 1; + + if (ieee->current_network.channel == 0) { + do { + ch++; + if (ch > MAX_CHANNEL_NUMBER) + return; /* no channel found */ + } while (!ieee->active_channel_map[ch]); + ieee->current_network.channel = ch; + } + + if (ieee->current_network.beacon_interval == 0) + ieee->current_network.beacon_interval = 100; + + for (i = 0; i < 17; i++) { + ieee->last_rxseq_num[i] = -1; + ieee->last_rxfrag_num[i] = -1; + ieee->last_packet_time[i] = 0; + } + + ieee->wmm_acm = 0; + /* if the user set the MAC of the ad-hoc cell and then + * switch to managed mode, shall we make sure that association + * attempts does not fail just because the user provide the essid + * and the nic is still checking for the AP MAC ?? + */ + switch (ieee->iw_mode) { + case IW_MODE_INFRA: + rtllib_start_bss(ieee); + break; + case IW_MODE_ADHOC: + rtllib_start_ibss(ieee); + break; + case IW_MODE_MONITOR: + rtllib_start_monitor_mode(ieee); + break; + } +} + +int rtllib_softmac_init(struct rtllib_device *ieee) +{ + int i; + + memset(&ieee->current_network, 0, sizeof(struct rtllib_network)); + + ieee->link_state = MAC80211_NOLINK; + for (i = 0; i < 5; i++) + ieee->seq_ctrl[i] = 0; + ieee->dot11d_info = kzalloc(sizeof(struct rt_dot11d_info), GFP_ATOMIC); + if (!ieee->dot11d_info) + return -ENOMEM; + + ieee->link_detect_info.SlotIndex = 0; + ieee->link_detect_info.SlotNum = 2; + ieee->link_detect_info.NumRecvBcnInPeriod = 0; + ieee->link_detect_info.NumRecvDataInPeriod = 0; + ieee->link_detect_info.NumTxOkInPeriod = 0; + ieee->link_detect_info.NumRxOkInPeriod = 0; + ieee->link_detect_info.NumRxUnicastOkInPeriod = 0; + ieee->bIsAggregateFrame = false; + ieee->assoc_id = 0; + ieee->queue_stop = 0; + ieee->scanning_continue = 0; + ieee->softmac_features = 0; + ieee->wap_set = 0; + ieee->ssid_set = 0; + ieee->proto_started = 0; + ieee->proto_stoppping = 0; + ieee->basic_rate = RTLLIB_DEFAULT_BASIC_RATE; + ieee->rate = 22; + ieee->ps = RTLLIB_PS_DISABLED; + ieee->sta_sleep = LPS_IS_WAKE; + + ieee->reg_dot11ht_oper_rate_set[0] = 0xff; + ieee->reg_dot11ht_oper_rate_set[1] = 0xff; + ieee->reg_dot11ht_oper_rate_set[4] = 0x01; + + ieee->reg_dot11tx_ht_oper_rate_set[0] = 0xff; + ieee->reg_dot11tx_ht_oper_rate_set[1] = 0xff; + ieee->reg_dot11tx_ht_oper_rate_set[4] = 0x01; + + ieee->FirstIe_InScan = false; + ieee->actscanning = false; + ieee->beinretry = false; + ieee->is_set_key = false; + init_mgmt_queue(ieee); + + ieee->tx_pending.txb = NULL; + + timer_setup(&ieee->associate_timer, rtllib_associate_abort_cb, 0); + + timer_setup(&ieee->beacon_timer, rtllib_send_beacon_cb, 0); + + INIT_DELAYED_WORK(&ieee->link_change_wq, (void *)rtllib_link_change_wq); + INIT_DELAYED_WORK(&ieee->start_ibss_wq, (void *)rtllib_start_ibss_wq); + INIT_WORK(&ieee->associate_complete_wq, (void *)rtllib_associate_complete_wq); + INIT_DELAYED_WORK(&ieee->associate_procedure_wq, (void *)rtllib_associate_procedure_wq); + INIT_DELAYED_WORK(&ieee->softmac_scan_wq, (void *)rtllib_softmac_scan_wq); + INIT_DELAYED_WORK(&ieee->associate_retry_wq, (void *)rtllib_associate_retry_wq); + INIT_WORK(&ieee->wx_sync_scan_wq, (void *)rtllib_wx_sync_scan_wq); + + mutex_init(&ieee->wx_mutex); + mutex_init(&ieee->scan_mutex); + mutex_init(&ieee->ips_mutex); + + spin_lock_init(&ieee->mgmt_tx_lock); + spin_lock_init(&ieee->beacon_lock); + + INIT_WORK(&ieee->ps_task, rtllib_sta_ps); + + return 0; +} + +void rtllib_softmac_free(struct rtllib_device *ieee) +{ + mutex_lock(&ieee->wx_mutex); + kfree(ieee->dot11d_info); + ieee->dot11d_info = NULL; + del_timer_sync(&ieee->associate_timer); + + cancel_delayed_work_sync(&ieee->associate_retry_wq); + cancel_delayed_work_sync(&ieee->associate_procedure_wq); + cancel_delayed_work_sync(&ieee->softmac_scan_wq); + cancel_delayed_work_sync(&ieee->start_ibss_wq); + cancel_delayed_work_sync(&ieee->hw_wakeup_wq); + cancel_delayed_work_sync(&ieee->hw_sleep_wq); + cancel_delayed_work_sync(&ieee->link_change_wq); + cancel_work_sync(&ieee->associate_complete_wq); + cancel_work_sync(&ieee->ips_leave_wq); + cancel_work_sync(&ieee->wx_sync_scan_wq); + cancel_work_sync(&ieee->ps_task); + mutex_unlock(&ieee->wx_mutex); +} + +static inline struct sk_buff * +rtllib_disauth_skb(struct rtllib_network *beacon, + struct rtllib_device *ieee, u16 asRsn) +{ + struct sk_buff *skb; + struct rtllib_disauth *disauth; + int len = sizeof(struct rtllib_disauth) + ieee->tx_headroom; + + skb = dev_alloc_skb(len); + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + disauth = skb_put(skb, sizeof(struct rtllib_disauth)); + disauth->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_DEAUTH); + disauth->header.duration_id = 0; + + ether_addr_copy(disauth->header.addr1, beacon->bssid); + ether_addr_copy(disauth->header.addr2, ieee->dev->dev_addr); + ether_addr_copy(disauth->header.addr3, beacon->bssid); + + disauth->reason = cpu_to_le16(asRsn); + return skb; +} + +static inline struct sk_buff * +rtllib_disassociate_skb(struct rtllib_network *beacon, + struct rtllib_device *ieee, u16 asRsn) +{ + struct sk_buff *skb; + struct rtllib_disassoc *disass; + int len = sizeof(struct rtllib_disassoc) + ieee->tx_headroom; + + skb = dev_alloc_skb(len); + + if (!skb) + return NULL; + + skb_reserve(skb, ieee->tx_headroom); + + disass = skb_put(skb, sizeof(struct rtllib_disassoc)); + disass->header.frame_ctl = cpu_to_le16(RTLLIB_STYPE_DISASSOC); + disass->header.duration_id = 0; + + ether_addr_copy(disass->header.addr1, beacon->bssid); + ether_addr_copy(disass->header.addr2, ieee->dev->dev_addr); + ether_addr_copy(disass->header.addr3, beacon->bssid); + + disass->reason = cpu_to_le16(asRsn); + return skb; +} + +void SendDisassociation(struct rtllib_device *ieee, bool deauth, u16 asRsn) +{ + struct rtllib_network *beacon = &ieee->current_network; + struct sk_buff *skb; + + if (deauth) + skb = rtllib_disauth_skb(beacon, ieee, asRsn); + else + skb = rtllib_disassociate_skb(beacon, ieee, asRsn); + + if (skb) + softmac_mgmt_xmit(skb, ieee); +} + +u8 rtllib_ap_sec_type(struct rtllib_device *ieee) +{ + static u8 ccmp_ie[4] = {0x00, 0x50, 0xf2, 0x04}; + static u8 ccmp_rsn_ie[4] = {0x00, 0x0f, 0xac, 0x04}; + int wpa_ie_len = ieee->wpa_ie_len; + struct lib80211_crypt_data *crypt; + int encrypt; + + crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + encrypt = (ieee->current_network.capability & WLAN_CAPABILITY_PRIVACY) + || (crypt && crypt->ops && (strcmp(crypt->ops->name, "R-WEP") == 0)); + + /* simply judge */ + if (encrypt && (wpa_ie_len == 0)) { + return SEC_ALG_WEP; + } else if ((wpa_ie_len != 0)) { + if (((ieee->wpa_ie[0] == 0xdd) && + (!memcmp(&(ieee->wpa_ie[14]), ccmp_ie, 4))) || + ((ieee->wpa_ie[0] == 0x30) && + (!memcmp(&ieee->wpa_ie[10], ccmp_rsn_ie, 4)))) + return SEC_ALG_CCMP; + else + return SEC_ALG_TKIP; + } else { + return SEC_ALG_NONE; + } +} + +static void rtllib_MgntDisconnectIBSS(struct rtllib_device *rtllib) +{ + u8 OpMode; + u8 i; + bool bFilterOutNonAssociatedBSSID = false; + + rtllib->link_state = MAC80211_NOLINK; + + for (i = 0; i < 6; i++) + rtllib->current_network.bssid[i] = 0x55; + + rtllib->OpMode = RT_OP_MODE_NO_LINK; + rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_BSSID, + rtllib->current_network.bssid); + OpMode = RT_OP_MODE_NO_LINK; + rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_MEDIA_STATUS, &OpMode); + rtllib_stop_send_beacons(rtllib); + + bFilterOutNonAssociatedBSSID = false; + rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_CECHK_BSSID, + (u8 *)(&bFilterOutNonAssociatedBSSID)); + notify_wx_assoc_event(rtllib); +} + +static void rtllib_MlmeDisassociateRequest(struct rtllib_device *rtllib, + u8 *asSta, u8 asRsn) +{ + u8 i; + u8 OpMode; + + RemovePeerTS(rtllib, asSta); + + if (memcmp(rtllib->current_network.bssid, asSta, 6) == 0) { + rtllib->link_state = MAC80211_NOLINK; + + for (i = 0; i < 6; i++) + rtllib->current_network.bssid[i] = 0x22; + OpMode = RT_OP_MODE_NO_LINK; + rtllib->OpMode = RT_OP_MODE_NO_LINK; + rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_MEDIA_STATUS, + (u8 *)(&OpMode)); + rtllib_disassociate(rtllib); + + rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_BSSID, + rtllib->current_network.bssid); + } +} + +static void +rtllib_MgntDisconnectAP( + struct rtllib_device *rtllib, + u8 asRsn +) +{ + bool bFilterOutNonAssociatedBSSID = false; + + bFilterOutNonAssociatedBSSID = false; + rtllib->SetHwRegHandler(rtllib->dev, HW_VAR_CECHK_BSSID, + (u8 *)(&bFilterOutNonAssociatedBSSID)); + rtllib_MlmeDisassociateRequest(rtllib, rtllib->current_network.bssid, + asRsn); + + rtllib->link_state = MAC80211_NOLINK; +} + +bool rtllib_MgntDisconnect(struct rtllib_device *rtllib, u8 asRsn) +{ + if (rtllib->ps != RTLLIB_PS_DISABLED) + rtllib->sta_wake_up(rtllib->dev); + + if (rtllib->link_state == MAC80211_LINKED) { + if (rtllib->iw_mode == IW_MODE_ADHOC) + rtllib_MgntDisconnectIBSS(rtllib); + if (rtllib->iw_mode == IW_MODE_INFRA) + rtllib_MgntDisconnectAP(rtllib, asRsn); + } + + return true; +} +EXPORT_SYMBOL(rtllib_MgntDisconnect); + +void notify_wx_assoc_event(struct rtllib_device *ieee) +{ + union iwreq_data wrqu; + + if (ieee->cannot_notify) + return; + + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + if (ieee->link_state == MAC80211_LINKED) { + memcpy(wrqu.ap_addr.sa_data, ieee->current_network.bssid, + ETH_ALEN); + } else { + netdev_info(ieee->dev, "%s(): Tell user space disconnected\n", + __func__); + eth_zero_addr(wrqu.ap_addr.sa_data); + } + wireless_send_event(ieee->dev, SIOCGIWAP, &wrqu, NULL); +} +EXPORT_SYMBOL(notify_wx_assoc_event); diff --git a/drivers/staging/rtl8192e/rtllib_softmac_wx.c b/drivers/staging/rtl8192e/rtllib_softmac_wx.c new file mode 100644 index 0000000000..0b690f0ffe --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_softmac_wx.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0 +/* IEEE 802.11 SoftMAC layer + * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com> + * + * Mostly extracted from the rtl8180-sa2400 driver for the + * in-kernel generic ieee802.11 stack. + * + * Some pieces of code might be stolen from ipw2100 driver + * copyright of who own it's copyright ;-) + * + * PS wx handler mostly stolen from hostap, copyright who + * own it's copyright ;-) + */ +#include <linux/etherdevice.h> + +#include "rtllib.h" +#include "dot11d.h" + +int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret; + struct iw_freq *fwrq = &wrqu->freq; + + mutex_lock(&ieee->wx_mutex); + + if (ieee->iw_mode == IW_MODE_INFRA) { + ret = 0; + goto out; + } + + /* if setting by freq convert to channel */ + if (fwrq->e == 1) { + if ((fwrq->m >= (int)2.412e8 && + fwrq->m <= (int)2.487e8)) { + fwrq->m = ieee80211_freq_khz_to_channel(fwrq->m / 100); + fwrq->e = 0; + } + } + + if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) { + ret = -EOPNOTSUPP; + goto out; + + } else { /* Set the channel */ + + if (ieee->active_channel_map[fwrq->m] != 1) { + ret = -EINVAL; + goto out; + } + ieee->current_network.channel = fwrq->m; + ieee->set_chan(ieee->dev, ieee->current_network.channel); + + if (ieee->iw_mode == IW_MODE_ADHOC) + if (ieee->link_state == MAC80211_LINKED) { + rtllib_stop_send_beacons(ieee); + rtllib_start_send_beacons(ieee); + } + } + + ret = 0; +out: + mutex_unlock(&ieee->wx_mutex); + return ret; +} +EXPORT_SYMBOL(rtllib_wx_set_freq); + +int rtllib_wx_get_freq(struct rtllib_device *ieee, + struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + struct iw_freq *fwrq = &wrqu->freq; + + if (ieee->current_network.channel == 0) + return -1; + fwrq->m = ieee80211_channel_to_freq_khz(ieee->current_network.channel, + NL80211_BAND_2GHZ) * 100; + fwrq->e = 1; + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_freq); + +int rtllib_wx_get_wap(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + unsigned long flags; + + wrqu->ap_addr.sa_family = ARPHRD_ETHER; + + if (ieee->iw_mode == IW_MODE_MONITOR) + return -1; + + /* We want avoid to give to the user inconsistent infos*/ + spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->link_state != MAC80211_LINKED && + ieee->link_state != MAC80211_LINKED_SCANNING && + ieee->wap_set == 0) + + eth_zero_addr(wrqu->ap_addr.sa_data); + else + memcpy(wrqu->ap_addr.sa_data, + ieee->current_network.bssid, ETH_ALEN); + + spin_unlock_irqrestore(&ieee->lock, flags); + + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_wap); + +int rtllib_wx_set_wap(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *awrq, + char *extra) +{ + int ret = 0; + unsigned long flags; + + short ifup = ieee->proto_started; + struct sockaddr *temp = (struct sockaddr *)awrq; + + rtllib_stop_scan_syncro(ieee); + + mutex_lock(&ieee->wx_mutex); + /* use ifconfig hw ether */ + + if (temp->sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto out; + } + + if (is_zero_ether_addr(temp->sa_data)) { + spin_lock_irqsave(&ieee->lock, flags); + ether_addr_copy(ieee->current_network.bssid, temp->sa_data); + ieee->wap_set = 0; + spin_unlock_irqrestore(&ieee->lock, flags); + ret = -1; + goto out; + } + + if (ifup) + rtllib_stop_protocol(ieee, true); + + /* just to avoid to give inconsistent infos in the + * get wx method. not really needed otherwise + */ + spin_lock_irqsave(&ieee->lock, flags); + + ieee->cannot_notify = false; + ether_addr_copy(ieee->current_network.bssid, temp->sa_data); + ieee->wap_set = !is_zero_ether_addr(temp->sa_data); + + spin_unlock_irqrestore(&ieee->lock, flags); + + if (ifup) + rtllib_start_protocol(ieee); +out: + mutex_unlock(&ieee->wx_mutex); + return ret; +} +EXPORT_SYMBOL(rtllib_wx_set_wap); + +int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int len, ret = 0; + unsigned long flags; + + if (ieee->iw_mode == IW_MODE_MONITOR) + return -1; + + /* We want avoid to give to the user inconsistent infos*/ + spin_lock_irqsave(&ieee->lock, flags); + + if (ieee->current_network.ssid[0] == '\0' || + ieee->current_network.ssid_len == 0) { + ret = -1; + goto out; + } + + if (ieee->link_state != MAC80211_LINKED && + ieee->link_state != MAC80211_LINKED_SCANNING && + ieee->ssid_set == 0) { + ret = -1; + goto out; + } + len = ieee->current_network.ssid_len; + wrqu->essid.length = len; + strncpy(b, ieee->current_network.ssid, len); + wrqu->essid.flags = 1; + +out: + spin_unlock_irqrestore(&ieee->lock, flags); + + return ret; +} +EXPORT_SYMBOL(rtllib_wx_get_essid); + +int rtllib_wx_set_rate(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u32 target_rate = wrqu->bitrate.value; + + ieee->rate = target_rate / 100000; + return 0; +} +EXPORT_SYMBOL(rtllib_wx_set_rate); + +int rtllib_wx_get_rate(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u32 tmp_rate; + + tmp_rate = TxCountToDataRate(ieee, + ieee->softmac_stats.CurrentShowTxate); + wrqu->bitrate.value = tmp_rate * 500000; + + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_rate); + +int rtllib_wx_set_rts(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + if (wrqu->rts.disabled || !wrqu->rts.fixed) { + ieee->rts = DEFAULT_RTS_THRESHOLD; + } else { + if (wrqu->rts.value < MIN_RTS_THRESHOLD || + wrqu->rts.value > MAX_RTS_THRESHOLD) + return -EINVAL; + ieee->rts = wrqu->rts.value; + } + return 0; +} +EXPORT_SYMBOL(rtllib_wx_set_rts); + +int rtllib_wx_get_rts(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + wrqu->rts.value = ieee->rts; + wrqu->rts.fixed = 0; /* no auto select */ + wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_rts); + +int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int set_mode_status = 0; + + rtllib_stop_scan_syncro(ieee); + mutex_lock(&ieee->wx_mutex); + switch (wrqu->mode) { + case IW_MODE_MONITOR: + case IW_MODE_ADHOC: + case IW_MODE_INFRA: + break; + case IW_MODE_AUTO: + wrqu->mode = IW_MODE_INFRA; + break; + default: + set_mode_status = -EINVAL; + goto out; + } + + if (wrqu->mode == ieee->iw_mode) + goto out; + + if (wrqu->mode == IW_MODE_MONITOR) { + ieee->dev->type = ARPHRD_IEEE80211; + rtllib_EnableNetMonitorMode(ieee->dev, false); + } else { + ieee->dev->type = ARPHRD_ETHER; + if (ieee->iw_mode == IW_MODE_MONITOR) + rtllib_DisableNetMonitorMode(ieee->dev, false); + } + + if (!ieee->proto_started) { + ieee->iw_mode = wrqu->mode; + } else { + rtllib_stop_protocol(ieee, true); + ieee->iw_mode = wrqu->mode; + rtllib_start_protocol(ieee); + } + +out: + mutex_unlock(&ieee->wx_mutex); + return set_mode_status; +} +EXPORT_SYMBOL(rtllib_wx_set_mode); + +void rtllib_wx_sync_scan_wq(void *data) +{ + struct rtllib_device *ieee = container_of(data, struct rtllib_device, wx_sync_scan_wq); + short chan; + enum ht_extchnl_offset chan_offset = 0; + enum ht_channel_width bandwidth = 0; + int b40M = 0; + + mutex_lock(&ieee->wx_mutex); + if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) { + rtllib_start_scan_syncro(ieee); + goto out; + } + + chan = ieee->current_network.channel; + + ieee->leisure_ps_leave(ieee->dev); + /* notify AP to be in PS mode */ + rtllib_sta_ps_send_null_frame(ieee, 1); + rtllib_sta_ps_send_null_frame(ieee, 1); + + rtllib_stop_all_queues(ieee); + rtllib_stop_send_beacons(ieee); + ieee->link_state = MAC80211_LINKED_SCANNING; + ieee->link_change(ieee->dev); + /* wait for ps packet to be kicked out successfully */ + msleep(50); + + ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP); + + if (ieee->ht_info->bCurrentHTSupport && ieee->ht_info->enable_ht && + ieee->ht_info->bCurBW40MHz) { + b40M = 1; + chan_offset = ieee->ht_info->CurSTAExtChnlOffset; + bandwidth = (enum ht_channel_width)ieee->ht_info->bCurBW40MHz; + ieee->set_bw_mode_handler(ieee->dev, HT_CHANNEL_WIDTH_20, + HT_EXTCHNL_OFFSET_NO_EXT); + } + + rtllib_start_scan_syncro(ieee); + + if (b40M) { + if (chan_offset == HT_EXTCHNL_OFFSET_UPPER) + ieee->set_chan(ieee->dev, chan + 2); + else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER) + ieee->set_chan(ieee->dev, chan - 2); + else + ieee->set_chan(ieee->dev, chan); + ieee->set_bw_mode_handler(ieee->dev, bandwidth, chan_offset); + } else { + ieee->set_chan(ieee->dev, chan); + } + + ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE); + + ieee->link_state = MAC80211_LINKED; + ieee->link_change(ieee->dev); + + /* Notify AP that I wake up again */ + rtllib_sta_ps_send_null_frame(ieee, 0); + + if (ieee->link_detect_info.NumRecvBcnInPeriod == 0 || + ieee->link_detect_info.NumRecvDataInPeriod == 0) { + ieee->link_detect_info.NumRecvBcnInPeriod = 1; + ieee->link_detect_info.NumRecvDataInPeriod = 1; + } + if (ieee->iw_mode == IW_MODE_ADHOC) + rtllib_start_send_beacons(ieee); + + rtllib_wake_all_queues(ieee); + +out: + mutex_unlock(&ieee->wx_mutex); +} + +int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + int ret = 0; + + if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) { + ret = -1; + goto out; + } + + if (ieee->link_state == MAC80211_LINKED) { + schedule_work(&ieee->wx_sync_scan_wq); + /* intentionally forget to up sem */ + return 0; + } + +out: + return ret; +} +EXPORT_SYMBOL(rtllib_wx_set_scan); + +int rtllib_wx_set_essid(struct rtllib_device *ieee, + struct iw_request_info *a, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0, len; + short proto_started; + unsigned long flags; + + rtllib_stop_scan_syncro(ieee); + mutex_lock(&ieee->wx_mutex); + + proto_started = ieee->proto_started; + + len = min_t(__u16, wrqu->essid.length, IW_ESSID_MAX_SIZE); + + if (ieee->iw_mode == IW_MODE_MONITOR) { + ret = -1; + goto out; + } + + if (proto_started) + rtllib_stop_protocol(ieee, true); + + /* this is just to be sure that the GET wx callback + * has consistent infos. not needed otherwise + */ + spin_lock_irqsave(&ieee->lock, flags); + + if (wrqu->essid.flags && wrqu->essid.length) { + strncpy(ieee->current_network.ssid, extra, len); + ieee->current_network.ssid_len = len; + ieee->cannot_notify = false; + ieee->ssid_set = 1; + } else { + ieee->ssid_set = 0; + ieee->current_network.ssid[0] = '\0'; + ieee->current_network.ssid_len = 0; + } + spin_unlock_irqrestore(&ieee->lock, flags); + + if (proto_started) + rtllib_start_protocol(ieee); +out: + mutex_unlock(&ieee->wx_mutex); + return ret; +} +EXPORT_SYMBOL(rtllib_wx_set_essid); + +int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a, + union iwreq_data *wrqu, char *b) +{ + wrqu->mode = ieee->iw_mode; + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_mode); + +int rtllib_wx_set_rawtx(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int *parms = (int *)extra; + int enable = (parms[0] > 0); + short prev = ieee->raw_tx; + + mutex_lock(&ieee->wx_mutex); + + if (enable) + ieee->raw_tx = 1; + else + ieee->raw_tx = 0; + + netdev_info(ieee->dev, "raw TX is %s\n", + ieee->raw_tx ? "enabled" : "disabled"); + + if (ieee->iw_mode == IW_MODE_MONITOR) { + if (prev == 0 && ieee->raw_tx) + netif_carrier_on(ieee->dev); + + if (prev && ieee->raw_tx == 1) + netif_carrier_off(ieee->dev); + } + + mutex_unlock(&ieee->wx_mutex); + + return 0; +} +EXPORT_SYMBOL(rtllib_wx_set_rawtx); + +int rtllib_wx_get_name(struct rtllib_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + const char *n = ieee->mode & (WIRELESS_MODE_N_24G) ? "n" : ""; + + scnprintf(wrqu->name, sizeof(wrqu->name), "802.11bg%s", n); + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_name); + +/* this is mostly stolen from hostap */ +int rtllib_wx_set_power(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + + if ((!ieee->sta_wake_up) || + (!ieee->enter_sleep_state) || + (!ieee->ps_is_queue_empty)) { + netdev_warn(ieee->dev, + "%s(): PS mode is tried to be use but driver missed a callback\n", + __func__); + return -1; + } + + mutex_lock(&ieee->wx_mutex); + + if (wrqu->power.disabled) { + ieee->ps = RTLLIB_PS_DISABLED; + goto exit; + } + if (wrqu->power.flags & IW_POWER_TIMEOUT) + ieee->ps_timeout = wrqu->power.value / 1000; + + if (wrqu->power.flags & IW_POWER_PERIOD) + ieee->ps_period = wrqu->power.value / 1000; + + switch (wrqu->power.flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + ieee->ps = RTLLIB_PS_UNICAST; + break; + case IW_POWER_MULTICAST_R: + ieee->ps = RTLLIB_PS_MBCAST; + break; + case IW_POWER_ALL_R: + ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST; + break; + + case IW_POWER_ON: + break; + + default: + ret = -EINVAL; + goto exit; + } +exit: + mutex_unlock(&ieee->wx_mutex); + return ret; +} +EXPORT_SYMBOL(rtllib_wx_set_power); + +/* this is stolen from hostap */ +int rtllib_wx_get_power(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + mutex_lock(&ieee->wx_mutex); + + if (ieee->ps == RTLLIB_PS_DISABLED) { + wrqu->power.disabled = 1; + goto exit; + } + + wrqu->power.disabled = 0; + + if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + wrqu->power.flags = IW_POWER_TIMEOUT; + wrqu->power.value = ieee->ps_timeout * 1000; + } else { + wrqu->power.flags = IW_POWER_PERIOD; + wrqu->power.value = ieee->ps_period * 1000; + } + + if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) == + (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) + wrqu->power.flags |= IW_POWER_ALL_R; + else if (ieee->ps & RTLLIB_PS_MBCAST) + wrqu->power.flags |= IW_POWER_MULTICAST_R; + else + wrqu->power.flags |= IW_POWER_UNICAST_R; + +exit: + mutex_unlock(&ieee->wx_mutex); + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_power); diff --git a/drivers/staging/rtl8192e/rtllib_tx.c b/drivers/staging/rtl8192e/rtllib_tx.c new file mode 100644 index 0000000000..4199aee930 --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_tx.c @@ -0,0 +1,955 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved. + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * Few modifications for Realtek's Wi-Fi drivers by + * Andrea Merello <andrea.merello@gmail.com> + * + * A special thanks goes to Realtek for their support ! + */ +#include <linux/compiler.h> +#include <linux/errno.h> +#include <linux/if_arp.h> +#include <linux/in6.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/proc_fs.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/tcp.h> +#include <linux/types.h> +#include <linux/wireless.h> +#include <linux/etherdevice.h> +#include <linux/uaccess.h> +#include <linux/if_vlan.h> + +#include "rtllib.h" + +/* 802.11 Data Frame + * + * + * 802.11 frame_control for data frames - 2 bytes + * ,--------------------------------------------------------------------. + * bits | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c | d | e | + * |---|---|---|---|---|---|---|---|---|----|----|-----|-----|-----|----| + * val | 0 | 0 | 0 | 1 | x | 0 | 0 | 0 | 1 | 0 | x | x | x | x | x | + * |---|---|---|---|---|---|---|---|---|----|----|-----|-----|-----|----| + * desc | ver | type | ^-subtype-^ |to |from|more|retry| pwr |more |wep | + * | | | x=0 data |DS | DS |frag| | mgm |data | | + * | | | x=1 data+ack | | | | | | | | + * '--------------------------------------------------------------------' + * /\ + * | + * 802.11 Data Frame | + * ,--------- 'ctrl' expands to >---' + * | + * ,--'---,-------------------------------------------------------------. + * Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + * |------|------|---------|---------|---------|------|---------|------| + * Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs | + * | | tion | (BSSID) | | | ence | data | | + * `--------------------------------------------------| |------' + * Total: 28 non-data bytes `----.----' + * | + * .- 'Frame data' expands to <---------------------------' + * | + * V + * ,---------------------------------------------------. + * Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 | + * |------|------|---------|----------|------|---------| + * Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | + * | DSAP | SSAP | | | | Packet | + * | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | + * `-----------------------------------------| | + * Total: 8 non-data bytes `----.----' + * | + * .- 'IP Packet' expands, if WEP enabled, to <--' + * | + * V + * ,-----------------------. + * Bytes | 4 | 0-2296 | 4 | + * |-----|-----------|-----| + * Desc. | IV | Encrypted | ICV | + * | | IP Packet | | + * `-----------------------' + * Total: 8 non-data bytes + * + * + * 802.3 Ethernet Data Frame + * + * ,-----------------------------------------. + * Bytes | 6 | 6 | 2 | Variable | 4 | + * |-------|-------|------|-----------|------| + * Desc. | Dest. | Source| Type | IP Packet | fcs | + * | MAC | MAC | | | | + * `-----------------------------------------' + * Total: 18 non-data bytes + * + * In the event that fragmentation is required, the incoming payload is split + * into N parts of size ieee->fts. The first fragment contains the SNAP header + * and the remaining packets are just data. + * + * If encryption is enabled, each fragment payload size is reduced by enough + * space to add the prefix and postfix (IV and ICV totalling 8 bytes in + * the case of WEP) So if you have 1500 bytes of payload with ieee->fts set to + * 500 without encryption it will take 3 frames. With WEP it will take 4 frames + * as the payload of each frame is reduced to 492 bytes. + * + * SKB visualization + * + * ,- skb->data + * | + * | ETHERNET HEADER ,-<-- PAYLOAD + * | | 14 bytes from skb->data + * | 2 bytes for Type --> ,T. | (sizeof ethhdr) + * | | | | + * |,-Dest.--. ,--Src.---. | | | + * | 6 bytes| | 6 bytes | | | | + * v | | | | | | + * 0 | v 1 | v | v 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * ^ | ^ | ^ | + * | | | | | | + * | | | | `T' <---- 2 bytes for Type + * | | | | + * | | '---SNAP--' <-------- 6 bytes for SNAP + * | | + * `-IV--' <-------------------- 4 bytes for IV (WEP) + * + * SNAP HEADER + * + */ + +static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; +static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; + +static int rtllib_put_snap(u8 *data, u16 h_proto) +{ + struct rtllib_snap_hdr *snap; + u8 *oui; + + snap = (struct rtllib_snap_hdr *)data; + snap->dsap = 0xaa; + snap->ssap = 0xaa; + snap->ctrl = 0x03; + + if (h_proto == 0x8137 || h_proto == 0x80f3) + oui = P802_1H_OUI; + else + oui = RFC1042_OUI; + snap->oui[0] = oui[0]; + snap->oui[1] = oui[1]; + snap->oui[2] = oui[2]; + + *(__be16 *)(data + SNAP_SIZE) = htons(h_proto); + + return SNAP_SIZE + sizeof(u16); +} + +int rtllib_encrypt_fragment(struct rtllib_device *ieee, struct sk_buff *frag, + int hdr_len) +{ + struct lib80211_crypt_data *crypt = NULL; + int res; + + crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + + if (!(crypt && crypt->ops)) { + netdev_info(ieee->dev, "=========>%s(), crypt is null\n", + __func__); + return -1; + } + /* To encrypt, frame format is: + * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) + */ + + /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so + * call both MSDU and MPDU encryption functions from here. + */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops->encrypt_msdu) + res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv); + if (res == 0 && crypt->ops->encrypt_mpdu) + res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); + + atomic_dec(&crypt->refcnt); + if (res < 0) { + netdev_info(ieee->dev, "%s: Encryption failed: len=%d.\n", + ieee->dev->name, frag->len); + return -1; + } + + return 0; +} + +void rtllib_txb_free(struct rtllib_txb *txb) +{ + if (unlikely(!txb)) + return; + kfree(txb); +} + +static struct rtllib_txb *rtllib_alloc_txb(int nr_frags, int txb_size, + gfp_t gfp_mask) +{ + struct rtllib_txb *txb; + int i; + + txb = kzalloc(struct_size(txb, fragments, nr_frags), gfp_mask); + if (!txb) + return NULL; + + txb->nr_frags = nr_frags; + txb->frag_size = cpu_to_le16(txb_size); + + for (i = 0; i < nr_frags; i++) { + txb->fragments[i] = dev_alloc_skb(txb_size); + if (unlikely(!txb->fragments[i])) + goto err_free; + memset(txb->fragments[i]->cb, 0, sizeof(txb->fragments[i]->cb)); + } + + return txb; + +err_free: + while (--i >= 0) + dev_kfree_skb_any(txb->fragments[i]); + kfree(txb); + + return NULL; +} + +static int rtllib_classify(struct sk_buff *skb, u8 bIsAmsdu) +{ + struct ethhdr *eth; + struct iphdr *ip; + + eth = (struct ethhdr *)skb->data; + if (eth->h_proto != htons(ETH_P_IP)) + return 0; + +#ifdef VERBOSE_DEBUG + print_hex_dump_bytes("%s: ", __func__, DUMP_PREFIX_NONE, skb->data, + skb->len); +#endif + ip = ip_hdr(skb); + switch (ip->tos & 0xfc) { + case 0x20: + return 2; + case 0x40: + return 1; + case 0x60: + return 3; + case 0x80: + return 4; + case 0xa0: + return 5; + case 0xc0: + return 6; + case 0xe0: + return 7; + default: + return 0; + } +} + +static void rtllib_tx_query_agg_cap(struct rtllib_device *ieee, + struct sk_buff *skb, + struct cb_desc *tcb_desc) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + struct tx_ts_record *pTxTs = NULL; + struct rtllib_hdr_1addr *hdr = (struct rtllib_hdr_1addr *)skb->data; + + if (rtllib_act_scanning(ieee, false)) + return; + + if (!ht_info->bCurrentHTSupport || !ht_info->enable_ht) + return; + if (!IsQoSDataFrame(skb->data)) + return; + if (is_multicast_ether_addr(hdr->addr1)) + return; + + if (tcb_desc->bdhcp || ieee->CntAfterLink < 2) + return; + + if (ht_info->iot_action & HT_IOT_ACT_TX_NO_AGGREGATION) + return; + + if (!ieee->GetNmodeSupportBySecCfg(ieee->dev)) + return; + if (ht_info->bCurrentAMPDUEnable) { + if (!GetTs(ieee, (struct ts_common_info **)(&pTxTs), hdr->addr1, + skb->priority, TX_DIR, true)) { + netdev_info(ieee->dev, "%s: can't get TS\n", __func__); + return; + } + if (!pTxTs->TxAdmittedBARecord.b_valid) { + if (ieee->wpa_ie_len && (ieee->pairwise_key_type == + KEY_TYPE_NA)) { + ; + } else if (tcb_desc->bdhcp == 1) { + ; + } else if (!pTxTs->bDisable_AddBa) { + TsStartAddBaProcess(ieee, pTxTs); + } + goto FORCED_AGG_SETTING; + } else if (!pTxTs->bUsingBa) { + if (SN_LESS(pTxTs->TxAdmittedBARecord.ba_start_seq_ctrl.field.seq_num, + (pTxTs->TxCurSeq + 1) % 4096)) + pTxTs->bUsingBa = true; + else + goto FORCED_AGG_SETTING; + } + if (ieee->iw_mode == IW_MODE_INFRA) { + tcb_desc->bAMPDUEnable = true; + tcb_desc->ampdu_factor = ht_info->CurrentAMPDUFactor; + tcb_desc->ampdu_density = ht_info->current_mpdu_density; + } + } +FORCED_AGG_SETTING: + switch (ht_info->ForcedAMPDUMode) { + case HT_AGG_AUTO: + break; + + case HT_AGG_FORCE_ENABLE: + tcb_desc->bAMPDUEnable = true; + tcb_desc->ampdu_density = ht_info->forced_mpdu_density; + tcb_desc->ampdu_factor = ht_info->forced_ampdu_factor; + break; + + case HT_AGG_FORCE_DISABLE: + tcb_desc->bAMPDUEnable = false; + tcb_desc->ampdu_density = 0; + tcb_desc->ampdu_factor = 0; + break; + } +} + +static void rtllib_query_ShortPreambleMode(struct rtllib_device *ieee, + struct cb_desc *tcb_desc) +{ + tcb_desc->bUseShortPreamble = false; + if (tcb_desc->data_rate == 2) + return; + else if (ieee->current_network.capability & + WLAN_CAPABILITY_SHORT_PREAMBLE) + tcb_desc->bUseShortPreamble = true; +} + +static void rtllib_query_HTCapShortGI(struct rtllib_device *ieee, + struct cb_desc *tcb_desc) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + tcb_desc->bUseShortGI = false; + + if (!ht_info->bCurrentHTSupport || !ht_info->enable_ht) + return; + + if (ht_info->forced_short_gi) { + tcb_desc->bUseShortGI = true; + return; + } + + if (ht_info->bCurBW40MHz && ht_info->bCurShortGI40MHz) + tcb_desc->bUseShortGI = true; + else if (!ht_info->bCurBW40MHz && ht_info->bCurShortGI20MHz) + tcb_desc->bUseShortGI = true; +} + +static void rtllib_query_BandwidthMode(struct rtllib_device *ieee, + struct cb_desc *tcb_desc) +{ + struct rt_hi_throughput *ht_info = ieee->ht_info; + + tcb_desc->bPacketBW = false; + + if (!ht_info->bCurrentHTSupport || !ht_info->enable_ht) + return; + + if (tcb_desc->bMulticast || tcb_desc->bBroadcast) + return; + + if ((tcb_desc->data_rate & 0x80) == 0) + return; + if (ht_info->bCurBW40MHz && ht_info->cur_tx_bw40mhz && + !ieee->bandwidth_auto_switch.bforced_tx20Mhz) + tcb_desc->bPacketBW = true; +} + +static void rtllib_query_protectionmode(struct rtllib_device *ieee, + struct cb_desc *tcb_desc, + struct sk_buff *skb) +{ + struct rt_hi_throughput *ht_info; + + tcb_desc->bRTSSTBC = false; + tcb_desc->bRTSUseShortGI = false; + tcb_desc->bCTSEnable = false; + tcb_desc->RTSSC = 0; + tcb_desc->bRTSBW = false; + + if (tcb_desc->bBroadcast || tcb_desc->bMulticast) + return; + + if (is_broadcast_ether_addr(skb->data + 16)) + return; + + if (ieee->mode < WIRELESS_MODE_N_24G) { + if (skb->len > ieee->rts) { + tcb_desc->bRTSEnable = true; + tcb_desc->rts_rate = MGN_24M; + } else if (ieee->current_network.buseprotection) { + tcb_desc->bRTSEnable = true; + tcb_desc->bCTSEnable = true; + tcb_desc->rts_rate = MGN_24M; + } + return; + } + + ht_info = ieee->ht_info; + + while (true) { + if (ht_info->iot_action & HT_IOT_ACT_FORCED_CTS2SELF) { + tcb_desc->bCTSEnable = true; + tcb_desc->rts_rate = MGN_24M; + tcb_desc->bRTSEnable = true; + break; + } else if (ht_info->iot_action & (HT_IOT_ACT_FORCED_RTS | + HT_IOT_ACT_PURE_N_MODE)) { + tcb_desc->bRTSEnable = true; + tcb_desc->rts_rate = MGN_24M; + break; + } + if (ieee->current_network.buseprotection) { + tcb_desc->bRTSEnable = true; + tcb_desc->bCTSEnable = true; + tcb_desc->rts_rate = MGN_24M; + break; + } + if (ht_info->bCurrentHTSupport && ht_info->enable_ht) { + u8 HTOpMode = ht_info->current_op_mode; + + if ((ht_info->bCurBW40MHz && (HTOpMode == 2 || + HTOpMode == 3)) || + (!ht_info->bCurBW40MHz && HTOpMode == 3)) { + tcb_desc->rts_rate = MGN_24M; + tcb_desc->bRTSEnable = true; + break; + } + } + if (skb->len > ieee->rts) { + tcb_desc->rts_rate = MGN_24M; + tcb_desc->bRTSEnable = true; + break; + } + if (tcb_desc->bAMPDUEnable) { + tcb_desc->rts_rate = MGN_24M; + tcb_desc->bRTSEnable = false; + break; + } + goto NO_PROTECTION; + } + if (ieee->current_network.capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + tcb_desc->bUseShortPreamble = true; + return; +NO_PROTECTION: + tcb_desc->bRTSEnable = false; + tcb_desc->bCTSEnable = false; + tcb_desc->rts_rate = 0; + tcb_desc->RTSSC = 0; + tcb_desc->bRTSBW = false; +} + +static void rtllib_txrate_selectmode(struct rtllib_device *ieee, + struct cb_desc *tcb_desc) +{ + if (ieee->tx_dis_rate_fallback) + tcb_desc->tx_dis_rate_fallback = true; + + if (ieee->tx_use_drv_assinged_rate) + tcb_desc->tx_use_drv_assinged_rate = true; + if (!tcb_desc->tx_dis_rate_fallback || + !tcb_desc->tx_use_drv_assinged_rate) { + if (ieee->iw_mode == IW_MODE_INFRA || + ieee->iw_mode == IW_MODE_ADHOC) + tcb_desc->ratr_index = 0; + } +} + +static u16 rtllib_query_seqnum(struct rtllib_device *ieee, struct sk_buff *skb, + u8 *dst) +{ + u16 seqnum = 0; + + if (is_multicast_ether_addr(dst)) + return 0; + if (IsQoSDataFrame(skb->data)) { + struct tx_ts_record *pTS = NULL; + + if (!GetTs(ieee, (struct ts_common_info **)(&pTS), dst, + skb->priority, TX_DIR, true)) + return 0; + seqnum = pTS->TxCurSeq; + pTS->TxCurSeq = (pTS->TxCurSeq + 1) % 4096; + return seqnum; + } + return 0; +} + +static int wme_downgrade_ac(struct sk_buff *skb) +{ + switch (skb->priority) { + case 6: + case 7: + skb->priority = 5; /* VO -> VI */ + return 0; + case 4: + case 5: + skb->priority = 3; /* VI -> BE */ + return 0; + case 0: + case 3: + skb->priority = 1; /* BE -> BK */ + return 0; + default: + return -1; + } +} + +static u8 rtllib_current_rate(struct rtllib_device *ieee) +{ + if (ieee->mode & IEEE_MODE_MASK) + return ieee->rate; + + if (ieee->HTCurrentOperaRate) + return ieee->HTCurrentOperaRate; + else + return ieee->rate & 0x7F; +} + +static int rtllib_xmit_inter(struct sk_buff *skb, struct net_device *dev) +{ + struct rtllib_device *ieee = (struct rtllib_device *) + netdev_priv_rsl(dev); + struct rtllib_txb *txb = NULL; + struct rtllib_hdr_3addrqos *frag_hdr; + int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size; + unsigned long flags; + struct net_device_stats *stats = &ieee->stats; + int ether_type = 0, encrypt; + int bytes, fc, qos_ctl = 0, hdr_len; + struct sk_buff *skb_frag; + struct rtllib_hdr_3addrqos header = { /* Ensure zero initialized */ + .duration_id = 0, + .seq_ctl = 0, + .qos_ctl = 0 + }; + int qos_activated = ieee->current_network.qos_data.active; + u8 dest[ETH_ALEN]; + u8 src[ETH_ALEN]; + struct lib80211_crypt_data *crypt = NULL; + struct cb_desc *tcb_desc; + u8 bIsMulticast = false; + u8 IsAmsdu = false; + bool bdhcp = false; + + spin_lock_irqsave(&ieee->lock, flags); + + /* If there is no driver handler to take the TXB, don't bother + * creating it... + */ + if (!(ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE) || + ((!ieee->softmac_data_hard_start_xmit && + (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE)))) { + netdev_warn(ieee->dev, "No xmit handler.\n"); + goto success; + } + + if (likely(ieee->raw_tx == 0)) { + if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { + netdev_warn(ieee->dev, "skb too small (%d).\n", + skb->len); + goto success; + } + /* Save source and destination addresses */ + ether_addr_copy(dest, skb->data); + ether_addr_copy(src, skb->data + ETH_ALEN); + + memset(skb->cb, 0, sizeof(skb->cb)); + ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto); + + if (ieee->iw_mode == IW_MODE_MONITOR) { + txb = rtllib_alloc_txb(1, skb->len, GFP_ATOMIC); + if (unlikely(!txb)) { + netdev_warn(ieee->dev, + "Could not allocate TXB\n"); + goto failed; + } + + txb->encrypted = 0; + txb->payload_size = cpu_to_le16(skb->len); + skb_put_data(txb->fragments[0], skb->data, skb->len); + + goto success; + } + + if (skb->len > 282) { + if (ether_type == ETH_P_IP) { + const struct iphdr *ip = (struct iphdr *) + ((u8 *)skb->data + 14); + if (ip->protocol == IPPROTO_UDP) { + struct udphdr *udp; + + udp = (struct udphdr *)((u8 *)ip + + (ip->ihl << 2)); + if (((((u8 *)udp)[1] == 68) && + (((u8 *)udp)[3] == 67)) || + ((((u8 *)udp)[1] == 67) && + (((u8 *)udp)[3] == 68))) { + bdhcp = true; + ieee->LPSDelayCnt = 200; + } + } + } else if (ether_type == ETH_P_ARP) { + netdev_info(ieee->dev, + "=================>DHCP Protocol start tx ARP pkt!!\n"); + bdhcp = true; + ieee->LPSDelayCnt = + ieee->current_network.tim.tim_count; + } + } + + skb->priority = rtllib_classify(skb, IsAmsdu); + crypt = ieee->crypt_info.crypt[ieee->crypt_info.tx_keyidx]; + encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) && crypt && crypt->ops; + if (!encrypt && ieee->ieee802_1x && + ieee->drop_unencrypted && ether_type != ETH_P_PAE) { + stats->tx_dropped++; + goto success; + } + if (crypt && !encrypt && ether_type == ETH_P_PAE) { + struct eapol *eap = (struct eapol *)(skb->data + + sizeof(struct ethhdr) - SNAP_SIZE - + sizeof(u16)); + netdev_dbg(ieee->dev, + "TX: IEEE 802.11 EAPOL frame: %s\n", + eap_get_type(eap->type)); + } + + /* Advance the SKB to the start of the payload */ + skb_pull(skb, sizeof(struct ethhdr)); + + /* Determine total amount of storage required for TXB packets */ + bytes = skb->len + SNAP_SIZE + sizeof(u16); + + if (encrypt) + fc = RTLLIB_FTYPE_DATA | RTLLIB_FCTL_WEP; + else + fc = RTLLIB_FTYPE_DATA; + + if (qos_activated) + fc |= RTLLIB_STYPE_QOS_DATA; + else + fc |= RTLLIB_STYPE_DATA; + + if (ieee->iw_mode == IW_MODE_INFRA) { + fc |= RTLLIB_FCTL_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, + * Addr3 = DA + */ + ether_addr_copy(header.addr1, + ieee->current_network.bssid); + ether_addr_copy(header.addr2, src); + if (IsAmsdu) + ether_addr_copy(header.addr3, + ieee->current_network.bssid); + else + ether_addr_copy(header.addr3, dest); + } else if (ieee->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, + * Addr3 = BSSID + */ + ether_addr_copy(header.addr1, dest); + ether_addr_copy(header.addr2, src); + ether_addr_copy(header.addr3, + ieee->current_network.bssid); + } + + bIsMulticast = is_multicast_ether_addr(header.addr1); + + header.frame_ctl = cpu_to_le16(fc); + + /* Determine fragmentation size based on destination (multicast + * and broadcast are not fragmented) + */ + if (bIsMulticast) { + frag_size = MAX_FRAG_THRESHOLD; + qos_ctl |= QOS_CTL_NOTCONTAIN_ACK; + } else { + frag_size = ieee->fts; + qos_ctl = 0; + } + + if (qos_activated) { + hdr_len = RTLLIB_3ADDR_LEN + 2; + + /* in case we are a client verify acm is not set for this ac */ + while (unlikely(ieee->wmm_acm & (0x01 << skb->priority))) { + netdev_info(ieee->dev, "skb->priority = %x\n", + skb->priority); + if (wme_downgrade_ac(skb)) + break; + netdev_info(ieee->dev, "converted skb->priority = %x\n", + skb->priority); + } + + qos_ctl |= skb->priority; + header.qos_ctl = cpu_to_le16(qos_ctl & RTLLIB_QOS_TID); + + } else { + hdr_len = RTLLIB_3ADDR_LEN; + } + /* Determine amount of payload per fragment. Regardless of if + * this stack is providing the full 802.11 header, one will + * eventually be affixed to this fragment -- so we must account + * for it when determining the amount of payload space. + */ + bytes_per_frag = frag_size - hdr_len; + if (ieee->config & + (CFG_RTLLIB_COMPUTE_FCS | CFG_RTLLIB_RESERVE_FCS)) + bytes_per_frag -= RTLLIB_FCS_LEN; + + /* Each fragment may need to have room for encrypting + * pre/postfix + */ + if (encrypt) { + bytes_per_frag -= crypt->ops->extra_mpdu_prefix_len + + crypt->ops->extra_mpdu_postfix_len + + crypt->ops->extra_msdu_prefix_len + + crypt->ops->extra_msdu_postfix_len; + } + /* Number of fragments is the total bytes_per_frag / + * payload_per_fragment + */ + nr_frags = bytes / bytes_per_frag; + bytes_last_frag = bytes % bytes_per_frag; + if (bytes_last_frag) + nr_frags++; + else + bytes_last_frag = bytes_per_frag; + + /* When we allocate the TXB we allocate enough space for the + * reserve and full fragment bytes (bytes_per_frag doesn't + * include prefix, postfix, header, FCS, etc.) + */ + txb = rtllib_alloc_txb(nr_frags, frag_size + + ieee->tx_headroom, GFP_ATOMIC); + if (unlikely(!txb)) { + netdev_warn(ieee->dev, "Could not allocate TXB\n"); + goto failed; + } + txb->encrypted = encrypt; + txb->payload_size = cpu_to_le16(bytes); + + if (qos_activated) + txb->queue_index = UP2AC(skb->priority); + else + txb->queue_index = WME_AC_BE; + + for (i = 0; i < nr_frags; i++) { + skb_frag = txb->fragments[i]; + tcb_desc = (struct cb_desc *)(skb_frag->cb + + MAX_DEV_ADDR_SIZE); + if (qos_activated) { + skb_frag->priority = skb->priority; + tcb_desc->queue_index = UP2AC(skb->priority); + } else { + skb_frag->priority = WME_AC_BE; + tcb_desc->queue_index = WME_AC_BE; + } + skb_reserve(skb_frag, ieee->tx_headroom); + + if (encrypt) { + if (ieee->hwsec_active) + tcb_desc->bHwSec = 1; + else + tcb_desc->bHwSec = 0; + skb_reserve(skb_frag, + crypt->ops->extra_mpdu_prefix_len + + crypt->ops->extra_msdu_prefix_len); + } else { + tcb_desc->bHwSec = 0; + } + frag_hdr = skb_put_data(skb_frag, &header, hdr_len); + + /* If this is not the last fragment, then add the + * MOREFRAGS bit to the frame control + */ + if (i != nr_frags - 1) { + frag_hdr->frame_ctl = cpu_to_le16(fc | + RTLLIB_FCTL_MOREFRAGS); + bytes = bytes_per_frag; + + } else { + /* The last fragment has the remaining length */ + bytes = bytes_last_frag; + } + if ((qos_activated) && (!bIsMulticast)) { + frag_hdr->seq_ctl = + cpu_to_le16(rtllib_query_seqnum(ieee, skb_frag, + header.addr1)); + frag_hdr->seq_ctl = + cpu_to_le16(le16_to_cpu(frag_hdr->seq_ctl) << 4 | i); + } else { + frag_hdr->seq_ctl = + cpu_to_le16(ieee->seq_ctrl[0] << 4 | i); + } + /* Put a SNAP header on the first fragment */ + if (i == 0) { + rtllib_put_snap(skb_put(skb_frag, + SNAP_SIZE + + sizeof(u16)), ether_type); + bytes -= SNAP_SIZE + sizeof(u16); + } + + skb_put_data(skb_frag, skb->data, bytes); + + /* Advance the SKB... */ + skb_pull(skb, bytes); + + /* Encryption routine will move the header forward in + * order to insert the IV between the header and the + * payload + */ + if (encrypt) + rtllib_encrypt_fragment(ieee, skb_frag, + hdr_len); + if (ieee->config & + (CFG_RTLLIB_COMPUTE_FCS | CFG_RTLLIB_RESERVE_FCS)) + skb_put(skb_frag, 4); + } + + if ((qos_activated) && (!bIsMulticast)) { + if (ieee->seq_ctrl[UP2AC(skb->priority) + 1] == 0xFFF) + ieee->seq_ctrl[UP2AC(skb->priority) + 1] = 0; + else + ieee->seq_ctrl[UP2AC(skb->priority) + 1]++; + } else { + if (ieee->seq_ctrl[0] == 0xFFF) + ieee->seq_ctrl[0] = 0; + else + ieee->seq_ctrl[0]++; + } + } else { + if (unlikely(skb->len < sizeof(struct rtllib_hdr_3addr))) { + netdev_warn(ieee->dev, "skb too small (%d).\n", + skb->len); + goto success; + } + + txb = rtllib_alloc_txb(1, skb->len, GFP_ATOMIC); + if (!txb) { + netdev_warn(ieee->dev, "Could not allocate TXB\n"); + goto failed; + } + + txb->encrypted = 0; + txb->payload_size = cpu_to_le16(skb->len); + skb_put_data(txb->fragments[0], skb->data, skb->len); + } + + success: + if (txb) { + tcb_desc = (struct cb_desc *) + (txb->fragments[0]->cb + MAX_DEV_ADDR_SIZE); + tcb_desc->bTxEnableFwCalcDur = 1; + tcb_desc->priority = skb->priority; + + if (ether_type == ETH_P_PAE) { + if (ieee->ht_info->iot_action & + HT_IOT_ACT_WA_IOT_Broadcom) { + tcb_desc->data_rate = + MgntQuery_TxRateExcludeCCKRates(ieee); + tcb_desc->tx_dis_rate_fallback = false; + } else { + tcb_desc->data_rate = ieee->basic_rate; + tcb_desc->tx_dis_rate_fallback = 1; + } + + tcb_desc->ratr_index = 7; + tcb_desc->tx_use_drv_assinged_rate = 1; + } else { + if (is_multicast_ether_addr(header.addr1)) + tcb_desc->bMulticast = 1; + if (is_broadcast_ether_addr(header.addr1)) + tcb_desc->bBroadcast = 1; + rtllib_txrate_selectmode(ieee, tcb_desc); + if (tcb_desc->bMulticast || tcb_desc->bBroadcast) + tcb_desc->data_rate = ieee->basic_rate; + else + tcb_desc->data_rate = rtllib_current_rate(ieee); + + if (bdhcp) { + if (ieee->ht_info->iot_action & + HT_IOT_ACT_WA_IOT_Broadcom) { + tcb_desc->data_rate = + MgntQuery_TxRateExcludeCCKRates(ieee); + tcb_desc->tx_dis_rate_fallback = false; + } else { + tcb_desc->data_rate = MGN_1M; + tcb_desc->tx_dis_rate_fallback = 1; + } + + tcb_desc->ratr_index = 7; + tcb_desc->tx_use_drv_assinged_rate = 1; + tcb_desc->bdhcp = 1; + } + + rtllib_query_ShortPreambleMode(ieee, tcb_desc); + rtllib_tx_query_agg_cap(ieee, txb->fragments[0], + tcb_desc); + rtllib_query_HTCapShortGI(ieee, tcb_desc); + rtllib_query_BandwidthMode(ieee, tcb_desc); + rtllib_query_protectionmode(ieee, tcb_desc, + txb->fragments[0]); + } + } + spin_unlock_irqrestore(&ieee->lock, flags); + dev_kfree_skb_any(skb); + if (txb) { + if (ieee->softmac_features & IEEE_SOFTMAC_TX_QUEUE) { + dev->stats.tx_packets++; + dev->stats.tx_bytes += le16_to_cpu(txb->payload_size); + rtllib_softmac_xmit(txb, ieee); + } else { + rtllib_txb_free(txb); + } + } + + return 0; + + failed: + spin_unlock_irqrestore(&ieee->lock, flags); + netif_stop_queue(dev); + stats->tx_errors++; + return 1; +} + +netdev_tx_t rtllib_xmit(struct sk_buff *skb, struct net_device *dev) +{ + memset(skb->cb, 0, sizeof(skb->cb)); + return rtllib_xmit_inter(skb, dev) ? NETDEV_TX_BUSY : NETDEV_TX_OK; +} +EXPORT_SYMBOL(rtllib_xmit); diff --git a/drivers/staging/rtl8192e/rtllib_wx.c b/drivers/staging/rtl8192e/rtllib_wx.c new file mode 100644 index 0000000000..e9469bfef3 --- /dev/null +++ b/drivers/staging/rtl8192e/rtllib_wx.c @@ -0,0 +1,752 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2004 Intel Corporation. All rights reserved. + * + * Portions of this file are based on the WEP enablement code provided by the + * Host AP project hostap-drivers v0.1.3 + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * <jkmaline@cc.hut.fi> + * Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> + * + * Contact Information: + * James P. Ketrenos <ipw2100-admin@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + */ +#include <linux/wireless.h> +#include <linux/kmod.h> +#include <linux/module.h> +#include <linux/etherdevice.h> +#include "rtllib.h" + +static const char * const rtllib_modes[] = { + "a", "b", "g", "?", "N-24G" +}; + +#define MAX_CUSTOM_LEN 64 +static inline char *rtl819x_translate_scan(struct rtllib_device *ieee, + char *start, char *stop, + struct rtllib_network *network, + struct iw_request_info *info) +{ + char custom[MAX_CUSTOM_LEN]; + char proto_name[IFNAMSIZ]; + char *pname = proto_name; + char *p; + struct iw_event iwe; + int i, j; + u16 max_rate, rate; + static u8 EWC11NHTCap[] = {0x00, 0x90, 0x4c, 0x33}; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + ether_addr_copy(iwe.u.ap_addr.sa_data, network->bssid); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_ADDR_LEN); + /* Remaining entries will be displayed in the order we provide them */ + + /* Add the ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + if (network->ssid_len > 0) { + iwe.u.data.length = min_t(u8, network->ssid_len, 32); + start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid); + } else if (network->hidden_ssid_len == 0) { + iwe.u.data.length = sizeof("<hidden>"); + start = iwe_stream_add_point(info, start, stop, &iwe, "<hidden>"); + } else { + iwe.u.data.length = min_t(u8, network->hidden_ssid_len, 32); + start = iwe_stream_add_point(info, start, stop, &iwe, network->hidden_ssid); + } + /* Add the protocol name */ + iwe.cmd = SIOCGIWNAME; + for (i = 0; i < ARRAY_SIZE(rtllib_modes); i++) { + if (network->mode & BIT(i)) { + strcpy(pname, rtllib_modes[i]); + pname += strlen(rtllib_modes[i]); + } + } + *pname = '\0'; + snprintf(iwe.u.name, IFNAMSIZ, "IEEE802.11%s", proto_name); + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (network->capability & + (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { + if (network->capability & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_UINT_LEN); + } + + /* Add frequency/channel */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = network->channel; + iwe.u.freq.e = 0; + iwe.u.freq.i = 0; + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (network->capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + start = iwe_stream_add_point(info, start, stop, &iwe, network->ssid); + /* Add basic and extended rates */ + max_rate = 0; + p = custom; + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); + for (i = 0, j = 0; i < network->rates_len;) { + if (j < network->rates_ex_len && + ((network->rates_ex[j] & 0x7F) < + (network->rates[i] & 0x7F))) + rate = network->rates_ex[j++] & 0x7F; + else + rate = network->rates[i++] & 0x7F; + if (rate > max_rate) + max_rate = rate; + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + } + for (; j < network->rates_ex_len; j++) { + rate = network->rates_ex[j] & 0x7F; + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + if (rate > max_rate) + max_rate = rate; + } + + if (network->mode >= WIRELESS_MODE_N_24G) { + struct ht_capab_ele *ht_cap = NULL; + bool is40M = false, isShortGI = false; + u8 max_mcs = 0; + + if (!memcmp(network->bssht.bd_ht_cap_buf, EWC11NHTCap, 4)) + ht_cap = (struct ht_capab_ele *) + &network->bssht.bd_ht_cap_buf[4]; + else + ht_cap = (struct ht_capab_ele *) + &network->bssht.bd_ht_cap_buf[0]; + is40M = (ht_cap->ChlWidth) ? 1 : 0; + isShortGI = (ht_cap->ChlWidth) ? + ((ht_cap->ShortGI40Mhz) ? 1 : 0) : + ((ht_cap->ShortGI20Mhz) ? 1 : 0); + + max_mcs = HTGetHighestMCSRate(ieee, ht_cap->MCS, + MCS_FILTER_ALL); + rate = MCS_DATA_RATE[is40M][isShortGI][max_mcs & 0x7f]; + if (rate > max_rate) + max_rate = rate; + } + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.value = max_rate * 500000; + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_PARAM_LEN); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); + /* Add quality statistics */ + /* TODO: Fix these values... */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = network->stats.signal; + iwe.u.qual.level = network->stats.rssi; + iwe.u.qual.noise = network->stats.noise; + iwe.u.qual.updated = network->stats.mask & RTLLIB_STATMASK_WEMASK; + if (!(network->stats.mask & RTLLIB_STATMASK_RSSI)) + iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; + if (!(network->stats.mask & RTLLIB_STATMASK_NOISE)) + iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; + if (!(network->stats.mask & RTLLIB_STATMASK_SIGNAL)) + iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID; + iwe.u.qual.updated = 7; + start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_QUAL_LEN); + + iwe.cmd = IWEVCUSTOM; + p = custom; + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); + + memset(&iwe, 0, sizeof(iwe)); + if (network->wpa_ie_len) { + char buf[MAX_WPA_IE_LEN]; + + memcpy(buf, network->wpa_ie, network->wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = network->wpa_ie_len; + start = iwe_stream_add_point(info, start, stop, &iwe, buf); + } + memset(&iwe, 0, sizeof(iwe)); + if (network->rsn_ie_len) { + char buf[MAX_WPA_IE_LEN]; + + memcpy(buf, network->rsn_ie, network->rsn_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = network->rsn_ie_len; + start = iwe_stream_add_point(info, start, stop, &iwe, buf); + } + + /* add info for WZC */ + memset(&iwe, 0, sizeof(iwe)); + if (network->wzc_ie_len) { + char buf[MAX_WZC_IE_LEN]; + + memcpy(buf, network->wzc_ie, network->wzc_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = network->wzc_ie_len; + start = iwe_stream_add_point(info, start, stop, &iwe, buf); + } + + /* Add EXTRA: Age to display seconds since last beacon/probe response + * for given network. + */ + iwe.cmd = IWEVCUSTOM; + p = custom; + p += scnprintf(p, MAX_CUSTOM_LEN - (p - custom), + " Last beacon: %lums ago", + (100 * (jiffies - network->last_scanned)) / HZ); + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(info, start, stop, &iwe, custom); + + return start; +} + +int rtllib_wx_get_scan(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct rtllib_network *network; + unsigned long flags; + + char *ev = extra; + char *stop = ev + wrqu->data.length; + int i = 0; + int err = 0; + + netdev_dbg(ieee->dev, "Getting scan\n"); + mutex_lock(&ieee->wx_mutex); + spin_lock_irqsave(&ieee->lock, flags); + + list_for_each_entry(network, &ieee->network_list, list) { + i++; + if ((stop - ev) < 200) { + err = -E2BIG; + break; + } + if (ieee->scan_age == 0 || + time_after(network->last_scanned + ieee->scan_age, jiffies)) + ev = rtl819x_translate_scan(ieee, ev, stop, network, + info); + else + netdev_dbg(ieee->dev, + "Network '%s ( %pM)' hidden due to age (%lums).\n", + escape_essid(network->ssid, + network->ssid_len), + network->bssid, + (100 * (jiffies - network->last_scanned)) / + HZ); + } + + spin_unlock_irqrestore(&ieee->lock, flags); + mutex_unlock(&ieee->wx_mutex); + wrqu->data.length = ev - extra; + wrqu->data.flags = 0; + + netdev_dbg(ieee->dev, "%s(): %d networks returned.\n", __func__, i); + + return err; +} +EXPORT_SYMBOL(rtllib_wx_get_scan); + +int rtllib_wx_set_encode(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &wrqu->encoding; + struct net_device *dev = ieee->dev; + struct rtllib_security sec = { + .flags = 0 + }; + int i, key, key_provided, len; + struct lib80211_crypt_data **crypt; + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > NUM_WEP_KEYS) + return -EINVAL; + key--; + key_provided = 1; + } else { + key_provided = 0; + key = ieee->crypt_info.tx_keyidx; + } + + netdev_dbg(ieee->dev, "Key: %d [%s]\n", key, key_provided ? + "provided" : "default"); + crypt = &ieee->crypt_info.crypt[key]; + if (erq->flags & IW_ENCODE_DISABLED) { + if (key_provided && *crypt) { + netdev_dbg(ieee->dev, + "Disabling encryption on key %d.\n", key); + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + } else { + netdev_dbg(ieee->dev, "Disabling encryption.\n"); + } + + /* Check all the keys to see if any are still configured, + * and if no key index was provided, de-init them all + */ + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ieee->crypt_info.crypt[i]) { + if (key_provided) + break; + lib80211_crypt_delayed_deinit(&ieee->crypt_info, + &ieee->crypt_info.crypt[i]); + } + } + + if (i == NUM_WEP_KEYS) { + sec.enabled = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_ENABLED | SEC_LEVEL; + } + + goto done; + } + + sec.enabled = 1; + sec.flags |= SEC_ENABLED; + + if (*crypt && (*crypt)->ops && + strcmp((*crypt)->ops->name, "R-WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm + * on this key + */ + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + } + + if (!*crypt) { + struct lib80211_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); + if (!new_crypt) + return -ENOMEM; + new_crypt->ops = lib80211_get_crypto_ops("R-WEP"); + if (!new_crypt->ops) { + request_module("rtllib_crypt_wep"); + new_crypt->ops = lib80211_get_crypto_ops("R-WEP"); + } + + if (new_crypt->ops) + new_crypt->priv = new_crypt->ops->init(key); + + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + netdev_warn(dev, + "%s: could not initialize WEP: load module rtllib_crypt_wep\n", + dev->name); + return -EOPNOTSUPP; + } + *crypt = new_crypt; + } + + /* If a new key was provided, set it up */ + if (erq->length > 0) { + len = erq->length <= 5 ? 5 : 13; + memcpy(sec.keys[key], keybuf, erq->length); + if (len > erq->length) + memset(sec.keys[key] + erq->length, 0, + len - erq->length); + netdev_dbg(ieee->dev, "Setting key %d to '%s' (%d:%d bytes)\n", + key, escape_essid(sec.keys[key], len), erq->length, + len); + sec.key_sizes[key] = len; + (*crypt)->ops->set_key(sec.keys[key], len, NULL, + (*crypt)->priv); + sec.flags |= (1 << key); + /* This ensures a key will be activated if no key is + * explicitly set + */ + if (key == sec.active_key) + sec.flags |= SEC_ACTIVE_KEY; + ieee->crypt_info.tx_keyidx = key; + + } else { + len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, + NULL, (*crypt)->priv); + if (len == 0) { + /* Set a default key of all 0 */ + netdev_info(ieee->dev, "Setting key %d to all zero.\n", key); + + memset(sec.keys[key], 0, 13); + (*crypt)->ops->set_key(sec.keys[key], 13, NULL, + (*crypt)->priv); + sec.key_sizes[key] = 13; + sec.flags |= (1 << key); + } + + /* No key data - just set the default TX key index */ + if (key_provided) { + netdev_dbg(ieee->dev, + "Setting key %d as default Tx key.\n", key); + ieee->crypt_info.tx_keyidx = key; + sec.active_key = key; + sec.flags |= SEC_ACTIVE_KEY; + } + } + done: + ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); + ieee->auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : + WLAN_AUTH_SHARED_KEY; + sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY; + sec.flags |= SEC_AUTH_MODE; + netdev_dbg(ieee->dev, "Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ? + "OPEN" : "SHARED KEY"); + + /* For now we just support WEP, so only set that security level... + * TODO: When WPA is added this is one place that needs to change + */ + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ + return 0; +} +EXPORT_SYMBOL(rtllib_wx_set_encode); + +int rtllib_wx_get_encode(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &wrqu->encoding; + int len, key; + struct lib80211_crypt_data *crypt; + + if (ieee->iw_mode == IW_MODE_MONITOR) + return -1; + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > NUM_WEP_KEYS) + return -EINVAL; + key--; + } else { + key = ieee->crypt_info.tx_keyidx; + } + crypt = ieee->crypt_info.crypt[key]; + + erq->flags = key + 1; + + if (!crypt || !crypt->ops) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + len = crypt->ops->get_key(keybuf, SCM_KEY_LEN, NULL, crypt->priv); + + erq->length = max(len, 0); + + erq->flags |= IW_ENCODE_ENABLED; + + if (ieee->open_wep) + erq->flags |= IW_ENCODE_OPEN; + else + erq->flags |= IW_ENCODE_RESTRICTED; + + return 0; +} +EXPORT_SYMBOL(rtllib_wx_get_encode); + +int rtllib_wx_set_encode_ext(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + struct net_device *dev = ieee->dev; + struct iw_point *encoding = &wrqu->encoding; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int i, idx; + int group_key = 0; + const char *alg, *module; + struct lib80211_crypto_ops *ops; + struct lib80211_crypt_data **crypt; + + struct rtllib_security sec = { + .flags = 0, + }; + idx = encoding->flags & IW_ENCODE_INDEX; + if (idx) { + if (idx < 1 || idx > NUM_WEP_KEYS) + return -EINVAL; + idx--; + } else { + idx = ieee->crypt_info.tx_keyidx; + } + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { + crypt = &ieee->crypt_info.crypt[idx]; + group_key = 1; + } else { + /* some Cisco APs use idx>0 for unicast in dynamic WEP */ + if (idx != 0 && ext->alg != IW_ENCODE_ALG_WEP) + return -EINVAL; + if (ieee->iw_mode == IW_MODE_INFRA) + crypt = &ieee->crypt_info.crypt[idx]; + else + return -EINVAL; + } + + sec.flags |= SEC_ENABLED; + if ((encoding->flags & IW_ENCODE_DISABLED) || + ext->alg == IW_ENCODE_ALG_NONE) { + if (*crypt) + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ieee->crypt_info.crypt[i]) + break; + } + if (i == NUM_WEP_KEYS) { + sec.enabled = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_LEVEL; + } + goto done; + } + + sec.enabled = 1; + switch (ext->alg) { + case IW_ENCODE_ALG_WEP: + alg = "R-WEP"; + module = "rtllib_crypt_wep"; + break; + case IW_ENCODE_ALG_TKIP: + alg = "R-TKIP"; + module = "rtllib_crypt_tkip"; + break; + case IW_ENCODE_ALG_CCMP: + alg = "R-CCMP"; + module = "rtllib_crypt_ccmp"; + break; + default: + netdev_dbg(ieee->dev, "Unknown crypto alg %d\n", ext->alg); + ret = -EINVAL; + goto done; + } + netdev_dbg(dev, "alg name:%s\n", alg); + + ops = lib80211_get_crypto_ops(alg); + if (!ops) { + char tempbuf[100]; + + memset(tempbuf, 0x00, 100); + sprintf(tempbuf, "%s", module); + request_module("%s", tempbuf); + ops = lib80211_get_crypto_ops(alg); + } + if (!ops) { + netdev_info(dev, "========>unknown crypto alg %d\n", ext->alg); + ret = -EINVAL; + goto done; + } + + if (!*crypt || (*crypt)->ops != ops) { + struct lib80211_crypt_data *new_crypt; + + lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); + + new_crypt = kzalloc(sizeof(*new_crypt), GFP_KERNEL); + if (!new_crypt) { + ret = -ENOMEM; + goto done; + } + new_crypt->ops = ops; + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(idx); + + if (!new_crypt->priv) { + kfree(new_crypt); + ret = -EINVAL; + goto done; + } + *crypt = new_crypt; + } + + if (ext->key_len > 0 && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, + (*crypt)->priv) < 0) { + netdev_info(dev, "key setting failed\n"); + ret = -EINVAL; + goto done; + } + if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { + ieee->crypt_info.tx_keyidx = idx; + sec.active_key = idx; + sec.flags |= SEC_ACTIVE_KEY; + } + if (ext->alg != IW_ENCODE_ALG_NONE) { + sec.key_sizes[idx] = ext->key_len; + sec.flags |= (1 << idx); + if (ext->alg == IW_ENCODE_ALG_WEP) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; + } else if (ext->alg == IW_ENCODE_ALG_TKIP) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_2; + } else if (ext->alg == IW_ENCODE_ALG_CCMP) { + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_3; + } + /* Don't set sec level for group keys. */ + if (group_key) + sec.flags &= ~SEC_LEVEL; + } +done: + return ret; +} +EXPORT_SYMBOL(rtllib_wx_set_encode_ext); + +int rtllib_wx_set_mlme(struct rtllib_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + u8 i = 0; + bool deauth = false; + struct iw_mlme *mlme = (struct iw_mlme *)extra; + + if (ieee->link_state != MAC80211_LINKED) + return -ENOLINK; + + mutex_lock(&ieee->wx_mutex); + + switch (mlme->cmd) { + case IW_MLME_DEAUTH: + deauth = true; + fallthrough; + case IW_MLME_DISASSOC: + if (deauth) + netdev_info(ieee->dev, "disauth packet !\n"); + else + netdev_info(ieee->dev, "dis associate packet!\n"); + + ieee->cannot_notify = true; + + SendDisassociation(ieee, deauth, mlme->reason_code); + rtllib_disassociate(ieee); + + ieee->wap_set = 0; + for (i = 0; i < 6; i++) + ieee->current_network.bssid[i] = 0x55; + + ieee->ssid_set = 0; + ieee->current_network.ssid[0] = '\0'; + ieee->current_network.ssid_len = 0; + break; + default: + mutex_unlock(&ieee->wx_mutex); + return -EOPNOTSUPP; + } + + mutex_unlock(&ieee->wx_mutex); + + return 0; +} +EXPORT_SYMBOL(rtllib_wx_set_mlme); + +int rtllib_wx_set_auth(struct rtllib_device *ieee, + struct iw_request_info *info, + struct iw_param *data, char *extra) +{ + switch (data->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + break; + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* Host AP driver does not use these parameters and allows + * wpa_supplicant to control them internally. + */ + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + ieee->tkip_countermeasures = data->value; + break; + case IW_AUTH_DROP_UNENCRYPTED: + ieee->drop_unencrypted = data->value; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (data->value & IW_AUTH_ALG_SHARED_KEY) { + ieee->open_wep = 0; + ieee->auth_mode = 1; + } else if (data->value & IW_AUTH_ALG_OPEN_SYSTEM) { + ieee->open_wep = 1; + ieee->auth_mode = 0; + } else if (data->value & IW_AUTH_ALG_LEAP) { + ieee->open_wep = 1; + ieee->auth_mode = 2; + } else { + return -EINVAL; + } + break; + + case IW_AUTH_WPA_ENABLED: + ieee->wpa_enabled = (data->value) ? 1 : 0; + break; + + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + ieee->ieee802_1x = data->value; + break; + case IW_AUTH_PRIVACY_INVOKED: + ieee->privacy_invoked = data->value; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} +EXPORT_SYMBOL(rtllib_wx_set_auth); + +int rtllib_wx_set_gen_ie(struct rtllib_device *ieee, u8 *ie, size_t len) +{ + u8 *buf; + u8 eid, wps_oui[4] = {0x0, 0x50, 0xf2, 0x04}; + + if (len > MAX_WPA_IE_LEN || (len && !ie)) + return -EINVAL; + + if (len) { + eid = ie[0]; + if ((eid == MFIE_TYPE_GENERIC) && (!memcmp(&ie[2], wps_oui, 4))) { + ieee->wps_ie_len = min_t(size_t, len, MAX_WZC_IE_LEN); + buf = kmemdup(ie, ieee->wps_ie_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ieee->wps_ie = buf; + return 0; + } + } + ieee->wps_ie_len = 0; + kfree(ieee->wps_ie); + ieee->wps_ie = NULL; + if (len) { + if (len != ie[1] + 2) + return -EINVAL; + buf = kmemdup(ie, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + kfree(ieee->wpa_ie); + ieee->wpa_ie = buf; + ieee->wpa_ie_len = len; + } else { + kfree(ieee->wpa_ie); + ieee->wpa_ie = NULL; + ieee->wpa_ie_len = 0; + } + return 0; +} +EXPORT_SYMBOL(rtllib_wx_set_gen_ie); |