7385 lines
271 KiB
C
Executable File
7385 lines
271 KiB
C
Executable File
|
|
#include <linux/types.h>
|
|
#include <linux/version.h>
|
|
#include <net/cfg80211.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/ctype.h>
|
|
|
|
#include "nvt_icfg.h"
|
|
#include "nvt_wlan_linux.h"
|
|
#include "nvt_util_dbg.h"
|
|
#include "nvt_iw.h"
|
|
#include "nvt_bus_sdioif.h"
|
|
|
|
#define NVT_NUM_SCAN_MAX 5
|
|
#define NVT_SCAN_VSIE_LEN_MAX 512
|
|
#define MAX_EP0_SIZE 1024
|
|
#define RTS_THRESHOLD_LIMIT 255
|
|
#define FRAG_LOW_LIMIT 255
|
|
#define FRAG_UP_LIMIT 7936
|
|
#define RETRY_LIMIT 255
|
|
|
|
#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
|
|
#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
|
|
#define TLV_HDR_LEN 2
|
|
|
|
//AT: combine reg0 & reg1/ reg2 & reg3
|
|
#define WOW_MAXPATTERNS 4
|
|
#define WOW_MAXPATTERNLEN 31
|
|
#define POLYNOMIAL 0x8005
|
|
#define INITIAL_REMAINDER 0xFFFF
|
|
#define FINAL_XOR_VALUE 0x0000
|
|
#define REVERSE_DATA(X) ((u8) reverse((X), 8))
|
|
#define REVERSE_REMAINDER(X) (X)
|
|
#define WIDTH (8 * sizeof(u16))
|
|
#define TOPBIT (1 << (WIDTH - 1))
|
|
|
|
/* wow config parameters */
|
|
#define WOW_MAGIC_PKT 0x01
|
|
#define WOW_PATTERN 0x02
|
|
#define WOW_DISCONNECT 0x04
|
|
#define WOW_4_REG 0x08
|
|
#define WOW_2_REG 0x10
|
|
#define WOW_1_REG 0x20
|
|
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
#define MAC2STR(a) ((a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
|
|
#endif
|
|
|
|
#define CHAN2G(_channel, _freq, _flags) { \
|
|
.band = IEEE80211_BAND_2GHZ, \
|
|
.hw_value = (_channel), \
|
|
.center_freq = (_freq), \
|
|
.flags = (_flags), \
|
|
.max_antenna_gain = 0, \
|
|
.max_power = 30, \
|
|
}
|
|
|
|
#define RATETAB_ENT(_rate, _rateid, _flags) { \
|
|
.bitrate = (_rate), \
|
|
.flags = (_flags), \
|
|
.hw_value = (_rateid), \
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
|
|
#define WLAN_OUI_MICROSOFT 0x0050f2
|
|
#define WLAN_OUI_TYPE_MICROSOFT_WPA 1
|
|
#define WLAN_OUI_TYPE_MICROSOFT_WMM 2
|
|
#define WLAN_OUI_TYPE_MICROSOFT_WPS 4
|
|
#endif
|
|
|
|
/* Reset Varialble here */
|
|
u8 g_wep_type = 0;
|
|
u8 g_wep_is_used = 0;
|
|
u8 g_start_op = DONT_RESET;
|
|
|
|
static struct ieee80211_channel nvt_2ghz_channels[] = {
|
|
CHAN2G(1, 2412, 0),
|
|
CHAN2G(2, 2417, 0),
|
|
CHAN2G(3, 2422, 0),
|
|
CHAN2G(4, 2427, 0),
|
|
CHAN2G(5, 2432, 0),
|
|
CHAN2G(6, 2437, 0),
|
|
CHAN2G(7, 2442, 0),
|
|
CHAN2G(8, 2447, 0),
|
|
CHAN2G(9, 2452, 0),
|
|
CHAN2G(10, 2457, 0),
|
|
CHAN2G(11, 2462, 0),
|
|
CHAN2G(12, 2467, 0),
|
|
CHAN2G(13, 2472, 0),
|
|
CHAN2G(14, 2484, 0),
|
|
};
|
|
|
|
static struct ieee80211_rate nvt_rates[] = {
|
|
RATETAB_ENT(10, 0x1, 0),
|
|
RATETAB_ENT(20, 0x2, 0),
|
|
RATETAB_ENT(55, 0x4, 0),
|
|
RATETAB_ENT(110, 0x8, 0),
|
|
RATETAB_ENT(60, 0x10, 0),
|
|
RATETAB_ENT(90, 0x20, 0),
|
|
RATETAB_ENT(120, 0x40, 0),
|
|
RATETAB_ENT(180, 0x80, 0),
|
|
RATETAB_ENT(240, 0x100, 0),
|
|
RATETAB_ENT(360, 0x200, 0),
|
|
RATETAB_ENT(480, 0x400, 0),
|
|
RATETAB_ENT(540, 0x800, 0),
|
|
};
|
|
|
|
#define nvt_g_rates (nvt_rates + 0)
|
|
#define nvt_g_rates_size 12
|
|
#define nvt_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
|
|
IEEE80211_HT_CAP_SGI_20 | \
|
|
IEEE80211_HT_CAP_SGI_40 | \
|
|
IEEE80211_HT_CAP_GRN_FLD | \
|
|
IEEE80211_HT_CAP_DSSSCCK40 | \
|
|
IEEE80211_HT_CAP_TX_STBC | \
|
|
IEEE80211_HT_CAP_RX_STBC)
|
|
|
|
static struct ieee80211_supported_band nvt_band_2ghz = {
|
|
.n_channels = ARRAY_SIZE(nvt_2ghz_channels),
|
|
.channels = nvt_2ghz_channels,
|
|
.n_bitrates = nvt_g_rates_size,
|
|
.bitrates = nvt_g_rates,
|
|
.ht_cap.cap = nvt_g_htcap,
|
|
.ht_cap.ht_supported = true,
|
|
};
|
|
|
|
static const struct ieee80211_txrx_stypes
|
|
nvt_txrx_stypes[NUM_NL80211_IFTYPES] = {
|
|
[NL80211_IFTYPE_STATION] = {
|
|
.tx = 0xffff,
|
|
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
|
|
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
|
|
},
|
|
[NL80211_IFTYPE_AP] = {
|
|
.tx = 0xffff,
|
|
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
|
|
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
|
|
},
|
|
[NL80211_IFTYPE_P2P_CLIENT] = {
|
|
.tx = 0xffff,
|
|
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
|
|
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
|
|
},
|
|
[NL80211_IFTYPE_P2P_GO] = {
|
|
.tx = 0xffff,
|
|
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
|
|
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
|
|
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
|
|
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
|
|
BIT(IEEE80211_STYPE_AUTH >> 4) |
|
|
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
|
|
BIT(IEEE80211_STYPE_ACTION >> 4)
|
|
}
|
|
};
|
|
|
|
static const struct ieee80211_regdomain nvt_regdom_custom = {
|
|
.n_reg_rules = 2,
|
|
.alpha2 = "99",
|
|
.reg_rules = {
|
|
REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
|
|
REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
|
|
}
|
|
};
|
|
|
|
static const u32 nvt_cipher_suites[] = {
|
|
WLAN_CIPHER_SUITE_WEP40,
|
|
WLAN_CIPHER_SUITE_WEP104,
|
|
WLAN_CIPHER_SUITE_TKIP,
|
|
WLAN_CIPHER_SUITE_CCMP,
|
|
WLAN_CIPHER_SUITE_AES_CMAC,
|
|
WLAN_CIPHER_SUITE_SMS4,
|
|
};
|
|
|
|
static const u16 crctable[256] = {
|
|
0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, 0x8033, 0x0036,
|
|
0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, 0x8063, 0x0066, 0x006c, 0x8069,
|
|
0x0078, 0x807d, 0x8077, 0x0072, 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e,
|
|
0x0044, 0x8041, 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
|
|
0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, 0x00a0, 0x80a5,
|
|
0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, 0x8093, 0x0096, 0x009c, 0x8099,
|
|
0x0088, 0x808d, 0x8087, 0x0082, 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d,
|
|
0x8197, 0x0192, 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
|
|
0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, 0x81d3, 0x01d6,
|
|
0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, 0x0140, 0x8145, 0x814f, 0x014a,
|
|
0x815b, 0x015e, 0x0154, 0x8151, 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d,
|
|
0x8167, 0x0162, 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
|
|
0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, 0x8303, 0x0306,
|
|
0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, 0x0330, 0x8335, 0x833f, 0x033a,
|
|
0x832b, 0x032e, 0x0324, 0x8321, 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e,
|
|
0x0374, 0x8371, 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
|
|
0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, 0x83f3, 0x03f6,
|
|
0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, 0x83a3, 0x03a6, 0x03ac, 0x83a9,
|
|
0x03b8, 0x83bd, 0x83b7, 0x03b2, 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e,
|
|
0x0384, 0x8381, 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
|
|
0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, 0x82e3, 0x02e6,
|
|
0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, 0x02d0, 0x82d5, 0x82df, 0x02da,
|
|
0x82cb, 0x02ce, 0x02c4, 0x82c1, 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d,
|
|
0x8257, 0x0252, 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
|
|
0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, 0x8213, 0x0216,
|
|
0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202};
|
|
|
|
static const u8 extended_capabilities[] = {WLAN_EXT_PTK_OFFLOAD_CAP};
|
|
|
|
/*AT add set_pmk handler for catching command from supplicant*/
|
|
#ifdef VENDOR_CMD_SUPPORT
|
|
static int nvt_set_pmk_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
|
|
const void *data, int data_len)
|
|
{
|
|
struct _nvt_cfg80211 *cfg = (struct _nvt_cfg80211 *)wiphy_priv(wiphy);
|
|
struct _nvt_adapter *nvt_adapter = cfg->nvt_adapter;
|
|
u8 enable_ptkoffload = 1;
|
|
s32 ret = 0;
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* enable f/w internal supplicant */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ENABLE_INT_SUPP, &enable_ptkoffload,
|
|
1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* add psk to f/w */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_11I_PSK_VALUE, (u8 *)data,
|
|
32);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
/*AT add vendor command for supporting PTK offload*/
|
|
static struct wiphy_vendor_command nvt_vendor_commands[] = {
|
|
{
|
|
.info = {.vendor_id = OUI_NVT,
|
|
.subcmd = NVT_PTK_OFFLOAD},
|
|
.flags = WIPHY_VENDOR_CMD_NEED_NETDEV,
|
|
.doit = nvt_set_pmk_cmd
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 0)
|
|
static struct wiphy_wowlan_support nvt_wowlan_support = {
|
|
.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
|
|
.n_patterns = WOW_MAXPATTERNS,
|
|
.pattern_max_len = WOW_MAXPATTERNLEN * 4,
|
|
.pattern_min_len = 1,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
.max_pkt_offset = 255,
|
|
#endif
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
static s32 nvt_cfg80211_start_ap(struct wiphy *, struct net_device *,
|
|
struct cfg80211_ap_settings *);
|
|
static s32 nvt_cfg80211_stop_ap(struct wiphy *, struct net_device *);
|
|
static s32 nvt_cfg80211_change_beacon(struct wiphy *,
|
|
struct net_device *, struct cfg80211_beacon_data *);
|
|
#else
|
|
static int nvt_cfg80211_set_channel(struct wiphy *wiphy,
|
|
struct net_device *dev, struct ieee80211_channel *chan,
|
|
enum nl80211_channel_type channel_type);
|
|
static int nvt_cfg80211_add_beacon(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
struct beacon_parameters *info);
|
|
static int nvt_cfg80211_set_beacon(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
struct beacon_parameters *info);
|
|
static int nvt_cfg80211_del_beacon(struct wiphy *wiphy,
|
|
struct net_device *dev);
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
|
static s32 nvt_cfg80211_del_station(struct wiphy *,
|
|
struct net_device *, u8 *);
|
|
#else
|
|
static s32
|
|
nvt_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
|
|
struct station_del_parameters *params);
|
|
#endif
|
|
static s32
|
|
nvt_cfg80211_change_iface(struct wiphy *, struct net_device *,
|
|
enum nl80211_iftype, u32 *, struct vif_params *);
|
|
static void nvt_scan_init(struct _nvt_cfg80211 *cfg);
|
|
static s32 nvt_filter_p2p_ie(const u8 **, size_t *);
|
|
static s32 nvt_filter_wfd_ie(const u8 **, size_t *);
|
|
|
|
#ifdef CONFIG_PM
|
|
static unsigned long reverse(unsigned long data, unsigned char nbits)
|
|
{
|
|
unsigned long reversed = 0x00000000;
|
|
unsigned char bit;
|
|
|
|
/* Reverse the data about the center bit */
|
|
for (bit = 0; bit < nbits; ++bit) {
|
|
/* If the LSB bit is set, set the reflection of it */
|
|
if (data & 0x01) {
|
|
reversed |= (1 << ((nbits - 1) - bit));
|
|
}
|
|
|
|
data = (data >> 1);
|
|
}
|
|
return reversed;
|
|
}
|
|
|
|
static u16 compute_crc(u32 message[], s32 nbytes)
|
|
{
|
|
u16 remainder = INITIAL_REMAINDER;
|
|
u16 crc = 0;
|
|
u8 data;
|
|
s32 byte;
|
|
|
|
/* Divide the message by the polynomial, a byte at a time */
|
|
for (byte = 0; byte < nbytes; ++byte) {
|
|
data = REVERSE_DATA(message[byte]) ^ (remainder >> (WIDTH - 8));
|
|
remainder = crctable[data] ^ (remainder << 8);
|
|
}
|
|
|
|
/* The final remainder is the CRC */
|
|
crc = REVERSE_REMAINDER(remainder) ^ FINAL_XOR_VALUE;
|
|
return crc;
|
|
}
|
|
#endif
|
|
|
|
/*Check if sta is connected*/
|
|
static bool is_sta_connected(struct _nvt_if *nvt_if)
|
|
{
|
|
if (!test_bit(NVT_IF_CONNECTED, &nvt_if->state_flags)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool check_if_ready(struct _nvt_if *nvt_if)
|
|
{
|
|
if (!test_bit(NVT_IF_ENABLED, &nvt_if->state_flags)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool nvt_is_wpa_ie(const u8 *pos)
|
|
{
|
|
return pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
|
|
pos[2] == 0x00 && pos[3] == 0x50 &&
|
|
pos[4] == 0xf2 && pos[5] == 0x01;
|
|
}
|
|
|
|
static bool nvt_is_rsn_ie(const u8 *pos)
|
|
{
|
|
return pos[0] == WLAN_EID_RSN;
|
|
}
|
|
|
|
/* 0x44 is the WAPI Element ID */
|
|
static bool nvt_is_wapi_ie(const u8 *pos)
|
|
{
|
|
return pos[0] == 0x44;
|
|
}
|
|
|
|
/* when set_extra_ie, we want to filter out sec IE */
|
|
static bool is_sec_ie(const u8 *pos)
|
|
{
|
|
if (nvt_is_wpa_ie(pos) || nvt_is_rsn_ie(pos) ||
|
|
nvt_is_wapi_ie(pos)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool nvt_is_wps_ie(const u8 *pos)
|
|
{
|
|
return (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
|
|
pos[1] >= 4 &&
|
|
pos[2] == 0x00 && pos[3] == 0x50 && pos[4] == 0xf2 &&
|
|
pos[5] == 0x04);
|
|
}
|
|
|
|
static bool nvt_is_an_alpha2(const char *alpha2)
|
|
{
|
|
if (!alpha2)
|
|
return false;
|
|
return isalpha(alpha2[0]) && isalpha(alpha2[1]);
|
|
}
|
|
|
|
/*get_num_of_conn_sta : return how many sta are connected to the station
|
|
*
|
|
*@buff => buff is the buffer get from WID_CONNECTED_STA_LIST
|
|
* */
|
|
static s32 get_num_of_conn_sta(u8 *buff, u32 size)
|
|
{
|
|
s32 i = 0;
|
|
s32 ret_val = 0;
|
|
while (i < size) {
|
|
ret_val++;
|
|
i += 16;
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
static u8 nvt_find_bss(struct _nvt_if *nvt_if)
|
|
{
|
|
u8 zeromac[ETH_ALEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
|
u8 match_bss = 0;
|
|
struct _nvt_cfg80211 *cfg = nvt_if->nvt_adapter->nvt_cfg80211;
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_bss_info *bss;
|
|
|
|
/* check matching bss */
|
|
list_for_each_entry(bss, &cfg->bss_list, list) {
|
|
/* compare ssid */
|
|
if ((memcmp(bss->ssid, wconf->ssid, wconf->ssid_len)) ||
|
|
(bss->ssid_len != wconf->ssid_len)) {
|
|
continue;
|
|
}
|
|
|
|
/* compare channel */
|
|
if (wconf->pref_channel) {
|
|
if (bss->channel != wconf->pref_channel) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
/* compare bssid */
|
|
if (memcmp(wconf->bssid, zeromac, ETH_ALEN)) {
|
|
if (memcmp(wconf->bssid, bss->bssid, ETH_ALEN)) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* compare security */
|
|
if (wconf->nvt_sec.mode_11i & WID_11I_SECURITY_ON) {
|
|
if (((wconf->nvt_sec.mode_11i & WID_11I_WEP40) &&
|
|
(bss->cap_info & 0x10)) ||
|
|
((wconf->nvt_sec.mode_11i & WID_11I_WEP104) &&
|
|
(bss->cap_info & 0x10)) ||
|
|
((wconf->nvt_sec.mode_11i & WID_11I_TKIP) &&
|
|
(bss->dot11i_info & WID_11I_TKIP)) ||
|
|
((wconf->nvt_sec.mode_11i & WID_11I_CCMP) &&
|
|
(bss->dot11i_info & WID_11I_CCMP))) {
|
|
match_bss = 1;
|
|
break;
|
|
} else if (wconf->nvt_sec.ext_mode_11i &
|
|
WID_EXT_11I_WAPI) {
|
|
match_bss = 1;
|
|
break;
|
|
}
|
|
} else {
|
|
if (wconf->ctrl_flag & WPS_FLAG) {
|
|
match_bss = 1;
|
|
break;
|
|
} else {
|
|
if (!(bss->dot11i_info & WID_11I_SECURITY_ON)) {
|
|
match_bss = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (match_bss) {
|
|
wconf->conn_bss = bss;
|
|
}
|
|
|
|
return match_bss;
|
|
}
|
|
|
|
static void nvt_addkey_wait(struct net_device *ndev)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
int ret = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
ret = wait_event_timeout(nvt_if->addkey_wait,
|
|
!atomic_read(&nvt_if->eapol_cnt),
|
|
msecs_to_jiffies(ADDKEY_WAIT_EAPOL_TIME));
|
|
|
|
if (ret == 0) {
|
|
nvt_dbg(CFG80211, "addkey wait timeout\n");
|
|
}
|
|
}
|
|
|
|
static void nvt_disconnect_wait(struct net_device *ndev)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
int ret = 0;
|
|
|
|
ret = wait_event_timeout(nvt_if->disconnect_wait,
|
|
atomic_read(&nvt_if->disconnect_cnt),
|
|
msecs_to_jiffies(DISCONNECT_WAIT__TIME));
|
|
|
|
if (ret == 0) {
|
|
nvt_dbg(CFG80211, "disconnect timeout\n");
|
|
|
|
clear_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
if (nvt_if->filter_disconnect == false) {
|
|
nvt_scan_init(nvt_adapter->nvt_cfg80211);
|
|
cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL);
|
|
}
|
|
/* Reset BA handlers */
|
|
nvt_reset_ba_handle(nvt_adapter, 1);
|
|
|
|
}
|
|
}
|
|
|
|
static s32 nvt_update_bss(struct _nvt_cfg80211 *cfg,
|
|
struct _nvt_bss_info *binfo)
|
|
{
|
|
struct wiphy *wiphy = cfg->wiphy;
|
|
struct ieee80211_channel *chan;
|
|
struct ieee80211_supported_band *band;
|
|
struct cfg80211_bss *bss = NULL;
|
|
s32 notify_signal = 0;
|
|
|
|
if (binfo->ie_length > IE_MAX_LEN) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (binfo->channel <= 14) {
|
|
band = wiphy->bands[IEEE80211_BAND_2GHZ];
|
|
chan = ieee80211_get_channel(wiphy,
|
|
ieee80211_channel_to_frequency(binfo->channel,
|
|
band->band));
|
|
notify_signal = (s32)binfo->rssi;
|
|
notify_signal = (s16)(0-notify_signal) * 100;
|
|
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 0)
|
|
bss = cfg80211_inform_bss(wiphy, chan,
|
|
CFG80211_BSS_FTYPE_UNKNOWN, binfo->bssid, 0,
|
|
binfo->cap_info, binfo->beacon_period,
|
|
(u8 *)binfo+binfo->ie_offset, binfo->ie_length,
|
|
notify_signal, GFP_KERNEL);
|
|
#else
|
|
bss = cfg80211_inform_bss(wiphy, chan, binfo->bssid, 0,
|
|
binfo->cap_info, binfo->beacon_period,
|
|
(u8 *)binfo+binfo->ie_offset, binfo->ie_length,
|
|
notify_signal, GFP_KERNEL);
|
|
#endif
|
|
if (bss == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
|
|
cfg80211_put_bss(wiphy, bss);
|
|
#else
|
|
cfg80211_put_bss(bss);
|
|
#endif
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nvt_bss_connect(struct _nvt_if *nvt_if, u16 status, u8 *data)
|
|
{
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_cfg80211 *cfg = nvt_adapter->nvt_cfg80211;
|
|
s32 assoc_req_len = 0;
|
|
s32 assoc_rsp_len = 0;
|
|
|
|
if (test_bit(NVT_IF_CONNECTING, &nvt_if->state_flags)) {
|
|
clear_bit(NVT_IF_CONNECTING, &nvt_if->state_flags);
|
|
}
|
|
|
|
if (status == WLAN_STATUS_SUCCESS) {
|
|
assoc_req_len = data[12] | (data[13] << 8);
|
|
assoc_rsp_len = data[14] | (data[15] << 8);
|
|
|
|
if (wconf->conn_bss != NULL) {
|
|
if (nvt_update_bss(cfg, wconf->conn_bss) < 0) {
|
|
nvt_dbg(CFG80211, "update bss fail\n");
|
|
} else {
|
|
nvt_dbg(CFG80211, "conn bss empty\n");
|
|
|
|
wconf->conn_bss = NULL;
|
|
set_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
|
|
cfg80211_connect_result(nvt_if->ndev, &data[4],
|
|
&data[16], assoc_req_len, &data[528],
|
|
assoc_rsp_len, WLAN_STATUS_SUCCESS,
|
|
GFP_KERNEL);
|
|
}
|
|
}
|
|
} else {
|
|
cfg80211_connect_result(nvt_if->ndev, NULL, NULL, 0,
|
|
NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL);
|
|
}
|
|
}
|
|
|
|
static s32 nvt_disconnect_action(struct _nvt_if *nvt_if)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
u8 val = 0;
|
|
|
|
mutex_lock(&nvt_if->disconnect_lock);
|
|
atomic_set(&nvt_if->eapol_cnt, 0);
|
|
atomic_set(&nvt_if->disconnect_cnt, 0);
|
|
|
|
if (nvt_icfg_reset(nvt_adapter->nvt_bus) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET, WID_DISCONNECT,
|
|
(u8 *)&val, sizeof(val)) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
nvt_disconnect_wait(nvt_if->wdev.netdev);
|
|
mutex_unlock(&nvt_if->disconnect_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void nvt_scan_complete(struct _nvt_adapter *nvt_adapter,
|
|
bool drv_abort_scan, u8 fw_stop_scan)
|
|
{
|
|
struct _nvt_bus *nvt_bus = nvt_adapter->nvt_bus;
|
|
struct _nvt_cfg80211 *cfg = nvt_adapter->nvt_cfg80211;
|
|
struct cfg80211_scan_request *scan_request = NULL;
|
|
struct _nvt_if *nvt_if = cfg->scan_if;
|
|
|
|
mutex_lock(&cfg->scan_lock);
|
|
scan_request = cfg->scan_request;
|
|
cfg->scan_request = NULL;
|
|
|
|
if (timer_pending(&cfg->scan_timer)) {
|
|
del_timer_sync(&cfg->scan_timer);
|
|
}
|
|
|
|
if (fw_stop_scan) {
|
|
if (nvt_icfg_lock(nvt_bus) < 0) {
|
|
goto next_check;
|
|
}
|
|
|
|
if (nvt_icfg_reset(nvt_bus) < 0) {
|
|
goto next_check;
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_SCAN_ABORT,
|
|
(u8 *)&fw_stop_scan, sizeof(fw_stop_scan)) < 0) {
|
|
goto next_check;
|
|
}
|
|
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
goto next_check;
|
|
}
|
|
|
|
msleep(DEFAULT_P2P_ACTIVE_TIME);
|
|
|
|
next_check:
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
if (scan_request) {
|
|
cfg80211_scan_done(scan_request, drv_abort_scan);
|
|
}
|
|
|
|
if (test_and_clear_bit(SCAN_PROCESS, &cfg->scan_sts)) {
|
|
/* do nothing */
|
|
}
|
|
|
|
if (test_bit(NVT_IF_CONNECTING, &nvt_if->state_flags) &&
|
|
(drv_abort_scan == 1)) {
|
|
nvt_bss_connect(nvt_if, WLAN_STATUS_UNSPECIFIED_FAILURE, NULL);
|
|
}
|
|
|
|
mutex_unlock(&cfg->scan_lock);
|
|
}
|
|
|
|
static s32 nvt_cfg80211_add_key(struct wiphy *wiphy,
|
|
struct net_device *ndev, u8 key_idx, bool pairwise, const u8 *mac_addr,
|
|
struct key_params *params)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_priv *nvt_priv = &(nvt_adapter->nvt_priv);
|
|
struct _nvt_sec *sec;
|
|
u8 sta_index;
|
|
s32 ret = 0;
|
|
u8 tmpbuf[WLAN_MAX_KEY_LEN];
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
sec = &nvt_if->nvt_wconf.nvt_sec;
|
|
if ((params->cipher == WLAN_CIPHER_SUITE_TKIP) &&
|
|
(nvt_if->mode == NVT_FW_STA)) {
|
|
memset(tmpbuf, 0x0, WLAN_MAX_KEY_LEN);
|
|
memcpy(tmpbuf, params->key, params->key_len);
|
|
memcpy((void *)¶ms->key[24], &tmpbuf[16], 8);
|
|
memcpy((void *)¶ms->key[16], &tmpbuf[24], 8);
|
|
}
|
|
/*Store the broadcast PN*/
|
|
if (params->seq && params->seq_len &&
|
|
(params->cipher == WLAN_CIPHER_SUITE_TKIP ||
|
|
params->cipher == WLAN_CIPHER_SUITE_CCMP)) {
|
|
if (nvt_priv->pn_bcmc == NULL) {
|
|
nvt_priv->pn_bcmc = kzalloc(params->seq_len,
|
|
GFP_KERNEL);
|
|
}
|
|
if (nvt_priv->pn_bcmc != NULL) {
|
|
memcpy(nvt_priv->pn_bcmc, params->seq, params->seq_len);
|
|
} else {
|
|
nvt_dbg(ERROR, "broadcast pn alloc fail\n");
|
|
}
|
|
|
|
}
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
switch (params->cipher) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
memset(&sec->wep, 0, sizeof(struct _nvt_wep));
|
|
sec->wep.key_id = key_idx;
|
|
sec->wep.key_len = params->key_len;
|
|
memcpy(sec->wep.key_data, params->key, params->key_len);
|
|
if (params->cipher == WLAN_CIPHER_SUITE_WEP40) {
|
|
sec->wep_type |= (0 << key_idx);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_WEP_KEY, (u8 *)&sec->wep,
|
|
sizeof(struct _nvt_wep)-8);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
sec->wep_type |= (1 << key_idx);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_WEP_KEY, (u8 *)&sec->wep,
|
|
sizeof(struct _nvt_wep));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
sec->wep_type |= 0x80;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
for (sta_index = 1; sta_index < 9; sta_index++) {
|
|
nvt_reset_unicast_pn(nvt_adapter, sta_index);
|
|
}
|
|
if (mac_addr) {
|
|
memset(&sec->ptk, 0, sizeof(struct _nvt_ptk));
|
|
memcpy(sec->ptk.addr, mac_addr, ETH_ALEN);
|
|
sec->ptk.key_len = params->key_len;
|
|
memcpy(sec->ptk.key_tk, params->key, 16);
|
|
memcpy(sec->ptk.key_rx_mic, ¶ms->key[16], 8);
|
|
memcpy(sec->ptk.key_tx_mic, ¶ms->key[24], 8);
|
|
if (nvt_if->mode == NVT_FW_STA) {
|
|
nvt_addkey_wait(ndev);
|
|
}
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_PTK, (u8 *)&sec->ptk,
|
|
sizeof(struct _nvt_ptk));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
if (nvt_if->mode == NVT_FW_STA) {
|
|
memset(&sec->gtk, 0, sizeof(struct _nvt_gtk));
|
|
memcpy(sec->gtk.addr, nvt_if->nvt_wconf.bssid,
|
|
ETH_ALEN);
|
|
if (params->seq && params->seq_len == 6) {
|
|
memcpy(sec->gtk.key_rsc, params->seq,
|
|
params->seq_len);
|
|
}
|
|
sec->gtk.key_id = key_idx;
|
|
sec->gtk.key_len = params->key_len;
|
|
memcpy(sec->gtk.key_tk, params->key, 16);
|
|
memcpy(sec->gtk.key_mic, ¶ms->key[16], 8);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_RX_GTK,
|
|
(u8 *)&sec->gtk,
|
|
sizeof(struct _nvt_gtk) - 8);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else if (nvt_if->mode == NVT_FW_AP) {
|
|
memset(&sec->gtk, 0, sizeof(struct _nvt_gtk));
|
|
sec->gtk.key_id = key_idx;
|
|
sec->gtk.key_len = params->key_len;
|
|
memcpy(sec->gtk.key_tk, params->key, 16);
|
|
memcpy(sec->gtk.key_mic, ¶ms->key[16], 16);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_TX_GTK,
|
|
(u8 *)&sec->gtk,
|
|
sizeof(struct _nvt_gtk));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
ret = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
}
|
|
break;
|
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
memset(&sec->igtk, 0, sizeof(struct _nvt_gtk));
|
|
memcpy(sec->igtk.addr, nvt_if->nvt_wconf.bssid,
|
|
ETH_ALEN);
|
|
if (params->seq && params->seq_len == 6) {
|
|
memcpy(sec->igtk.key_rsc, params->seq,
|
|
params->seq_len);
|
|
}
|
|
sec->igtk.key_id = key_idx;
|
|
sec->igtk.key_len = params->key_len;
|
|
memcpy(sec->igtk.key_tk, params->key, 16);
|
|
|
|
if (nvt_if->mode == NVT_FW_STA) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_RX_IGTK,
|
|
(u8 *)&sec->igtk,
|
|
sizeof(struct _nvt_gtk)-8);
|
|
} else {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_TX_IGTK,
|
|
(u8 *)&sec->igtk,
|
|
sizeof(struct _nvt_gtk));
|
|
}
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
nvt_dbg(CFG80211, "%s WLAN_CIPHER_SUITE_SMS4\n", __func__);
|
|
if (params->key_len != 32) {
|
|
ret = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
nvt_dbg(CFG80211, "key_len=%d, seq_len=%d, pairwise=%d\n",
|
|
params->key_len, params->seq_len, pairwise);
|
|
if (pairwise) {
|
|
memset(&sec->wapi_ptk, 0, sizeof(struct _nvt_wapi_ptk));
|
|
memcpy(sec->wapi_ptk.addr, mac_addr, ETH_ALEN);
|
|
sec->wapi_ptk.key_len = params->key_len;
|
|
sec->wapi_ptk.key_id = key_idx;
|
|
memcpy(sec->wapi_ptk.key_tk, params->key, 16);
|
|
memcpy(sec->wapi_ptk.key_mic, ¶ms->key[16], 16);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_WAPI_PTK,
|
|
(u8 *)&sec->wapi_ptk,
|
|
sizeof(struct _nvt_wapi_ptk));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
memset(&sec->wapi_gtk, 0, sizeof(struct _nvt_wapi_gtk));
|
|
memcpy(sec->wapi_gtk.addr,
|
|
nvt_if->nvt_wconf.bssid, ETH_ALEN);
|
|
if (params->seq && params->seq_len == 16) {
|
|
memcpy(sec->wapi_gtk.key_rsc, params->seq,
|
|
params->seq_len);
|
|
}
|
|
sec->wapi_gtk.key_id = key_idx;
|
|
sec->wapi_gtk.key_len = params->key_len;
|
|
memcpy(sec->wapi_gtk.key_tk, params->key, 16);
|
|
memcpy(sec->wapi_gtk.key_mic, ¶ms->key[16], 16);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_ADD_WAPI_GTK,
|
|
(u8 *)&sec->wapi_gtk,
|
|
sizeof(struct _nvt_wapi_gtk));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_del_key(struct wiphy *wiphy,
|
|
struct net_device *ndev, u8 key_idx, bool pairwise, const u8 *mac_addr)
|
|
{
|
|
/* key_id(4)+key_len(4) */
|
|
#define RM_OFFSET 8
|
|
u8 tmp_addr[ETH_ALEN];
|
|
u8 rm_addr[RM_OFFSET+ETH_ALEN];
|
|
s32 ret = 0;
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
if (key_idx >= MAX_KEY_INDEX) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
memset(tmp_addr, 0x0, sizeof(tmp_addr));
|
|
memset(rm_addr, 0x0, sizeof(rm_addr));
|
|
|
|
if (mac_addr) {
|
|
memcpy(&rm_addr[RM_OFFSET], mac_addr, ETH_ALEN);
|
|
} else {
|
|
if (nvt_if->mode == NVT_FW_STA) {
|
|
memcpy(&rm_addr[RM_OFFSET], nvt_if->nvt_wconf.bssid,
|
|
ETH_ALEN);
|
|
}
|
|
}
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_REMOVE_WEP_KEY, (u8 *)&tmp_addr, sizeof(tmp_addr));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_REMOVE_KEY, (u8 *)&rm_addr, sizeof(rm_addr));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_set_default_key(struct wiphy *wiphy,
|
|
struct net_device *ndev, u8 key_idx,
|
|
bool unicast, bool multicast)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_sec *sec;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
sec = &nvt_if->nvt_wconf.nvt_sec;
|
|
if ((nvt_if->mode == NVT_FW_AP) && (sec->wep_type & 0x80)) {
|
|
sec->wep_type |= (key_idx << 4);
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_KEY_ID, (u8 *)&key_idx, sizeof(key_idx));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus,
|
|
NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
}
|
|
return ret;
|
|
|
|
}
|
|
|
|
static u32 is_rts_correct(u32 rts_val)
|
|
{
|
|
if (rts_val > RTS_THRESHOLD_LIMIT &&
|
|
rts_val < 0xFFFFFFFF) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_set_wiphy_params(struct wiphy *wiphy,
|
|
u32 changed)
|
|
{
|
|
struct _nvt_cfg80211 *cfg = (struct _nvt_cfg80211 *)wiphy_priv(wiphy);
|
|
struct _nvt_adapter *nvt_adapter = cfg->nvt_adapter;
|
|
struct _nvt_if *nvt_if = nvt_vif_first(nvt_adapter);
|
|
s8 rts_buf[2];
|
|
s8 frag_buf[2];
|
|
s8 retry_long_buf[2];
|
|
s8 retry_short_buf[2];
|
|
u8 is_wiphy_param_set;
|
|
|
|
u32 wid = 0;
|
|
s32 ret = 0;
|
|
is_wiphy_param_set = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
if (!nvt_if) {
|
|
nvt_dbg(ERROR, "nvt_if == NULL\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
|
|
if (is_rts_correct(wiphy->rts_threshold)) {
|
|
rts_buf[0] = wiphy->rts_threshold & 0xff;
|
|
rts_buf[1] = (wiphy->rts_threshold >> 8) & 0xff;
|
|
nvt_if->rts_val = wiphy->rts_threshold;
|
|
wid = WID_RTS_THRESHOLD;
|
|
is_wiphy_param_set |= (1 << 0);
|
|
} else {
|
|
wiphy->rts_threshold = nvt_if->rts_val;
|
|
nvt_dbg(CFG80211, "rts is not set\n");
|
|
}
|
|
}
|
|
|
|
if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
|
|
if (wiphy->frag_threshold > FRAG_LOW_LIMIT &&
|
|
wiphy->frag_threshold <= FRAG_UP_LIMIT) {
|
|
frag_buf[0] = wiphy->frag_threshold & 0xff;
|
|
frag_buf[1] = (wiphy->frag_threshold >> 8) & 0xff;
|
|
nvt_if->frag_val = wiphy->frag_threshold;
|
|
is_wiphy_param_set |= (1 << 1);
|
|
wid = WID_FRAG_THRESHOLD;
|
|
} else {
|
|
wiphy->frag_threshold = nvt_if->frag_val;
|
|
nvt_dbg(CFG80211, "fragment is not set\n");
|
|
}
|
|
}
|
|
|
|
if (changed & WIPHY_PARAM_RETRY_LONG) {
|
|
if (wiphy->retry_long != 0 &&
|
|
wiphy->retry_long <= RETRY_LIMIT) {
|
|
retry_long_buf[0] = wiphy->retry_long & 0xff;
|
|
//retry_long_buf[1] = (wiphy->retry_long >> 8) & 0xff;
|
|
retry_long_buf[1] = 0;
|
|
nvt_if->retry_long_val = wiphy->retry_long;
|
|
is_wiphy_param_set |= (1 << 2);
|
|
wid = WID_LONG_RETRY_LIMIT;
|
|
|
|
} else {
|
|
wiphy->retry_long = nvt_if->retry_long_val;
|
|
nvt_dbg(CFG80211, "long retry is not set\n");
|
|
}
|
|
}
|
|
|
|
if (changed & WIPHY_PARAM_RETRY_SHORT) {
|
|
if (wiphy->retry_short != 0 &&
|
|
wiphy->retry_short <= RETRY_LIMIT) {
|
|
retry_short_buf[0] = wiphy->retry_short & 0xff;
|
|
//retry_short_buf[1] = (wiphy->retry_short >> 8) & 0xff;
|
|
retry_short_buf[1] = 0;
|
|
nvt_if->retry_short_val = wiphy->retry_short;
|
|
wid = WID_SHORT_RETRY_LIMIT;
|
|
is_wiphy_param_set |= (1 << 3);
|
|
} else {
|
|
wiphy->retry_short = nvt_if->retry_short_val;
|
|
nvt_dbg(CFG80211, "short retry is not set\n");
|
|
}
|
|
}
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
//check if rts is set
|
|
if (is_wiphy_param_set & (1 << 0)) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_RTS_THRESHOLD, (u8 *)rts_buf, sizeof(rts_buf));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
//check if frag is set
|
|
if (is_wiphy_param_set & (1 << 1)) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_FRAG_THRESHOLD, (u8 *)frag_buf, sizeof(frag_buf));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
//check if long retry flag is set
|
|
if (is_wiphy_param_set & (1 << 2)) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_LONG_RETRY_LIMIT,
|
|
(u8 *)retry_long_buf, sizeof(retry_long_buf));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
//check if short retry flag is set
|
|
if (is_wiphy_param_set & (1 << 3)) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SHORT_RETRY_LIMIT,
|
|
(u8 *)retry_short_buf, sizeof(retry_short_buf));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_set_power_mgmt(struct wiphy *wiphy,
|
|
struct net_device *ndev, bool enabled,
|
|
s32 timeout)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_adapter *nvt_adapter;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
nvt_adapter = nvt_if->nvt_adapter;
|
|
nvt_if->nvt_wconf.pwr_save = enabled;
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_POWER_MANAGEMENT, (u8 *)&nvt_if->nvt_wconf.pwr_save,
|
|
sizeof(nvt_if->nvt_wconf.pwr_save));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
static void nvt_scan_init(struct _nvt_cfg80211 *cfg)
|
|
{
|
|
ulong flags = 0;
|
|
struct _nvt_bss_info *binfo;
|
|
struct _nvt_bss_info *tmp_binfo;
|
|
|
|
if (timer_pending(&cfg->scan_timer)) {
|
|
del_timer_sync(&cfg->scan_timer);
|
|
}
|
|
|
|
spin_lock_irqsave(&cfg->bss_list_lock, flags);
|
|
list_for_each_entry_safe(binfo, tmp_binfo, &cfg->bss_list, list) {
|
|
list_del(&binfo->list);
|
|
kfree(binfo);
|
|
cfg->bss_count--;
|
|
}
|
|
spin_unlock_irqrestore(&cfg->bss_list_lock, flags);
|
|
}
|
|
|
|
static s32 nvt_connect_action(struct _nvt_if *nvt_if)
|
|
{
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_sec *sec = &wconf->nvt_sec;
|
|
struct _nvt_bss_info *conn_bss = wconf->conn_bss;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
s32 bss_len = 0;
|
|
s32 ret = 0;
|
|
u8 wps_enable = 0;
|
|
u8 ba_enable = 1;
|
|
u8 start_op = 0;
|
|
|
|
if (conn_bss == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
bss_len = conn_bss->length - sizeof(struct _nvt_bss_info) -
|
|
conn_bss->ie_length;
|
|
if (bss_len <= 0) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
wps_enable = (nvt_if->nvt_wconf.ctrl_flag & WPS_FLAG) ? 1 : 0;
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
if (sec->ciphers_pairwise == WLAN_CIPHER_SUITE_WEP40) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_ADD_WEP_KEY, (u8 *)&wconf->nvt_sec.wep,
|
|
sizeof(struct _nvt_wep)-8);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else if (sec->ciphers_pairwise == WLAN_CIPHER_SUITE_WEP104) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_ADD_WEP_KEY, (u8 *)&wconf->nvt_sec.wep,
|
|
sizeof(struct _nvt_wep));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_WPS_REGISTRATION_START, (u8 *)&wps_enable,
|
|
sizeof(wps_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_11I_MODE, (u8 *)&wconf->nvt_sec.mode_11i,
|
|
sizeof(u8));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_EXT_MODE_11I, (u8 *)&wconf->nvt_sec.ext_mode_11i,
|
|
sizeof(u8));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_AUTH_TYPE, (u8 *)&wconf->nvt_sec.auth_type,
|
|
sizeof(u8));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_PMF_ENABLE, (u8 *)&wconf->nvt_sec.pmf_enable,
|
|
sizeof(u8));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_11N_IMMEDIATE_BA_ENABLED, (u8 *)&ba_enable,
|
|
sizeof(ba_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_JOIN_REQ_BSSTBL, (u8 *)conn_bss +
|
|
sizeof(struct _nvt_bss_info), bss_len);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
start_op = DO_RESTART;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_START_OP, (u8 *)&start_op, sizeof(start_op));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_handle_reg_channel(struct _nvt_adapter *nvt_adapter,
|
|
struct wiphy *wiphy, struct ieee80211_supported_band *sband)
|
|
{
|
|
struct ieee80211_channel *ch;
|
|
unsigned int i;
|
|
u8 *ch_param = NULL;
|
|
u8 ch_index = 0;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
if (!sband)
|
|
return 0;
|
|
|
|
ch_param = kzalloc(sband->n_channels * 9, GFP_KERNEL);
|
|
if (ch_param == NULL) {
|
|
nvt_dbg(CFG80211, "Fail memory allocate!\n");
|
|
ret = -ENOMEM;
|
|
goto err_ret;
|
|
}
|
|
|
|
for (i = 0; i < sband->n_channels; i++) {
|
|
ch = &sband->channels[i];
|
|
ch_param[ch_index++] = ch->band;
|
|
ch_param[ch_index++] = ch->hw_value;
|
|
ch_param[ch_index++] = ch->max_power;
|
|
ch_param[ch_index++] =
|
|
(ch->flags & IEEE80211_CHAN_DISABLED);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
ch_param[ch_index++] =
|
|
(ch->flags & IEEE80211_CHAN_NO_IR) >> 1;
|
|
#else
|
|
ch_param[ch_index++] =
|
|
(ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) >> 1;
|
|
#endif
|
|
ch_param[ch_index++] =
|
|
(ch->flags & IEEE80211_CHAN_RADAR) >> 3;
|
|
ch_param[ch_index++] =
|
|
(ch->flags & IEEE80211_CHAN_NO_HT40PLUS) >> 4;
|
|
ch_param[ch_index++] =
|
|
(ch->flags & IEEE80211_CHAN_NO_HT40MINUS) >> 5;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
ch_param[ch_index++] =
|
|
(ch->flags & IEEE80211_CHAN_NO_OFDM) >> 6;
|
|
#endif
|
|
}
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SET_REGFLAGS, (u8 *)ch_param, sband->n_channels * 9);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
err_ret:
|
|
kfree(ch_param);
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_scan_action(struct wiphy *wiphy,
|
|
struct _nvt_adapter *nvt_adapter, u32 n_channels,
|
|
struct ieee80211_channel *channels[], u8 *ssid_list_ptr,
|
|
u8 list_len, u8 scan_type, u16 scan_time,
|
|
u8 scan_filter, u8 no_cck)
|
|
{
|
|
struct _nvt_cfg80211 *cfg = nvt_adapter->nvt_cfg80211;
|
|
//use for ap scanning
|
|
struct _nvt_if *nvt_if = nvt_vif_first(nvt_adapter);
|
|
enum ieee80211_band band;
|
|
u8 *ch_param = NULL;
|
|
u8 ch_param_free = 0;
|
|
u8 ch_index = 0;
|
|
u8 scan_ch_num = 0xff;
|
|
u8 i = 0;
|
|
u8 site_survey = 1;
|
|
u8 user_scan = 1;
|
|
//u8 start_op = 0;
|
|
u32 scan_timeout = 0;
|
|
s32 ret = 0;
|
|
|
|
if (!nvt_if) {
|
|
nvt_dbg(ERROR, "nvt_if == NULL\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
|
|
if ((n_channels > 0) && (n_channels < 128)) {
|
|
ch_param = kzalloc(n_channels * 2 + 1, GFP_KERNEL);
|
|
if (ch_param == NULL) {
|
|
ch_param = &scan_ch_num;
|
|
} else {
|
|
ch_param_free = 1;
|
|
ch_param[ch_index++] = n_channels;
|
|
for (i = 0; i < n_channels; i++) {
|
|
ch_param[ch_index++] = channels[i]->band;
|
|
ch_param[ch_index++] = channels[i]->hw_value;
|
|
}
|
|
}
|
|
} else {
|
|
ch_param = &scan_ch_num;
|
|
}
|
|
|
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++)
|
|
ret = nvt_handle_reg_channel(nvt_adapter,
|
|
wiphy, wiphy->bands[band]);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
if (nvt_if->mode != NVT_FW_AP) {
|
|
if (ch_param[0] == 0xFF) {
|
|
scan_timeout = nvt_band_2ghz.n_channels*scan_time+5000;
|
|
} else {
|
|
scan_timeout = n_channels*scan_time+5000;
|
|
}
|
|
} else {
|
|
if (ch_param[0] == 0xFF) {
|
|
scan_timeout = nvt_band_2ghz.n_channels*scan_time
|
|
+ 20000;
|
|
} else {
|
|
scan_timeout = n_channels*scan_time + 20000;
|
|
}
|
|
}
|
|
|
|
mod_timer(&cfg->scan_timer, jiffies + scan_timeout * HZ/1000);
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set scan_type */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_TYPE, (u8 *)&scan_type, sizeof(scan_type));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set scan filter */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_FILTER, (u8 *)&scan_filter, sizeof(scan_filter));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set scan time */
|
|
if (scan_type == ACTIVE_SCAN) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SITE_SURVEY_SCAN_TIME,
|
|
(u8 *)&scan_time, sizeof(scan_time));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_PASSIVE_SCAN_TIME, (u8 *)&scan_time,
|
|
sizeof(scan_time));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
/* set scan channel list */
|
|
if (ch_param[0] == 0xFF) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_MULTICH, (u8 *)ch_param, sizeof(u8));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_MULTICH, (u8 *)ch_param, n_channels*2+1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
/* set enable site survey */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SITE_SURVEY, (u8 *)&site_survey, sizeof(u8));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set ssid list */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_MULTISSID, (u8 *)ssid_list_ptr, list_len);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set user scan */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_START_SCAN_REQ, (u8 *)&user_scan, sizeof(user_scan));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set op mode */
|
|
if (no_cck == 0) {
|
|
no_cck = 2;
|
|
} else {
|
|
no_cck = 1;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_11G_OPERATING_MODE, (u8 *)&no_cck, sizeof(no_cck));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set reset mode */
|
|
//start_op = DO_RESTART;
|
|
//ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
// WID_START_OP, (u8 *)&start_op, sizeof(start_op));
|
|
//if (ret < 0) {
|
|
// goto err_ret;
|
|
//}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
err_ret:
|
|
if (ch_param_free == 1) {
|
|
kfree(ch_param);
|
|
}
|
|
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
static s32 nvt_cfg80211_scan(struct wiphy *wiphy,
|
|
struct cfg80211_scan_request *request)
|
|
{
|
|
struct _nvt_if *nvt_if =
|
|
container_of(request->wdev, struct _nvt_if, wdev);
|
|
#else
|
|
static s32 nvt_cfg80211_scan(struct wiphy *wiphy,
|
|
struct net_device *ndev,
|
|
struct cfg80211_scan_request *request)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
#endif
|
|
struct _nvt_cfg80211 *cfg = wiphy_priv(wiphy);
|
|
struct _nvt_adapter *nvt_adapter = cfg->nvt_adapter;
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_probe_ssid ssid_list[MAX_PROBED_SSIDS];
|
|
s32 ret = 0;
|
|
u8 scan_filter = 0;
|
|
u8 scan_type = ACTIVE_SCAN;
|
|
u8 list_len = 0;
|
|
u8 i = 0;
|
|
u8 p2p_enable = 0;
|
|
u8 vsie_enable = 0;
|
|
u16 scan_time = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
if (!check_if_ready(nvt_if)) {
|
|
return -EIO;
|
|
}
|
|
|
|
/* check scan status */
|
|
if (test_bit(SCAN_PROCESS, &cfg->scan_sts)) {
|
|
nvt_dbg(CFG80211, "scan is processing\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* check scan ssid num */
|
|
if (request->n_ssids > MAX_PROBED_SSIDS) {
|
|
nvt_dbg(CFG80211, "request ssid [%d] over max ssids\n",
|
|
request->n_ssids);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_if->start_ap_flag == 0 && nvt_if->mode == NVT_FW_AP) {
|
|
nvt_dbg(CFG80211, "scan abort before start_ap ops\n");
|
|
cfg80211_scan_done(request, 1);
|
|
return 0;
|
|
}
|
|
|
|
set_bit(SCAN_PROCESS, &cfg->scan_sts);
|
|
|
|
/* clean ctrl flag */
|
|
wconf->ctrl_flag &= ~(PROBE_REQ_EXTRA_IE_FLAG|WPS_FLAG|
|
|
P2P_WILDCARD_SSID_FLAG|P2P_FLAG);
|
|
|
|
nvt_scan_init(cfg);
|
|
|
|
cfg->scan_request = request;
|
|
cfg->scan_if = nvt_if;
|
|
|
|
scan_filter = SCAN_NO_ADHOC | SCAN_SAVE_WITHOUTRSSI | SCAN_CURRENTCH;
|
|
|
|
memset(ssid_list, 0x0, sizeof(struct _nvt_probe_ssid)*MAX_PROBED_SSIDS);
|
|
|
|
|
|
if (request->n_ssids > 0) {
|
|
for (i = 0; i < request->n_ssids; i++) {
|
|
ssid_list[i].index = i;
|
|
ssid_list[i].ssid_len = request->ssids[i].ssid_len;
|
|
|
|
if (ssid_list[i].ssid_len) {
|
|
memcpy(ssid_list[i].ssid,
|
|
request->ssids[i].ssid,
|
|
request->ssids[i].ssid_len);
|
|
}
|
|
list_len += sizeof(struct _nvt_probe_ssid);
|
|
}
|
|
scan_type = ACTIVE_SCAN;
|
|
if (wconf->ctrl_flag & P2P_WILDCARD_SSID_FLAG) {
|
|
scan_time = DEFAULT_P2P_ACTIVE_TIME;
|
|
} else {
|
|
if (nvt_if->mode != NVT_FW_AP)
|
|
scan_time = DEFAULT_ACTIVE_TIME;
|
|
else
|
|
scan_time = DEFAULT_AP_SCAN_TIME;
|
|
}
|
|
} else {
|
|
ssid_list[i].index = 0;
|
|
ssid_list[i].ssid_len = 0;
|
|
scan_type = PASSIVE_SCAN;
|
|
scan_time = DEFAULT_PASSIVE_TIME;
|
|
list_len += sizeof(struct _nvt_probe_ssid);
|
|
}
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
if ((request->ie != NULL) && (request->ie_len > 0)) {
|
|
if (cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
|
|
request->ie, request->ie_len)) {
|
|
p2p_enable = 1;
|
|
wconf->ctrl_flag |= P2P_FLAG;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_P2P_ENABLE, (u8 *)&p2p_enable,
|
|
sizeof(p2p_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
p2p_enable = 0;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_P2P_ENABLE, (u8 *)&p2p_enable,
|
|
sizeof(p2p_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
vsie_enable |= 0x02;
|
|
wconf->ctrl_flag |= PROBE_REQ_EXTRA_IE_FLAG;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_VSIE_TX_PROBEREQ, (u8 *)request->ie,
|
|
request->ie_len);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
/* if our mode is AP or GO, need to check more vsie flags */
|
|
if (nvt_if->mode == NVT_FW_AP) {
|
|
if (wconf->ctrl_flag & BEACON_EXTRA_IE_FLAG) {
|
|
vsie_enable |= 0x01;
|
|
}
|
|
if (wconf->ctrl_flag & PROBE_RESP_EXTRA_IE_FLAG) {
|
|
vsie_enable |= 0x04;
|
|
}
|
|
if (wconf->ctrl_flag & ASSOC_RESP_EXTRA_IE_FLAG) {
|
|
vsie_enable |= 0x08;
|
|
}
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_VSIE_FRAME, (u8 *)&vsie_enable, sizeof(vsie_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
|
|
ret = nvt_scan_action(wiphy, nvt_adapter, request->n_channels,
|
|
request->channels, (u8 *)ssid_list, list_len, scan_type,
|
|
scan_time, scan_filter, (u8)request->no_cck);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
return ret;
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
|
|
cfg->scan_request = NULL;
|
|
if (timer_pending(&cfg->scan_timer)) {
|
|
del_timer_sync(&cfg->scan_timer);
|
|
}
|
|
clear_bit(SCAN_PROCESS, &cfg->scan_sts);
|
|
return ret;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
static u8 nvt_pmf_check(u8 *ie, u8 *ielen)
|
|
{
|
|
#define GROUP_CIPHER_LEN 4
|
|
#define PAIRWISE_CIPHER_CNT_LEN 2
|
|
#define PAIRWISE_CIPHER_LEN 4
|
|
#define AKM_SUITE_CNT 2
|
|
#define AKM_SUITE_LEN 4
|
|
|
|
u8 AKM_PSK_SHA256[] = {0x00, 0x0f, 0xac, 0x06};
|
|
u8 ret = 0;
|
|
u16 len = (u16)*ielen;
|
|
u16 cnt = 0;
|
|
u16 i = 0;
|
|
u16 rsn_capable = 0;
|
|
|
|
if (ie[2] == 1) {
|
|
ie += 4;
|
|
len -= 2;
|
|
if (len >= GROUP_CIPHER_LEN) {
|
|
ie += GROUP_CIPHER_LEN;
|
|
len -= GROUP_CIPHER_LEN;
|
|
} else {
|
|
return ret;
|
|
}
|
|
|
|
if (len >= PAIRWISE_CIPHER_CNT_LEN) {
|
|
cnt = ((ie[1] << 8) | ie[0]);
|
|
ie += PAIRWISE_CIPHER_CNT_LEN;
|
|
len -= PAIRWISE_CIPHER_CNT_LEN;
|
|
if ((cnt == 0) ||
|
|
(len < (cnt * PAIRWISE_CIPHER_LEN))) {
|
|
return ret;
|
|
} else {
|
|
ie += PAIRWISE_CIPHER_LEN * cnt;
|
|
len -= PAIRWISE_CIPHER_LEN * cnt;
|
|
}
|
|
} else {
|
|
return ret;
|
|
}
|
|
|
|
if (len >= AKM_SUITE_CNT) {
|
|
cnt = ((ie[1] << 8) | ie[0]);
|
|
ie += AKM_SUITE_CNT;
|
|
len -= AKM_SUITE_CNT;
|
|
for (i = 0; i < cnt; i++) {
|
|
if (memcmp(ie, AKM_PSK_SHA256,
|
|
AKM_SUITE_LEN) == 0) {
|
|
ret |= PMF_SHA256;
|
|
}
|
|
ie += AKM_SUITE_LEN;
|
|
len -= AKM_SUITE_LEN;
|
|
}
|
|
} else {
|
|
return ret;
|
|
}
|
|
|
|
if (len >= 2) {
|
|
rsn_capable = ((ie[1] << 8) | ie[0]);
|
|
if (rsn_capable & 0x80) {
|
|
ret |= PMF_CAPABLE;
|
|
if (rsn_capable & 0x40) {
|
|
ret |= PMF_REQUIRED;
|
|
}
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static s32 nvt_set_extra_ies(struct _nvt_if *nvt_if,
|
|
struct cfg80211_connect_params *sme)
|
|
{
|
|
const u8 *ies = sme->ie;
|
|
size_t ies_len = sme->ie_len;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
u8 *buf = NULL;
|
|
const u8 *pos = NULL;
|
|
u8 vsie_enable = 0;
|
|
size_t len = 0;
|
|
s32 ret = 0;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
struct _nvt_sec *sec = &nvt_if->nvt_wconf.nvt_sec;
|
|
u8 *rsn_ie = NULL;
|
|
#endif
|
|
if (ies && ies_len) {
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
// ??
|
|
if (sme->mfp == NL80211_MFP_REQUIRED) {
|
|
rsn_ie = (u8 *)cfg80211_find_ie(WLAN_EID_RSN, ies,
|
|
ies_len);
|
|
if (rsn_ie != NULL) {
|
|
sec->pmf_enable = nvt_pmf_check(rsn_ie,
|
|
rsn_ie+1);
|
|
}
|
|
}
|
|
#else
|
|
#endif
|
|
|
|
buf = kzalloc(ies_len, GFP_KERNEL);
|
|
if (buf == NULL) {
|
|
return -ENOMEM;
|
|
}
|
|
pos = ies;
|
|
wconf->ctrl_flag &= ~WPS_FLAG;
|
|
|
|
while (pos + 1 < ies + ies_len) {
|
|
if (pos + 2 + pos[1] > ies + ies_len) {
|
|
break;
|
|
}
|
|
if (!is_sec_ie(pos)) {
|
|
memcpy(buf + len, pos, 2 + pos[1]);
|
|
len += 2 + pos[1];
|
|
}
|
|
|
|
if (nvt_is_wps_ie(pos)) {
|
|
wconf->ctrl_flag |= WPS_FLAG;
|
|
}
|
|
|
|
pos += 2 + pos[1];
|
|
}
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
if ((len != 0) &&
|
|
(wconf->ctrl_flag & PROBE_REQ_EXTRA_IE_FLAG)) {
|
|
vsie_enable |= 0x82;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_VSIE_TX_ASSOCREQ, (u8 *)buf,
|
|
len);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_VSIE_FRAME,
|
|
(u8 *)&vsie_enable, sizeof(vsie_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else if ((len == 0) &&
|
|
(wconf->ctrl_flag & PROBE_REQ_EXTRA_IE_FLAG)) {
|
|
vsie_enable |= 0x02;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_VSIE_FRAME,
|
|
(u8 *)&vsie_enable, sizeof(vsie_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else if ((len != 0) &&
|
|
!(wconf->ctrl_flag & PROBE_REQ_EXTRA_IE_FLAG)) {
|
|
vsie_enable |= 0x80;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_VSIE_TX_ASSOCREQ, (u8 *)buf,
|
|
len);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_VSIE_FRAME,
|
|
(u8 *)&vsie_enable, sizeof(vsie_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_VSIE_FRAME,
|
|
(u8 *)&vsie_enable, sizeof(vsie_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus,
|
|
NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
|
|
kfree(buf);
|
|
}
|
|
return ret;
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
|
|
kfree(buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void nvt_set_auth_by_ext_mode_11i(u8 ext_mode_11i, u8 mode_11i,
|
|
struct cfg80211_connect_params *sme,
|
|
struct _nvt_sec *sec)
|
|
{
|
|
/* reset the auth_type */
|
|
sec->auth_type = 0;
|
|
|
|
/* May check other condition for WAPI CERT in the future */
|
|
if (ext_mode_11i & WID_EXT_11I_WAPI) {
|
|
sec->auth_type |= WID_EXT_11I_WAPI;
|
|
}
|
|
|
|
/* Check for the original auth_type if mode_11i is being set*/
|
|
if (sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) {
|
|
sec->auth_type |= WID_AUTH_OPEN_PSK;
|
|
} else if (sme->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
|
|
sec->auth_type |= WID_AUTH_SHARED_KEY;
|
|
} else if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
|
|
sec->auth_type |= (WID_AUTH_OPEN_PSK | WID_AUTH_SHARED_KEY);
|
|
} else if (sme->auth_type == NL80211_AUTHTYPE_NETWORK_EAP) {
|
|
sec->auth_type |= WID_AUTH_802_1X;
|
|
} else {
|
|
sec->auth_type |= WID_AUTH_OPEN_PSK;
|
|
}
|
|
}
|
|
|
|
static s32 nvt_cfg80211_connect(struct wiphy *wiphy,
|
|
struct net_device *ndev, struct cfg80211_connect_params *sme)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_sec *sec = &nvt_if->nvt_wconf.nvt_sec;
|
|
struct _nvt_cfg80211 *cfg = nvt_if->nvt_adapter->nvt_cfg80211;
|
|
struct _nvt_probe_ssid ssid_list[MAX_PROBED_SSIDS];
|
|
struct ieee80211_channel *ch = sme->channel;
|
|
s32 ret = 0;
|
|
u8 scan_filter = 0;
|
|
u8 list_len = 0;
|
|
u8 p2p_ie = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
if (!check_if_ready(nvt_if)) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (sme->ssid == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
set_bit(NVT_IF_CONNECTING, &nvt_if->state_flags);
|
|
|
|
/* clean wlan config */
|
|
memset(&nvt_if->nvt_wconf, 0x0, sizeof(struct _nvt_wlan_conf));
|
|
|
|
/* save ssid */
|
|
nvt_if->nvt_wconf.ssid_len = sme->ssid_len;
|
|
memcpy(nvt_if->nvt_wconf.ssid, sme->ssid, nvt_if->nvt_wconf.ssid_len);
|
|
|
|
/* save channel */
|
|
if (ch) {
|
|
nvt_if->nvt_wconf.pref_channel =
|
|
ieee80211_frequency_to_channel(ch->center_freq);
|
|
}
|
|
|
|
/* save bssid */
|
|
if (sme->bssid && !is_broadcast_ether_addr(sme->bssid)) {
|
|
memcpy(nvt_if->nvt_wconf.bssid, sme->bssid, ETH_ALEN);
|
|
}
|
|
|
|
/* save 11i mode */
|
|
if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
|
|
sec->mode_11i |= WID_11I_WPA;
|
|
} else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
|
|
sec->mode_11i |= WID_11I_RSN;
|
|
}
|
|
/* reset ext mode before using it*/
|
|
sec->ext_mode_11i = 0;
|
|
/* save cipher */
|
|
if (sme->crypto.n_ciphers_pairwise) {
|
|
switch (sme->crypto.ciphers_pairwise[0]) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_WEP40);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_WEP104);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_TKIP);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_CCMP);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
sec->mode_11i |= WID_11I_SECURITY_ON;
|
|
sec->ext_mode_11i |= (WID_EXT_11I_WAPI |
|
|
WID_EXT_11I_SMS4);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
}
|
|
sec->ciphers_pairwise = sme->crypto.ciphers_pairwise[0];
|
|
if (sme->crypto.cipher_group) {
|
|
switch (sme->crypto.cipher_group) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_WEP40);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_WEP104);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_TKIP);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
sec->mode_11i |= (WID_11I_SECURITY_ON | WID_11I_CCMP);
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
sec->mode_11i |= WID_11I_SECURITY_ON;
|
|
sec->ext_mode_11i |= (WID_EXT_11I_WAPI |
|
|
WID_EXT_11I_SMS4);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
}
|
|
|
|
//set the auth-type
|
|
nvt_set_auth_by_ext_mode_11i(sec->ext_mode_11i, sec->mode_11i,
|
|
sme, sec);
|
|
|
|
/* save wep key */
|
|
if ((sme->key_len != 0) && (sme->crypto.ciphers_pairwise[0] &
|
|
(WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) {
|
|
sec->wep.key_id = sme->key_idx;
|
|
sec->wep.key_len = sme->key_len;
|
|
memcpy(sec->wep.key_data, sme->key, sme->key_len);
|
|
}
|
|
|
|
/* check if connected state. if connected, disconnect and connect new */
|
|
if (test_bit(NVT_IF_CONNECTED, &nvt_if->state_flags)) {
|
|
nvt_if->filter_disconnect = true;
|
|
nvt_disconnect_action(nvt_if);
|
|
nvt_if->filter_disconnect = false;
|
|
}
|
|
|
|
/* set probe req or assoc req extra ie to fw */
|
|
ret = nvt_set_extra_ies(nvt_if, sme);
|
|
if (ret < 0) {
|
|
nvt_dbg(CFG80211, "set extra ie fail\n");
|
|
goto err_ret;
|
|
}
|
|
|
|
/* check bss list */
|
|
if (nvt_find_bss(nvt_if)) {
|
|
/* find matching bss, do connect */
|
|
ret = nvt_connect_action(nvt_if);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
/* no find matching bss, scan again */
|
|
set_bit(SCAN_PROCESS, &cfg->scan_sts);
|
|
nvt_scan_init(cfg);
|
|
ssid_list[0].index = 0;
|
|
ssid_list[0].ssid_len = nvt_if->nvt_wconf.ssid_len;
|
|
if (ssid_list[0].ssid_len) {
|
|
memcpy(ssid_list[0].ssid, nvt_if->nvt_wconf.ssid,
|
|
nvt_if->nvt_wconf.ssid_len);
|
|
}
|
|
list_len += sizeof(struct _nvt_probe_ssid);
|
|
cfg->scan_if = nvt_if;
|
|
scan_filter = SCAN_NO_ADHOC |
|
|
SCAN_SAVE_WITHOUTRSSI | SCAN_CURRENTCH;
|
|
if (cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P,
|
|
sme->ie, sme->ie_len)) {
|
|
p2p_ie = 1;
|
|
}
|
|
|
|
if (nvt_if->nvt_wconf.pref_channel) {
|
|
ret = nvt_scan_action(wiphy, nvt_if->nvt_adapter, 1,
|
|
&sme->channel, (u8 *)ssid_list, list_len,
|
|
ACTIVE_SCAN, DEFAULT_ACTIVE_TIME, scan_filter,
|
|
p2p_ie);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
ret = nvt_scan_action(wiphy, nvt_if->nvt_adapter, 0,
|
|
NULL, (u8 *)ssid_list, list_len,
|
|
ACTIVE_SCAN, DEFAULT_ACTIVE_TIME, scan_filter,
|
|
p2p_ie);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
|
|
err_ret:
|
|
clear_bit(NVT_IF_CONNECTING, &nvt_if->state_flags);
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_disconnect(struct wiphy *wiphy,
|
|
struct net_device *ndev, u16 reason_code)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
|
|
if (!check_if_ready(nvt_if)) {
|
|
return -EIO;
|
|
}
|
|
|
|
nvt_disconnect_action(nvt_if);
|
|
|
|
return 0;
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
static void nvt_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
|
|
struct wireless_dev *wdev, u16 frame_type, bool reg)
|
|
{
|
|
struct _nvt_if *nvt_if = container_of(wdev, struct _nvt_if, wdev);
|
|
#else
|
|
static void nvt_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
|
|
struct net_device *ndev, u16 frame_type, bool reg)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
#endif
|
|
u16 mgmt_type;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
nvt_dbg(CFG80211, "frame_type = %04x, reg = %d\n", frame_type, reg);
|
|
|
|
mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
|
|
|
|
if (reg) {
|
|
nvt_if->mgmt_rx_reg |= BIT(mgmt_type);
|
|
} else {
|
|
nvt_if->mgmt_rx_reg &= ~BIT(mgmt_type);
|
|
}
|
|
}
|
|
|
|
static void nvt_send_probe_resp_frame(struct _nvt_cfg80211 *nvt_cfg,
|
|
struct _nvt_if *nvt_if, u16 channel_nr, unsigned int wait,
|
|
const u8 *buf, u8 *wid_frame_buf, size_t len)
|
|
{
|
|
struct _nvt_p2p *nvt_p2p = &nvt_cfg->nvt_p2p;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
const u8 *ie_ptr = NULL;
|
|
s32 ie_offset;
|
|
s32 ie_len;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
|
|
/* get DSSS Parameter Set value */
|
|
ie_offset = IEEE80211_BCN_PRB_FIXED_LEN;
|
|
ie_len = cpu_to_le16(len - ie_offset);
|
|
ie_ptr = cfg80211_find_ie(WLAN_EID_DS_PARAMS,
|
|
(u8 *)&buf[ie_offset], ie_len);
|
|
if (ie_ptr == NULL) {
|
|
nvt_dbg(P2P, "ie_ptr == NULL\n");
|
|
return;
|
|
}
|
|
nvt_dbg(P2P, "DSSS Parameter IE = 0x%x\n", ie_ptr[2]);
|
|
|
|
if (test_bit(NVT_P2P_ROC_MODE, &nvt_p2p->status)) {
|
|
nvt_dbg(P2P, "FW is still Remain-on-channel right now\n");
|
|
if (nvt_p2p->listen_channel == ie_ptr[2]) {
|
|
nvt_dbg(P2P,
|
|
"DSSS value match ROC channel, send it!\n");
|
|
channel_nr = nvt_p2p->listen_channel;
|
|
} else {
|
|
nvt_dbg(P2P,
|
|
"DSSS value mismatch ROC channel, drop it!\n");
|
|
return;
|
|
}
|
|
} else {
|
|
nvt_dbg(P2P,
|
|
"Not ROC now, send probe resp. on specified channel\n");
|
|
channel_nr = ie_ptr[2];
|
|
}
|
|
|
|
wid_frame_buf[0] = channel_nr;
|
|
memcpy(&wid_frame_buf[1], buf, len);
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_MGMT_TX, (u8 *)wid_frame_buf, len + 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
nvt_dbg(P2P, "WID_MGMT_TX(probe resp.) has problems\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
static bool nvt_is_p2p_public_action(const void *data, u32 msg_len)
|
|
{
|
|
struct _nvt_p2p_public_action *p2p_pub_act;
|
|
|
|
if (data == NULL) {
|
|
return false;
|
|
}
|
|
p2p_pub_act = (struct _nvt_p2p_public_action *)data;
|
|
if (msg_len < sizeof(struct _nvt_p2p_public_action) - 1) {
|
|
return false;
|
|
}
|
|
if (p2p_pub_act->category == P2P_PUBLIC_ACTION_CATEGORY &&
|
|
p2p_pub_act->action == P2P_PUBLIC_ACTION_FIELD &&
|
|
memcmp(p2p_pub_act->oui, P2P_OUI, P2P_OUI_LEN) == 0 &&
|
|
p2p_pub_act->oui_type == P2P_OUI_TYPE) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool nvt_is_p2p_action(const void *data, u32 msg_len)
|
|
{
|
|
struct _nvt_p2p_action *p2p_act;
|
|
|
|
if (data == NULL) {
|
|
return false;
|
|
}
|
|
p2p_act = (struct _nvt_p2p_action *)data;
|
|
if (msg_len < sizeof(struct _nvt_p2p_action) - 1) {
|
|
return false;
|
|
}
|
|
if (p2p_act->category == P2P_ACTION_CATEGORY &&
|
|
memcmp(p2p_act->oui, P2P_OUI, P2P_OUI_LEN) == 0 &&
|
|
p2p_act->oui_type == P2P_OUI_TYPE) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool nvt_is_p2p_gas_public_action(const void *data, u32 msg_len)
|
|
{
|
|
struct _nvt_p2p_gas_public_action *p2p_gas_pub_act;
|
|
|
|
if (data == NULL) {
|
|
return false;
|
|
}
|
|
p2p_gas_pub_act = (struct _nvt_p2p_gas_public_action *)data;
|
|
if (msg_len < sizeof(struct _nvt_p2p_gas_public_action) - 1) {
|
|
return false;
|
|
}
|
|
if (p2p_gas_pub_act->category != P2P_PUBLIC_ACTION_CATEGORY) {
|
|
return false;
|
|
}
|
|
if (p2p_gas_pub_act->action == P2P_GAS_INITIAL_REQ ||
|
|
p2p_gas_pub_act->action == P2P_GAS_INITIAL_RSP ||
|
|
p2p_gas_pub_act->action == P2P_GAS_COMEBACK_REQ ||
|
|
p2p_gas_pub_act->action == P2P_GAS_COMEBACK_RSP) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void nvt_dbg_p2p(bool tx, const void *data, u32 msg_len)
|
|
{
|
|
struct _nvt_p2p_public_action *p2p_pub_act;
|
|
struct _nvt_p2p_action *p2p_act;
|
|
struct _nvt_p2p_gas_public_action *p2p_gas_pub_act;
|
|
|
|
if (!data || msg_len <= 2) {
|
|
return;
|
|
}
|
|
|
|
nvt_dbg(P2P, "%s", (tx) ? "TX " : "RX ");
|
|
if (nvt_is_p2p_public_action(data, msg_len)) {
|
|
p2p_pub_act = (struct _nvt_p2p_public_action *)data;
|
|
switch (p2p_pub_act->subtype) {
|
|
case P2P_GO_NEGO_REQ:
|
|
nvt_dbg(P2P, "P2P GO NEGO REQ Frame\n");
|
|
break;
|
|
case P2P_GO_NEGO_RSP:
|
|
nvt_dbg(P2P, "P2P GO NEGO RSP Frame\n");
|
|
break;
|
|
case P2P_GO_NEGO_CONF:
|
|
nvt_dbg(P2P, "P2P GO NEGO CONF Frame\n");
|
|
break;
|
|
case P2P_INVITE_REQ:
|
|
nvt_dbg(P2P, "P2P INVITE REQ Frame\n");
|
|
break;
|
|
case P2P_INVITE_RSP:
|
|
nvt_dbg(P2P, "P2P INVITE RSP Frame\n");
|
|
break;
|
|
case P2P_DEVICE_DISC_REQ:
|
|
nvt_dbg(P2P, "P2P DEVICE DISC REQ Frame\n");
|
|
break;
|
|
case P2P_DEVICE_DISC_RSP:
|
|
nvt_dbg(P2P, "P2P DEVICE DISC RSP Frame\n");
|
|
break;
|
|
case P2P_PROV_DISC_REQ:
|
|
nvt_dbg(P2P, "P2P PRIV DISC REQ Frame\n");
|
|
break;
|
|
case P2P_PROV_DISC_RSP:
|
|
nvt_dbg(P2P, "P2P PRIV DISC RSP Frame\n");
|
|
break;
|
|
default:
|
|
nvt_dbg(P2P, "Unknown P2P Public Action Frame\n");
|
|
break;
|
|
}
|
|
} else if (nvt_is_p2p_gas_public_action(data, msg_len)) {
|
|
p2p_gas_pub_act = (struct _nvt_p2p_gas_public_action *)data;
|
|
switch (p2p_gas_pub_act->action) {
|
|
case P2P_GAS_INITIAL_REQ:
|
|
nvt_dbg(P2P, "P2P GAS INITIAL REQ\n");
|
|
break;
|
|
case P2P_GAS_INITIAL_RSP:
|
|
nvt_dbg(P2P, "P2P GAS INITIAL RSP\n");
|
|
break;
|
|
case P2P_GAS_COMEBACK_REQ:
|
|
nvt_dbg(P2P, "P2P GAS COMEBACK REQ\n");
|
|
break;
|
|
case P2P_GAS_COMEBACK_RSP:
|
|
nvt_dbg(P2P, "P2P GAS COMEBACK RSP\n");
|
|
break;
|
|
default:
|
|
nvt_dbg(P2P, "Unknown P2P GAS Frame\n");
|
|
break;
|
|
}
|
|
} else if (nvt_is_p2p_action(data, msg_len)) {
|
|
p2p_act = (struct _nvt_p2p_action *)data;
|
|
switch (p2p_act->subtype) {
|
|
case P2P_NOA:
|
|
nvt_dbg(P2P, "P2P NOA Frame\n");
|
|
break;
|
|
case P2P_PRESENCE_REQ:
|
|
nvt_dbg(P2P, "P2P PRESENCE REQ Frame\n");
|
|
break;
|
|
case P2P_PRESENCE_RSP:
|
|
nvt_dbg(P2P, "P2P PRESENCE RSP Frame\n");
|
|
break;
|
|
case P2P_GO_DISC_REQ:
|
|
nvt_dbg(P2P, "P2P GO DISC REQ Frame\n");
|
|
break;
|
|
default:
|
|
nvt_dbg(P2P, "Unknown P2P Action Frame\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static s32 nvt_p2p_public_action_handle(struct _nvt_adapter *nvt_adapter,
|
|
const void *data)
|
|
{
|
|
struct _nvt_p2p_public_action *p2p_pub_act;
|
|
struct _nvt_bus *nvt_bus = nvt_adapter->nvt_bus;
|
|
s32 ret = 0;
|
|
u16 wait;
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
|
|
p2p_pub_act = (struct _nvt_p2p_public_action *)(data);
|
|
|
|
switch (p2p_pub_act->subtype) {
|
|
case P2P_GO_NEGO_REQ:
|
|
case P2P_GO_NEGO_RSP:
|
|
nvt_dbg(P2P, "handle GO NEGO REQ/RSP frame\n");
|
|
wait = P2P_NEGO_DWELL_TIME;
|
|
ret = nvt_icfg_lock(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_P2P_LISTEN_EXTEND, (u8 *)&wait, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
nvt_icfg_unlock(nvt_bus);
|
|
break;
|
|
default:
|
|
nvt_dbg(P2P, "p2p pub act frame subtype: %d, do nothing\n",
|
|
p2p_pub_act->subtype);
|
|
}
|
|
return 0;
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_bus);
|
|
nvt_dbg(P2P, "WID_P2P_LISTEN_EXTEND has problems\n");
|
|
return ret;
|
|
}
|
|
|
|
static bool nvt_send_action_frame(struct _nvt_cfg80211 *nvt_cfg,
|
|
struct _nvt_if *nvt_if, u16 channel_nr, unsigned int wait,
|
|
const u8 *buf, u8 *wid_frame_buf, size_t len)
|
|
{
|
|
struct _nvt_p2p *nvt_p2p = &nvt_cfg->nvt_p2p;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_bus *nvt_bus = nvt_adapter->nvt_bus;
|
|
s32 ie_offset;
|
|
s32 ie_len;
|
|
u8 category;
|
|
u8 action;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
|
|
/* Add the length exepted for 802.11 header */
|
|
ie_offset = IEEE80211_MGMT_HDR_LEN;
|
|
ie_len = cpu_to_le16(len - ie_offset);
|
|
category = buf[ie_offset];
|
|
action = buf[ie_offset + 1];
|
|
|
|
nvt_dbg_p2p(true, &buf[ie_offset], ie_len);
|
|
|
|
if (nvt_is_p2p_public_action(&buf[ie_offset], ie_len)) {
|
|
/* Process p2p public action frame */
|
|
if (nvt_p2p_public_action_handle(nvt_adapter,
|
|
&buf[ie_offset])) {
|
|
nvt_dbg(P2P, "handle p2p public action error!!\n");
|
|
}
|
|
} else if (nvt_is_p2p_gas_public_action(&buf[ie_offset], ie_len)) {
|
|
/* Process p2p GAS frame */
|
|
/* Do nothing */
|
|
} else if (nvt_is_p2p_action(&buf[ie_offset], ie_len)) {
|
|
/* Process p2p action frame */
|
|
/* Do nothing */
|
|
} else {
|
|
nvt_dbg(P2P, "Unknown Frame: category 0x%x, action 0x%x\n",
|
|
category, action);
|
|
return false;
|
|
}
|
|
|
|
if (test_bit(NVT_P2P_ROC_MODE, &nvt_p2p->status)) {
|
|
nvt_dbg(P2P, "FW is ROC right now\n");
|
|
nvt_dbg(P2P, "Not support off-channel TX\n");
|
|
if (nvt_p2p->listen_channel == (u8)channel_nr) {
|
|
nvt_dbg(P2P, "Channel match ROC channel\n");
|
|
if (wait > 0) {
|
|
nvt_dbg(P2P, "wait > 0 is weird!!\n");
|
|
ret = nvt_icfg_lock(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_P2P_LISTEN_EXTEND, (u8 *)&wait, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_bus,
|
|
NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
nvt_icfg_unlock(nvt_bus);
|
|
} else {
|
|
nvt_dbg(P2P, "wait == 0 is correct\n");
|
|
}
|
|
} else {
|
|
nvt_dbg(P2P, "Channel mismatch ROC channel\n");
|
|
nvt_dbg(P2P, "Send it on specified channel\n");
|
|
}
|
|
} else {
|
|
nvt_dbg(P2P, "Not ROC now,send it on specified channel\n");
|
|
}
|
|
|
|
wid_frame_buf[0] = channel_nr;
|
|
memcpy(&wid_frame_buf[1], buf, len);
|
|
ret = nvt_icfg_lock(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_MGMT_TX, (u8 *)wid_frame_buf, len + 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return true;
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_bus);
|
|
nvt_dbg(P2P, "WID_MGMT_TX(action frame) has problems\n");
|
|
return false;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_mgmt_tx(struct wiphy *wiphy,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
struct wireless_dev *wdev,
|
|
#else
|
|
struct net_device *ndev,
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
|
|
struct ieee80211_channel *chan,
|
|
bool offchan,
|
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 7, 0)
|
|
enum nl80211_channel_type channel_type,
|
|
bool channel_type_valid,
|
|
#endif
|
|
unsigned int wait, const u8 *buf, size_t len, bool no_cck,
|
|
bool dont_wait_for_ack,
|
|
#else
|
|
struct cfg80211_mgmt_tx_params *params,
|
|
#endif /* KERNEL_vERSION (3, 14, 0) */
|
|
u64 *cookie)
|
|
{
|
|
struct _nvt_if *nvt_if;
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct _nvt_cfg80211 *nvt_cfg = wiphy_priv(wiphy);
|
|
struct _nvt_p2p *nvt_p2p = &nvt_cfg->nvt_p2p;
|
|
u8 *wid_frame_buf = NULL;
|
|
u16 channel_nr = 0;
|
|
u32 freq = 0;
|
|
bool ack;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
struct ieee80211_channel *chan = params->chan;
|
|
bool offchan = params->offchan;
|
|
unsigned int wait = params->wait;
|
|
size_t len = params->len;
|
|
bool no_cck = params->no_cck;
|
|
bool dont_wait_for_ack = params->dont_wait_for_ack;
|
|
const u8 *buf = params->buf;
|
|
#endif
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
nvt_dbg(P2P, "offchan = %d, wait = %d, no_cck = %d\n",
|
|
offchan, wait, no_cck);
|
|
nvt_dbg(P2P, "len = %zu, dont_wait_for_ack = %d\n",
|
|
len, dont_wait_for_ack);
|
|
|
|
if (chan) {
|
|
freq = chan->center_freq;
|
|
channel_nr = ieee80211_frequency_to_channel(chan->center_freq);
|
|
/* never send freq zero to the firmware */
|
|
if (WARN_ON(freq == 0)) {
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
nvt_dbg(P2P, "Chan is NULL !!!!, return error\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
nvt_dbg(P2P, "freq = %d, channel_nr = %d\n", freq, channel_nr);
|
|
nvt_dbg(P2P, "p2p->status = %lx, p2p->listen_channel = %d\n",
|
|
nvt_p2p->status, nvt_p2p->listen_channel);
|
|
|
|
*cookie = 0;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *)buf;
|
|
if (!ieee80211_is_mgmt(mgmt->frame_control)) {
|
|
nvt_dbg(CFG80211, "Driver only allows mgmt packet\n");
|
|
return -EPERM;
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
nvt_if = container_of(wdev, struct _nvt_if, wdev);
|
|
#else
|
|
nvt_if = netdev_priv(ndev);
|
|
#endif
|
|
|
|
wid_frame_buf = kmalloc(len + 1, GFP_KERNEL);
|
|
if (!wid_frame_buf) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
|
|
nvt_dbg(P2P, "Send Probe Response\n");
|
|
nvt_send_probe_resp_frame(nvt_cfg, nvt_if, channel_nr,
|
|
wait, buf, wid_frame_buf, len);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
cfg80211_mgmt_tx_status(wdev, *cookie, buf,
|
|
len, true, GFP_KERNEL);
|
|
#else
|
|
cfg80211_mgmt_tx_status(ndev, *cookie, buf,
|
|
len, true, GFP_KERNEL);
|
|
#endif
|
|
} else if (ieee80211_is_action(mgmt->frame_control)) {
|
|
ack = nvt_send_action_frame(nvt_cfg, nvt_if, channel_nr,
|
|
wait, buf, wid_frame_buf, len);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
cfg80211_mgmt_tx_status(wdev, *cookie, buf,
|
|
len, ack, GFP_KERNEL);
|
|
#else
|
|
cfg80211_mgmt_tx_status(ndev, *cookie, buf,
|
|
len, ack, GFP_KERNEL);
|
|
#endif
|
|
} else {
|
|
nvt_dbg(P2P, "Unhandled, fc=%04x!\n", mgmt->frame_control);
|
|
}
|
|
kfree(wid_frame_buf);
|
|
return 0;
|
|
}
|
|
|
|
static s32 nvt_do_roc(struct _nvt_if *nvt_if, u16 channel, u16 duration)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_probe_ssid ssid_list[MAX_PROBED_SSIDS];
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
u8 list_len = 0;
|
|
u8 scan_type = PASSIVE_SCAN;
|
|
u8 scan_filter = SCAN_NO_ADHOC | SCAN_SAVE_WITHOUTRSSI | SCAN_CURRENTCH;
|
|
u8 *channels_param = NULL;
|
|
u8 site_survey = 1;
|
|
u8 user_scan = 1;
|
|
u8 p2p_enable = 1;
|
|
u8 p2p_listen_mode = 1;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
memset(ssid_list, 0x0,
|
|
sizeof(struct _nvt_probe_ssid) * MAX_PROBED_SSIDS);
|
|
|
|
nvt_dbg(P2P, "wconf->ctrl_flag=0x%x, channel=%d\n",
|
|
wconf->ctrl_flag, channel);
|
|
nvt_dbg(P2P, "listen_duration=%d ms\n", duration);
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Enable P2P Listen Mode */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_P2P_ENABLE, (u8 *)&p2p_enable, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_P2P_LISTEN_MODE, (u8 *)&p2p_listen_mode, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Enable Passive Scan to Firmware */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_TYPE, (u8 *)&scan_type, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Set Scan Filter value */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_FILTER, (u8 *)&scan_filter, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Set Scan Time value */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_PASSIVE_SCAN_TIME, (u8 *)&duration, 2);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Set Scan Channel*/
|
|
channels_param = kzalloc(3, GFP_KERNEL);
|
|
if (channels_param == NULL) {
|
|
nvt_dbg(P2P, "Fail memory allocate!\n");
|
|
ret = -ENOMEM;
|
|
goto err_ret;
|
|
} else {
|
|
channels_param[0] = 1;
|
|
channels_param[1] = IEEE80211_BAND_2GHZ; /* 2G Band */
|
|
channels_param[2] = channel; /* Channel number */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_MULTICH, (u8 *)channels_param, 3);
|
|
if (ret < 0) {
|
|
kfree(channels_param);
|
|
goto err_ret;
|
|
}
|
|
kfree(channels_param);
|
|
}
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SITE_SURVEY, (u8 *)&site_survey, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Set SSID List */
|
|
ssid_list[0].index = 0;
|
|
ssid_list[0].ssid_len = 0;
|
|
list_len += sizeof(struct _nvt_probe_ssid);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_MULTISSID, (u8 *)ssid_list, list_len);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Set User Scan */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_START_SCAN_REQ, (u8 *)&user_scan, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
|
|
return ret;
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
nvt_dbg(P2P, "WID(START ROC) has problems\n");
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_do_cancel_roc(struct _nvt_if *nvt_if)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
u8 p2p_enable = 1;
|
|
u8 p2p_listen_mode = 0;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
nvt_dbg(P2P, "wconf->ctrl_flag=0x%x\n", wconf->ctrl_flag);
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
/* Disable P2P Listen Mode */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_P2P_ENABLE, (u8 *)&p2p_enable, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_P2P_LISTEN_MODE, (u8 *)&p2p_listen_mode, 1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
nvt_dbg(CFG80211, "WID(CANCEL ROC) has problems\n");
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* nvt_p2p_roc_done - handle ROC done(listen done)
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
* @direction: true - Firmware send ROC done to Driver
|
|
* false - WPA_Supplicant send ROC done to Driver
|
|
*
|
|
* Return: zero - success.
|
|
* negative - failure.
|
|
*/
|
|
s32 nvt_p2p_roc_done(struct _nvt_if *nvt_if,
|
|
u16 msg_len, u8 *data, bool direction)
|
|
{
|
|
struct _nvt_cfg80211 *nvt_cfg = nvt_if->nvt_adapter->nvt_cfg80211;
|
|
struct _nvt_p2p *nvt_p2p = &nvt_cfg->nvt_p2p;
|
|
|
|
nvt_dbg(P2P, "%s is recieved from %s\n",
|
|
__func__, (direction) ? "WPA_SUPPLICANT" : "FW");
|
|
|
|
nvt_dbg(P2P, "p2p->status=%lx\n", nvt_p2p->status);
|
|
|
|
if (test_and_clear_bit(NVT_P2P_ROC_MODE, &nvt_p2p->status)) {
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
cfg80211_remain_on_channel_expired(&nvt_if->wdev,
|
|
nvt_p2p->roc_cookie, &nvt_p2p->remain_on_channel,
|
|
GFP_KERNEL);
|
|
#else
|
|
cfg80211_remain_on_channel_expired(nvt_if->ndev,
|
|
nvt_p2p->roc_cookie, &nvt_p2p->remain_on_channel,
|
|
NL80211_CHAN_NO_HT, GFP_KERNEL);
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static s32 nvt_p2p_roc_handle(struct _nvt_if *nvt_if,
|
|
struct _nvt_p2p *nvt_p2p, u16 channel, u32 duration, u8 action)
|
|
{
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(P2P, "%s, p2p->status=%lx\n", __func__, nvt_p2p->status);
|
|
|
|
switch (action) {
|
|
case NVT_P2P_START_ROC:
|
|
if (test_bit(NVT_P2P_ROC_MODE, &nvt_p2p->status)) {
|
|
nvt_dbg(P2P, "Previous ROC is not finish yet\n");
|
|
goto exit;
|
|
}
|
|
nvt_p2p->listen_channel = (u8)channel;
|
|
nvt_dbg(P2P, "listen_channel=%d\n",
|
|
nvt_p2p->listen_channel);
|
|
ret = nvt_do_roc(nvt_if, channel, (u16)duration);
|
|
if (!ret) {
|
|
set_bit(NVT_P2P_ROC_MODE, &nvt_p2p->status);
|
|
nvt_p2p->roc_cookie++;
|
|
} else {
|
|
goto exit;
|
|
}
|
|
nvt_dbg(P2P, "ROC_cookie=%d\n", nvt_p2p->roc_cookie);
|
|
break;
|
|
case NVT_P2P_CANCEL_ROC:
|
|
ret = nvt_do_cancel_roc(nvt_if);
|
|
if (!ret) {
|
|
nvt_p2p_roc_done(nvt_if, 0, NULL, true);
|
|
} else {
|
|
goto exit;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
exit:
|
|
return ret;
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
static s32 nvt_cfg80211_remain_on_channel(struct wiphy *wiphy,
|
|
struct wireless_dev *wdev, struct ieee80211_channel *channel,
|
|
unsigned int duration, u64 *cookie)
|
|
{
|
|
struct _nvt_if *nvt_if = container_of(wdev, struct _nvt_if, wdev);
|
|
#else
|
|
static s32 nvt_cfg80211_remain_on_channel(struct wiphy *wiphy,
|
|
struct net_device *ndev,
|
|
struct ieee80211_channel *channel,
|
|
enum nl80211_channel_type channel_type,
|
|
unsigned int duration, u64 *cookie)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
#endif
|
|
struct _nvt_cfg80211 *nvt_cfg = wiphy_priv(wiphy);
|
|
struct _nvt_p2p *nvt_p2p = &nvt_cfg->nvt_p2p;
|
|
s32 ret = 0;
|
|
u16 channel_nr;
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
|
|
channel_nr = ieee80211_frequency_to_channel(channel->center_freq);
|
|
|
|
nvt_dbg(P2P, "channel->center_freq= %d, channel_nr = %d\n",
|
|
channel->center_freq, channel_nr);
|
|
nvt_dbg(P2P, "duration=%d\n", duration);
|
|
|
|
ret = nvt_p2p_roc_handle(nvt_if, nvt_p2p,
|
|
channel_nr, duration, NVT_P2P_START_ROC);
|
|
if (ret) {
|
|
goto exit;
|
|
}
|
|
memcpy(&nvt_p2p->remain_on_channel, channel, sizeof(*channel));
|
|
*cookie = nvt_p2p->roc_cookie;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL);
|
|
#else
|
|
cfg80211_ready_on_channel(ndev, *cookie, channel, channel_type,
|
|
duration, GFP_KERNEL);
|
|
#endif
|
|
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
static s32
|
|
nvt_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
|
struct wireless_dev *wdev, u64 cookie)
|
|
{
|
|
struct _nvt_if *nvt_if = container_of(wdev, struct _nvt_if, wdev);
|
|
#else
|
|
static s32
|
|
nvt_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
|
|
struct net_device *ndev, u64 cookie)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
#endif
|
|
struct _nvt_cfg80211 *nvt_cfg = wiphy_priv(wiphy);
|
|
struct _nvt_p2p *nvt_p2p = &nvt_cfg->nvt_p2p;
|
|
s32 ret = 0;
|
|
|
|
nvt_dbg(P2P, "%s\n", __func__);
|
|
|
|
ret = nvt_p2p_roc_handle(nvt_if, nvt_p2p, 0, 0, NVT_P2P_CANCEL_ROC);
|
|
|
|
return ret;
|
|
}
|
|
static int nvt_cfg80211_set_txpower(struct wiphy *wiphy,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
struct wireless_dev *wdev,
|
|
#endif
|
|
enum nl80211_tx_power_setting type,
|
|
int mbm)
|
|
{
|
|
struct _nvt_cfg80211 *nvt_cfg = wiphy_priv(wiphy);
|
|
struct _nvt_cfg80211 **cfg_2ptr = &nvt_cfg;
|
|
struct _nvt_adapter *nvt_adapter = container_of(cfg_2ptr,
|
|
struct _nvt_adapter, nvt_cfg80211);
|
|
struct _nvt_adapter **adapter_2ptr = &nvt_adapter;
|
|
struct _nvt_if *nvt_if = container_of(adapter_2ptr,
|
|
struct _nvt_if, nvt_adapter);
|
|
int dbm = MBM_TO_DBM(mbm);
|
|
|
|
if (!check_if_ready(nvt_if))
|
|
return -EIO;
|
|
|
|
switch (type) {
|
|
case NL80211_TX_POWER_AUTOMATIC:
|
|
return 0;
|
|
case NL80211_TX_POWER_LIMITED:
|
|
nvt_cfg->tx_pwr = dbm;
|
|
return 0;
|
|
case NL80211_TX_POWER_FIXED:
|
|
nvt_cfg->tx_pwr = dbm;
|
|
return 0;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
//TODO
|
|
//need to know what WID needed to send to the firmware
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int nvt_cfg80211_set_bitrate(struct wiphy *wiphy,
|
|
struct net_device *dev,
|
|
const u8 *addr,
|
|
const struct cfg80211_bitrate_mask *mask)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(dev);
|
|
//TODO replace return 0 with the WID commands to the firmware
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
u8 wid_msg[4] = {0};
|
|
s32 ret = 0;
|
|
|
|
//MCS32
|
|
wid_msg[0] = 00;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
|
|
//MCS support
|
|
wid_msg[1] = mask->control[0].ht_mcs[4];
|
|
//fill the mcs
|
|
wid_msg[2] = mask->control[0].ht_mcs[0];
|
|
wid_msg[3] = mask->control[0].ht_mcs[1];
|
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
//MCS support
|
|
wid_msg[1] = mask->control[0].mcs[4];
|
|
//fill the mcs
|
|
wid_msg[2] = mask->control[0].mcs[0];
|
|
wid_msg[3] = mask->control[0].mcs[1];
|
|
#endif
|
|
ret = nvt_icfg_lock(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_SET_RXMCS, wid_msg, 4);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return 0;
|
|
}
|
|
|
|
static int nvt_get_stat_cmd(struct wiphy *wiphy, struct _nvt_if *nvt_if,
|
|
struct station_info *sinfo, u8 mode)
|
|
{
|
|
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
s32 retval = 0;
|
|
u8 wid_val_pos = 0;
|
|
u32 buff_size = 512;
|
|
s32 wid_rate_val = 0;
|
|
u8 sgi;
|
|
u8 *resp = kzalloc(sizeof(u8)*512, GFP_KERNEL);
|
|
if (resp == NULL) {
|
|
nvt_dbg(ERROR, "%s[0] kzalloc for diag_req is failed\n",
|
|
__func__);
|
|
retval = -1;
|
|
goto alloc_failed;
|
|
}
|
|
|
|
//the statistic we need for the iw dev link/station dump
|
|
nvt_get_wid_value(nvt_bus, WID_DTIM_PERIOD, resp,
|
|
buff_size, &wid_val_pos);
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 0)
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
|
|
#else
|
|
sinfo->filled |= STATION_INFO_BSS_PARAM;
|
|
#endif
|
|
sinfo->bss_param.dtim_period = resp[wid_val_pos];
|
|
nvt_get_wid_value(nvt_bus, WID_BEACON_INTERVAL,
|
|
resp, buff_size, &wid_val_pos);
|
|
sinfo->bss_param.beacon_interval = resp[wid_val_pos];
|
|
|
|
if (mode == NVT_STA_MODE) {
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 0)
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
|
|
//tx-rate
|
|
sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
|
|
#else
|
|
sinfo->filled |= STATION_INFO_RX_PACKETS;
|
|
sinfo->filled |= STATION_INFO_RX_BYTES;
|
|
sinfo->filled |= STATION_INFO_TX_PACKETS;
|
|
sinfo->filled |= STATION_INFO_TX_BYTES;
|
|
sinfo->filled |= STATION_INFO_SIGNAL;
|
|
//tx-rate
|
|
sinfo->filled |= STATION_INFO_TX_BITRATE;
|
|
#endif
|
|
sinfo->rx_packets = nvt_if->net_stats.rx_packets;
|
|
sinfo->rx_bytes = nvt_if->net_stats.rx_bytes;
|
|
|
|
sinfo->tx_packets = nvt_if->net_stats.tx_packets;
|
|
sinfo->tx_bytes = nvt_if->net_stats.tx_bytes;
|
|
|
|
nvt_get_wid_value(nvt_bus, WID_RSSI, resp,
|
|
buff_size, &wid_val_pos);
|
|
sinfo->signal = -1*resp[wid_val_pos];
|
|
|
|
nvt_get_wid_value(nvt_bus, WID_RATE_0, resp,
|
|
buff_size, &wid_val_pos);
|
|
/*check if the b7 of rate 0 */
|
|
if (resp[wid_val_pos] & 0x0080) {
|
|
nvt_if->ht_type = 1;
|
|
} else {
|
|
nvt_if->ht_type = 0;
|
|
}
|
|
wid_rate_val = nvt_parse_rate_0(resp[wid_val_pos]);
|
|
nvt_get_wid_value(nvt_bus, WID_11N_SHORT_GI_ENABLE, resp,
|
|
buff_size, &wid_val_pos);
|
|
sgi = resp[wid_val_pos];
|
|
|
|
if (nvt_if->ht_type == 0) {
|
|
sinfo->txrate.legacy = wid_rate_val / 100;
|
|
} else if (!sgi && nvt_if->ht_type == 1) {
|
|
sinfo->txrate.mcs = wid_rate_val;
|
|
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
|
|
} else if (sgi && nvt_if->ht_type == 1) {
|
|
sinfo->txrate.mcs = wid_rate_val;
|
|
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
|
|
sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
|
|
} else {
|
|
nvt_dbg(ERROR, "%s[0] get rate info is error!\n",
|
|
__func__);
|
|
}
|
|
//bss_flags
|
|
sinfo->bss_param.flags = 0;
|
|
nvt_get_wid_value(nvt_bus, WID_SHORT_SLOT_ALLOWED, resp,
|
|
buff_size, &wid_val_pos);
|
|
|
|
if (resp[wid_val_pos])
|
|
sinfo->bss_param.flags |=
|
|
BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
|
|
}
|
|
alloc_failed:
|
|
kfree(resp);
|
|
return retval;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
|
static int nvt_get_station(struct wiphy *wiphy, struct net_device *dev,
|
|
const u8 *mac, struct station_info *sinfo)
|
|
#else
|
|
static int nvt_get_station(struct wiphy *wiphy, struct net_device *dev,
|
|
u8 *mac, struct station_info *sinfo)
|
|
#endif
|
|
{
|
|
|
|
struct _nvt_if *nvt_if = netdev_priv(dev);
|
|
memcpy((void *)mac, nvt_if->nvt_wconf.bssid, ETH_ALEN);
|
|
if (!is_sta_connected(nvt_if)) {
|
|
return -ENOENT;
|
|
}
|
|
return nvt_get_stat_cmd(wiphy, nvt_if, sinfo, nvt_if->mode);
|
|
}
|
|
|
|
/*NOTE: NL80211 counts idx from zero*/
|
|
static int nvt_dump_station(struct wiphy *wiphy, struct net_device *dev,
|
|
int idx, u8 *mac, struct station_info *sinfo)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(dev);
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
u8 retval = 0;
|
|
u8 wid_val_pos = 0;
|
|
s32 num_of_sta;
|
|
u8 *buff = kzalloc(sizeof(u8)*512, GFP_KERNEL);
|
|
|
|
if (buff == NULL) {
|
|
nvt_dbg(ERROR, "%s[0] kzalloc for diag_req is failed\n",
|
|
__func__);
|
|
retval = -1;
|
|
goto alloc_failed;
|
|
}
|
|
|
|
/**station mode**/
|
|
if (nvt_if->mode == NVT_STA_MODE) {
|
|
if (idx || !is_sta_connected(nvt_if)) {
|
|
retval = -ENOENT;
|
|
goto alloc_failed;
|
|
}
|
|
memcpy(mac, nvt_if->nvt_wconf.bssid, ETH_ALEN);
|
|
kfree(buff);
|
|
return nvt_get_stat_cmd(wiphy, nvt_if, sinfo, nvt_if->mode);
|
|
/**In AP mode**/
|
|
} else if (nvt_if->mode == NVT_AP_MODE) {
|
|
nvt_get_wid_value(nvt_bus, WID_CONNECTED_STA_LIST,
|
|
buff, 512, &wid_val_pos);
|
|
/*wid_val_pos-2 is the length position*/
|
|
num_of_sta = get_num_of_conn_sta(&buff[wid_val_pos]
|
|
, buff[wid_val_pos-2]);
|
|
/*If no sta connected to ap => quit*/
|
|
if (num_of_sta == 0) {
|
|
retval = -ENOENT;
|
|
goto alloc_failed;
|
|
}
|
|
/*Loop all the sta connecting to the AP*/
|
|
if (idx != num_of_sta) {
|
|
memcpy(mac, &buff[idx * 16 + wid_val_pos + 1], 6);
|
|
kfree(buff);
|
|
return nvt_get_stat_cmd(wiphy,
|
|
nvt_if, sinfo, nvt_if->mode);
|
|
} else {
|
|
retval = -ENOENT;
|
|
goto alloc_failed;
|
|
}
|
|
} else {
|
|
retval = -ENOENT;
|
|
goto alloc_failed;
|
|
}
|
|
|
|
alloc_failed:
|
|
kfree(buff);
|
|
return retval;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
//Each pattern register can only support 31 bytes
|
|
static u8 nvt_get_num_of_pat_regs(struct cfg80211_wowlan *wow,
|
|
u8 pat_cnt)
|
|
{
|
|
u32 size_of_pattern = wow->patterns[pat_cnt].pattern_len;
|
|
u8 num_of_regs = 1;
|
|
while (size_of_pattern > WOW_MAXPATTERNLEN) {
|
|
size_of_pattern -= 31;
|
|
num_of_regs++;
|
|
}
|
|
return num_of_regs;
|
|
}
|
|
|
|
//AT sanity check for the patterns
|
|
static s32 nvt_is_pats_valid(struct cfg80211_wowlan *wow)
|
|
{
|
|
|
|
u8 cnt;
|
|
s32 ret = 0;
|
|
|
|
if (wow->n_patterns > 4) {
|
|
ret = -1;
|
|
} else if (wow->n_patterns > 2) {
|
|
//AT if pattern# equals 3 or 4 then each must smaller than
|
|
//WOW_MAXPATTERNLEN
|
|
for (cnt = 0; cnt < wow->n_patterns; cnt++) {
|
|
if (wow->patterns[cnt].pattern_len >
|
|
WOW_MAXPATTERNLEN) {
|
|
ret = -1;
|
|
}
|
|
}
|
|
} else if (wow->n_patterns == 2) {
|
|
//if only 2 patterns check each smaller than
|
|
//WOW_MAXPATTERNLEN * 2
|
|
for (cnt = 0; cnt < wow->n_patterns; cnt++) {
|
|
if (wow->patterns[cnt].pattern_len >
|
|
WOW_MAXPATTERNLEN * 2) {
|
|
ret = -1;
|
|
}
|
|
}
|
|
} else {
|
|
//only one pattern
|
|
if (wow->patterns[0].pattern_len >
|
|
4 * WOW_MAXPATTERNLEN) {
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void nvt_clean_unused_wow_mask(u32 *reg_mask,
|
|
u8 mask_len)
|
|
{
|
|
u8 i;
|
|
for (i = mask_len; i < WOW_MAXPATTERNLEN; i++) {
|
|
(*reg_mask) &= ~(1 << i);
|
|
}
|
|
}
|
|
|
|
//AT get the mask from register index
|
|
static void nvt_set_mask_by_reg_index(struct cfg80211_wowlan *wow,
|
|
u8 *reg_idx, struct wow_pattern *buf, u8 pat_cnt)
|
|
{
|
|
u32 *start_mask = (u32 *)(wow->patterns[pat_cnt].mask);
|
|
u8 pattern_len = wow->patterns[pat_cnt].pattern_len;
|
|
u8 mask_len = (pattern_len + 7) / 8;
|
|
u8 reg_mask_len = 0;
|
|
u32 reg_mask = 0;
|
|
|
|
if (wow->n_patterns > 2) {
|
|
nvt_clean_unused_wow_mask(start_mask, pattern_len);
|
|
memcpy((u32 *)&buf[*reg_idx].mask, start_mask,
|
|
mask_len);
|
|
} else {
|
|
if (wow->n_patterns == 2) {
|
|
if (*reg_idx == 0 || *reg_idx == 2) {
|
|
if (pattern_len > WOW_MAXPATTERNLEN) {
|
|
reg_mask_len = WOW_MAXPATTERNLEN;
|
|
} else {
|
|
reg_mask_len = pattern_len;
|
|
}
|
|
nvt_clean_unused_wow_mask(start_mask,
|
|
reg_mask_len);
|
|
|
|
//set to mask len per byte
|
|
reg_mask_len = (reg_mask_len + 7) / 8;
|
|
memcpy((u32 *)&buf[*reg_idx].mask,
|
|
start_mask, reg_mask_len);
|
|
} else {
|
|
reg_mask = *(start_mask + 1);
|
|
reg_mask <<= 1;
|
|
reg_mask &= ~(1 << 0);
|
|
//reg_mask now: BIT31:BIT62
|
|
reg_mask |= (((*start_mask) & (1 << 31))
|
|
>> 31);
|
|
//get the reg_mask_len
|
|
reg_mask_len = pattern_len -
|
|
WOW_MAXPATTERNLEN;
|
|
nvt_clean_unused_wow_mask(®_mask,
|
|
reg_mask_len);
|
|
reg_mask_len = (reg_mask_len + 7) / 8;
|
|
|
|
memcpy((u32 *)&buf[*reg_idx].mask,
|
|
®_mask,
|
|
reg_mask_len);
|
|
}
|
|
//only single long pattern
|
|
} else {
|
|
if (*reg_idx == 0) {
|
|
if (pattern_len > WOW_MAXPATTERNLEN) {
|
|
reg_mask_len = WOW_MAXPATTERNLEN;
|
|
} else {
|
|
reg_mask_len = pattern_len;
|
|
}
|
|
|
|
nvt_clean_unused_wow_mask(start_mask,
|
|
reg_mask_len);
|
|
reg_mask_len = (reg_mask_len + 7) / 8;
|
|
|
|
memcpy((u32 *)&buf[*reg_idx].mask,
|
|
start_mask,
|
|
reg_mask_len);
|
|
} else if (*reg_idx == 1) {
|
|
//reg_mask now : BIT32:BIT63
|
|
reg_mask = *(start_mask + 1);
|
|
reg_mask <<= 1;
|
|
reg_mask &= ~(1 << 0);
|
|
//reg_mask now: BIT31:BIT62
|
|
reg_mask |= (((*start_mask) & (1 << 31))
|
|
>> 31);
|
|
//get reg_mask_len
|
|
if (pattern_len >
|
|
2 * WOW_MAXPATTERNLEN) {
|
|
reg_mask_len = WOW_MAXPATTERNLEN;
|
|
} else {
|
|
reg_mask_len = pattern_len -
|
|
WOW_MAXPATTERNLEN;
|
|
}
|
|
nvt_clean_unused_wow_mask(®_mask,
|
|
reg_mask_len);
|
|
reg_mask_len = (reg_mask_len + 7) / 8;
|
|
memcpy((u32 *)&buf[*reg_idx].mask,
|
|
®_mask, reg_mask_len);
|
|
} else if (*reg_idx == 2) {
|
|
reg_mask = *(start_mask + 2);
|
|
reg_mask <<= 2;
|
|
//clear BIT62 and BIT63
|
|
reg_mask &= ~(1 << 0);
|
|
reg_mask &= ~(1 << 1);
|
|
//OR BIT62
|
|
reg_mask |= (((*(start_mask + 1)) & (1 << 30))
|
|
>> 30);
|
|
//OR BIT63
|
|
reg_mask |= ((((*(start_mask + 1)) & (1 << 31))
|
|
>> 31) << 1);
|
|
//get reg_mask_len
|
|
if (pattern_len >
|
|
3 * WOW_MAXPATTERNLEN) {
|
|
reg_mask_len = WOW_MAXPATTERNLEN;
|
|
} else {
|
|
reg_mask_len = pattern_len -
|
|
2 * WOW_MAXPATTERNLEN;
|
|
}
|
|
nvt_clean_unused_wow_mask(®_mask,
|
|
reg_mask_len);
|
|
reg_mask_len = (reg_mask_len + 7) / 8;
|
|
|
|
memcpy((u32 *)&buf[*reg_idx].mask,
|
|
®_mask, reg_mask_len);
|
|
} else {
|
|
reg_mask = *(start_mask + 3);
|
|
reg_mask <<= 3;
|
|
//clear BIT93 - BIT95
|
|
reg_mask &= ~(1 << 0);
|
|
reg_mask &= ~(1 << 1);
|
|
reg_mask &= ~(1 << 2);
|
|
//OR BIT93 to BIT95
|
|
reg_mask |= (((*(start_mask + 2)) & (1 << 29))
|
|
>> 29);
|
|
reg_mask |= ((((*(start_mask + 2)) & (1 << 30))
|
|
>> 30)
|
|
<< 1);
|
|
reg_mask |= ((((*(start_mask + 2)) & (1 << 31))
|
|
>> 31)
|
|
<< 2);
|
|
reg_mask_len = pattern_len -
|
|
3 * WOW_MAXPATTERNLEN;
|
|
nvt_clean_unused_wow_mask(®_mask,
|
|
reg_mask_len);
|
|
reg_mask_len = (reg_mask_len + 7) / 8;
|
|
memcpy((u32 *)&buf[*reg_idx].mask,
|
|
®_mask, reg_mask_len);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//AT config HW registers to one WoWlan Pattern
|
|
static void nvt_set_pat_buffer(struct cfg80211_wowlan *wow, u8 pat_cnt,
|
|
struct wow_pattern *buf, u8 *reg_idx)
|
|
{
|
|
u8 num_of_regs = nvt_get_num_of_pat_regs(wow, pat_cnt);
|
|
u8 i, j;
|
|
u8 pos = 0;
|
|
//u32 size = 0;
|
|
u16 crc16 = 0;
|
|
u32 wid_pat_buf[WOW_MAXPATTERNLEN];
|
|
u8 remain_pat_len = wow->patterns[pat_cnt].pattern_len;
|
|
u8 end_of_size = 0;
|
|
//u8 *start_mask = wow->patterns[pat_cnt].mask;
|
|
//Looping each FW register
|
|
for (j = 0; j < num_of_regs; j++) {
|
|
//Looping the pattern bytes
|
|
|
|
if (remain_pat_len < WOW_MAXPATTERNLEN) {
|
|
end_of_size = remain_pat_len;
|
|
} else {
|
|
end_of_size = WOW_MAXPATTERNLEN;
|
|
}
|
|
|
|
for (i = j * WOW_MAXPATTERNLEN;
|
|
i < j * WOW_MAXPATTERNLEN + end_of_size; i++) {
|
|
if (wow->patterns[pat_cnt].mask[i/8] &
|
|
(0x01 << (i % 8))) {
|
|
wid_pat_buf[pos++] =
|
|
wow->patterns[pat_cnt].pattern[i];
|
|
}
|
|
}
|
|
|
|
if (remain_pat_len >= WOW_MAXPATTERNLEN) {
|
|
remain_pat_len -= WOW_MAXPATTERNLEN;
|
|
}
|
|
|
|
crc16 = compute_crc(wid_pat_buf, pos);
|
|
nvt_set_mask_by_reg_index(wow, reg_idx, buf, pat_cnt);
|
|
buf[*reg_idx].crc16 = crc16;
|
|
/*enable this filter register*/
|
|
buf[*reg_idx].filter_enable = 1;
|
|
//set offset only at first register
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
buf[*reg_idx].offset =
|
|
wow->patterns[pat_cnt].pkt_offset +
|
|
j * WOW_MAXPATTERNLEN;
|
|
#endif
|
|
|
|
nvt_dbg(CFG80211, "%d[msk:%x][of:%d][cr:%x]\n",
|
|
*reg_idx, buf[*reg_idx].mask,
|
|
buf[*reg_idx].offset, buf[*reg_idx].crc16);
|
|
if (num_of_regs == 1 && wow->n_patterns == 2 && *reg_idx == 0)
|
|
*reg_idx += 2;
|
|
else
|
|
*reg_idx += 1;
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
s32 nvt_setup_wowlan(struct _nvt_if *nvt_if, struct _nvt_cfg80211 *nvt_cfg,
|
|
struct cfg80211_wowlan *wow)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_cfg->nvt_adapter;
|
|
s32 ret = 0;
|
|
u8 wow_mode = 0;
|
|
u8 wow_enable = 0;
|
|
u8 cnt = 0;
|
|
u8 idx = 0;
|
|
//u8 i = 0;
|
|
//u32 pattern[WOW_MAXPATTERNLEN];
|
|
//u8 pos = 0;
|
|
//u16 crc16 = 0;
|
|
//u32 size = 0;
|
|
struct wow_pattern buf[WOW_MAXPATTERNS];
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
/* todo: setup arp/ns/rekey offload */
|
|
/* setup wow mode */
|
|
if (wow == 0) {
|
|
nvt_dbg(CFG80211, "mode=0x%x\n", wow_mode);
|
|
goto err_ret;
|
|
}
|
|
if (wow->magic_pkt) {
|
|
wow_mode |= WOW_MAGIC_PKT;
|
|
}
|
|
if (wow->disconnect) {
|
|
wow_mode |= WOW_DISCONNECT;
|
|
}
|
|
|
|
if ((wow->patterns) && (wow->n_patterns)) {
|
|
nvt_dbg(CFG80211, "pattern num=%d\n", wow->n_patterns);
|
|
if (nvt_is_pats_valid(wow) < 0) {
|
|
nvt_dbg(ERROR, "pattern len check fail\n");
|
|
|
|
} else {
|
|
memset(buf, 0x0,
|
|
sizeof(struct wow_pattern) * WOW_MAXPATTERNS);
|
|
wow_mode |= WOW_PATTERN;
|
|
|
|
//configuraion of HW register concatenation
|
|
if (wow->n_patterns > 2) {
|
|
wow_mode |= WOW_4_REG;
|
|
} else if (wow->n_patterns == 2) {
|
|
wow_mode |= WOW_2_REG;
|
|
} else {
|
|
wow_mode |= WOW_1_REG;
|
|
}
|
|
|
|
for (cnt = 0; cnt < wow->n_patterns; cnt++) {
|
|
nvt_set_pat_buffer(wow, cnt, buf, &idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
nvt_dbg(CFG80211, "mode=0x%x\n", wow_mode);
|
|
/* setup wow */
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_WOW_CONFIG, (u8 *)&wow_mode, sizeof(wow_mode));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
if (idx != 0) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_WOW_PATTERN, (u8 *)buf,
|
|
sizeof(struct wow_pattern)*idx);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
/* set skip_dtim */
|
|
if (nvt_if->skip_dtim_scalar != 0) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SET_DTIM_SCALAR, (u8 *)&nvt_if->skip_dtim_scalar,
|
|
sizeof(nvt_if->skip_dtim_scalar));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
if (nvt_if->skip_dtim_time != 0) {
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SET_DTIM_TIME, (u8 *)&nvt_if->skip_dtim_time,
|
|
sizeof(nvt_if->skip_dtim_time));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
}
|
|
/* set ignore_bcn_bitmap */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_IGNORE_BCN_BITMAP, (u8 *)&nvt_if->ignore_bcn_bitmap,
|
|
sizeof(nvt_if->ignore_bcn_bitmap));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
if (wow_mode != 0) {
|
|
wow_enable = 1;
|
|
}
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_WOW_ENABLE, (u8 *)&wow_enable, sizeof(wow_enable));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus,
|
|
NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static s32 nvt_cfg80211_set_default_mgmt_key(struct wiphy *wiphy,
|
|
struct net_device *ndev, u8 key_id)
|
|
{
|
|
nvt_dbg(CFG80211, "set default mgmt key,id=%d\n", key_id);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static s32 nvt_cfg80211_resume(struct wiphy *wiphy)
|
|
{
|
|
struct _nvt_cfg80211 *cfg = (struct _nvt_cfg80211 *)wiphy_priv(wiphy);
|
|
struct _nvt_adapter *nvt_adapter = cfg->nvt_adapter;
|
|
struct _nvt_if *nvt_if = nvt_vif_first(nvt_adapter);
|
|
s32 ret = 0;
|
|
u8 fc_enable = 1;
|
|
u8 i = 0;
|
|
u8 buf[64] = {0};
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
if (!nvt_if) {
|
|
nvt_dbg(ERROR, "nvt_if == NULL\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
//nash:work around for nt96660
|
|
ret |= nvt_sdio_wa_resume(nvt_adapter->nvt_bus);
|
|
|
|
/* Disable flow control before suspend */
|
|
ret = nvt_send_wid_by_numeric(nvt_adapter->nvt_bus,
|
|
WID_FC_ENABLE, &fc_enable, 1);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s: FC enable fail\n",
|
|
__func__);
|
|
}
|
|
|
|
/* Prepare DELBA for all TID Command to FW before resume */
|
|
for (i = 0; i < 8; i++) {
|
|
buf[0] = 12;
|
|
/* Block Ack Category */
|
|
buf[1] = 3;
|
|
/* MLME_DELBA_REQ_TYPE */
|
|
buf[2] = 2;
|
|
memcpy(&buf[3], nvt_if->nvt_wconf.bssid, ETH_ALEN);
|
|
/* TID */
|
|
buf[9] = i;
|
|
/* Recipient */
|
|
buf[10] = 0;
|
|
/* QSTA_TIMEOUT */
|
|
buf[11] = 39;
|
|
ret = nvt_send_wid_by_numeric(nvt_adapter->nvt_bus,
|
|
WID_11N_P_ACTION_REQ, (u8 *)&buf, 12);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s: delba on tid(%d) fail\n",
|
|
__func__, i);
|
|
}
|
|
}
|
|
|
|
if (nvt_if->sleep_mode != NVT_SLEEP_MODE_DISABLE) {
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_WOW_HOST_RESUME, (u8 *)&(nvt_if->sleep_mode),
|
|
sizeof(nvt_if->sleep_mode));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
s32 nvt_set_arp_ns_offload(struct _nvt_if *nvt_if)
|
|
{
|
|
struct net_device *ndev = nvt_if->ndev;
|
|
u8 *ipv4_addr;
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
u8 *ipv6_addr;
|
|
#endif
|
|
u8 *wid_val_buf;
|
|
s32 ret;
|
|
u8 buf_size;
|
|
ret = 0;
|
|
buf_size = 1;
|
|
|
|
/* allocate buffer for IPv4 */
|
|
ipv4_addr = kzalloc(4, GFP_KERNEL);
|
|
if (!ipv4_addr) {
|
|
nvt_dbg(ERROR, "%s: IPv4 buffer created fail\n",
|
|
__func__);
|
|
ret = -1;
|
|
goto err_ret;
|
|
|
|
}
|
|
|
|
/* check if IPv4 address is NULL */
|
|
ret = nvt_get_first_ipv4(ndev, ipv4_addr);
|
|
if (ret == 0)
|
|
buf_size += 4;
|
|
else
|
|
ret = 0;
|
|
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
/* allocate buffer for IPv6 */
|
|
ipv6_addr = kzalloc(16, GFP_KERNEL);
|
|
if (!ipv6_addr) {
|
|
nvt_dbg(ERROR, "%s: IPv6 buffer created fail\n",
|
|
__func__);
|
|
ret = -1;
|
|
kfree(ipv4_addr);
|
|
goto err_ret;
|
|
}
|
|
/* check if IPv6 address is being set by user */
|
|
ret = nvt_get_first_ipv6(ndev, ipv6_addr);
|
|
if (ret == 0)
|
|
buf_size += 16;
|
|
else
|
|
ret = 0;
|
|
#else
|
|
nvt_dbg(CFG80211, "%s: KERNEL doesnot support IPv6\n", __func__);
|
|
#endif
|
|
|
|
/* create the buffer for sending to FW */
|
|
wid_val_buf = kzalloc(buf_size, GFP_KERNEL);
|
|
if (!wid_val_buf) {
|
|
nvt_dbg(ERROR, "%s: buffer send to FW created fail\n",
|
|
__func__);
|
|
ret = -1;
|
|
kfree(ipv4_addr);
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
kfree(ipv6_addr);
|
|
#endif
|
|
goto err_ret;
|
|
}
|
|
|
|
if (buf_size == 1) {
|
|
nvt_dbg(CFG80211, "%s: NO ARP-NS OFFLOAD\n", __func__);
|
|
*wid_val_buf = 0;
|
|
/* Only set IPv4 address */
|
|
} else if (buf_size == 5) {
|
|
*wid_val_buf = 128;
|
|
nvt_dbg(CFG80211, "%s: ARP OFFLOAD only\n", __func__);
|
|
memcpy(wid_val_buf + 1, ipv4_addr, 4);
|
|
/* Only set IPv6 address */
|
|
} else {
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
if (buf_size == 17) {
|
|
*wid_val_buf = 64;
|
|
nvt_dbg(CFG80211, "%s: NS OFFLOAD only\n",
|
|
__func__);
|
|
memcpy(wid_val_buf + 1, ipv6_addr, 16);
|
|
} else {
|
|
/* case: buf_size == 21 */
|
|
*wid_val_buf = 192;
|
|
nvt_dbg(CFG80211, "%s: ARP-NS OFFLOAD\n",
|
|
__func__);
|
|
memcpy(wid_val_buf + 1, ipv4_addr, 4);
|
|
memcpy(wid_val_buf + 5, ipv6_addr, 16);
|
|
}
|
|
#endif
|
|
}
|
|
if (ret == 0)
|
|
ret = nvt_send_wid_by_numeric(nvt_if->nvt_adapter->nvt_bus,
|
|
WID_ARP_NS_OFFLOAD, wid_val_buf, buf_size);
|
|
|
|
kfree(ipv4_addr);
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
kfree(ipv6_addr);
|
|
#endif
|
|
kfree(wid_val_buf);
|
|
err_ret:
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
s32 nvt_set_wowlan_on(struct cfg80211_wowlan *wow, struct _nvt_if *nvt_if)
|
|
{
|
|
struct _nvt_cfg80211 *nvt_cfg = nvt_if->nvt_adapter->nvt_cfg80211;
|
|
s32 ret = -1;
|
|
if (wow != NULL) {
|
|
if (nvt_setup_wowlan(nvt_if, nvt_cfg, wow) == 0) {
|
|
nvt_cfg->wow_enable = 1;
|
|
ret = 0;
|
|
} else {
|
|
nvt_cfg->wow_enable = 0;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_suspend(struct wiphy *wiphy,
|
|
struct cfg80211_wowlan *wow)
|
|
{
|
|
struct _nvt_cfg80211 *cfg = (struct _nvt_cfg80211 *)wiphy_priv(wiphy);
|
|
struct _nvt_adapter *nvt_adapter = cfg->nvt_adapter;
|
|
struct _nvt_if *nvt_if = nvt_vif_first(nvt_adapter);
|
|
s32 ret = 0;
|
|
u8 fc_enabled = 0;
|
|
u8 pn_byte_cnt = 0;
|
|
u8 sta_index;
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
if (!nvt_if) {
|
|
nvt_dbg(ERROR, "nvt_if == NULL\n");
|
|
return -EPERM;
|
|
}
|
|
if (!check_if_ready(nvt_if)) {
|
|
return 0;
|
|
}
|
|
|
|
if (nvt_txmq == 0) {
|
|
netif_stop_queue(nvt_if->ndev);
|
|
} else {
|
|
netif_tx_stop_all_queues(nvt_if->ndev);
|
|
}
|
|
|
|
nvt_abort_scanning(cfg);
|
|
/* Disable flow control before suspend */
|
|
ret = nvt_send_wid_by_numeric(nvt_adapter->nvt_bus,
|
|
WID_FC_ENABLE, &fc_enabled, 1);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s: FC disable fail\n",
|
|
__func__);
|
|
}
|
|
/* Reset BA handlers */
|
|
nvt_reset_ba_handle(nvt_adapter, 1);
|
|
//AT: reset broadcast pn to 0
|
|
if (nvt_adapter->nvt_priv.pn_bcmc != NULL) {
|
|
for (pn_byte_cnt = 0; pn_byte_cnt < 6; pn_byte_cnt++) {
|
|
nvt_adapter->nvt_priv.pn_bcmc[pn_byte_cnt] = 0;
|
|
}
|
|
}
|
|
/*AT reset the unicast when f/w offload 4-way handshake */
|
|
if (nvt_ptk_offload_enable &&
|
|
(nvt_adapter->nvt_fw_cap.mac_cap_1 &
|
|
FW_MAC_CAP_1_INTERNAL_SUPP_CAP)) {
|
|
for (sta_index = 1; sta_index < 9; sta_index++) {
|
|
nvt_reset_unicast_pn(nvt_adapter, sta_index);
|
|
}
|
|
}
|
|
|
|
if ((wow == NULL) ||
|
|
(!test_bit(NVT_IF_CONNECTED, &nvt_if->state_flags))) {
|
|
nvt_dbg(CFG80211, "%s without wow\n", __func__);
|
|
if (test_bit(NVT_IF_CONNECTED, &nvt_if->state_flags)) {
|
|
nvt_disconnect_action(nvt_if);
|
|
}
|
|
nvt_if->sleep_mode = NVT_SLEEP_MODE_NORMAL;
|
|
} else {
|
|
nvt_dbg(CFG80211, "%s with wow\n", __func__);
|
|
|
|
nvt_if->sleep_mode = NVT_SLEEP_MODE_WOW;
|
|
|
|
/* set ap recover */
|
|
ret = nvt_send_wid_by_numeric(nvt_adapter->nvt_bus,
|
|
WID_AP_RECOVER, nvt_if->recover_buf,
|
|
RECOVER_BUF_LEN);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s: WID_AP_RECOVER fail\n", __func__);
|
|
}
|
|
|
|
/* set arp_ns_offload */
|
|
ret = nvt_set_arp_ns_offload(nvt_if);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s: arp_ns_offload send fail to FW\n",
|
|
__func__);
|
|
}
|
|
/* set pmt module and wowlan main flag on */
|
|
ret = nvt_set_wowlan_on(wow, nvt_if);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s: arp_ns_offload send fail to FW\n",
|
|
__func__);
|
|
}
|
|
}
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_WOW_HOST_SUSPEND, (u8 *)&(nvt_if->sleep_mode),
|
|
sizeof(nvt_if->sleep_mode));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
|
|
//netif_stop_queue(nvt_if->ndev);
|
|
|
|
//nash:work around for nt96660
|
|
ret |= nvt_sdio_wa_suspend(nvt_adapter->nvt_bus);
|
|
|
|
return ret;
|
|
}
|
|
#endif /*CONFIG_PM*/
|
|
|
|
static s32 nvt_set_gtk_rekey_data(struct wiphy *wiphy,
|
|
struct net_device *ndev, struct cfg80211_gtk_rekey_data *data)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_sec *sec = &nvt_if->nvt_wconf.nvt_sec;
|
|
s32 ret = 0;
|
|
static s8 count;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
count++;
|
|
if (sec->ext_mode_11i & WID_EXT_11I_WAPI) {
|
|
nvt_dbg(CFG80211, "WAPI REKEY OFFLOAD\n");
|
|
if (count == 1 &&
|
|
memcmp(data->replay_ctr,
|
|
(u8 *) "\xff\xff\xff\xff\xff\xff\xff\xff",
|
|
NL80211_REPLAY_CTR_LEN) == 0) {
|
|
nvt_dbg(CFG80211, "Bufffer first half\n");
|
|
memset(&sec->wapi_rekey_offload, 0,
|
|
sizeof(struct _nvt_wapi_rekey_offload_data));
|
|
memcpy(sec->wapi_rekey_offload.kek,
|
|
data->kek, NL80211_KEK_LEN);
|
|
memcpy(sec->wapi_rekey_offload.kck,
|
|
data->kck, NL80211_KCK_LEN);
|
|
ret = 0;
|
|
goto err_ret;
|
|
} else if (count == 2 &&
|
|
memcmp(data->kek, data->kck, NL80211_KEK_LEN) == 0) {
|
|
nvt_dbg(CFG80211, "Bufffer second half\n");
|
|
count = 0;
|
|
/* copy reply_counter */
|
|
memcpy(sec->wapi_rekey_offload.replay_ctr,
|
|
data->kek, WAPI_REPLAY_CTR_LEN);
|
|
/* copy seq_num */
|
|
memcpy(sec->wapi_rekey_offload.seq_num,
|
|
data->replay_ctr, 8);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_WAPI_REKEY_OFFLOAD,
|
|
(u8 *)&sec->wapi_rekey_offload,
|
|
sizeof(struct _nvt_wapi_rekey_offload_data));
|
|
if (ret < 0)
|
|
goto err_ret;
|
|
} else {
|
|
nvt_dbg(CFG80211, "count=%d,Something Wrong!\n", count);
|
|
count = 0;
|
|
memset(&sec->wapi_rekey_offload, 0,
|
|
sizeof(struct _nvt_wapi_rekey_offload_data));
|
|
ret = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
} else if ((sec->mode_11i & WID_11I_TKIP) ||
|
|
(sec->mode_11i & WID_11I_CCMP)) {
|
|
nvt_dbg(CFG80211, "WPA REKEY OFFLOAD\n");
|
|
count = 0;
|
|
memset(&sec->wpa_rekey_offload, 0,
|
|
sizeof(struct _nvt_wpa_rekey_offload_data));
|
|
memcpy(sec->wpa_rekey_offload.kek, data->kek, NL80211_KEK_LEN);
|
|
memcpy(sec->wpa_rekey_offload.kck, data->kck, NL80211_KCK_LEN);
|
|
memcpy(sec->wpa_rekey_offload.replay_ctr,
|
|
data->replay_ctr, NL80211_REPLAY_CTR_LEN);
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus,
|
|
NVT_ICFG_SET, WID_WPA_REKEY_OFFLOAD,
|
|
(u8 *)&sec->wpa_rekey_offload,
|
|
sizeof(struct _nvt_wpa_rekey_offload_data));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "Rekey but not in WPA/WPA2/WAPI mode\n");
|
|
count = 0;
|
|
ret = -EINVAL;
|
|
goto err_ret;
|
|
}
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
return ret;
|
|
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
static int nvt_set_monitor_channel(struct wiphy *wiphy,
|
|
struct cfg80211_chan_def *chandef)
|
|
{
|
|
struct _nvt_cfg80211 *cfg = (struct _nvt_cfg80211 *)wiphy_priv(wiphy);
|
|
struct _nvt_adapter *nvt_adapter = cfg->nvt_adapter;
|
|
int channel;
|
|
u8 *ch_param = NULL;
|
|
u8 ch_index = 0;
|
|
u8 scan_type = PASSIVE_SCAN;
|
|
u8 scan_time = 10;
|
|
u8 list_len = 0;
|
|
s32 ret = 0;
|
|
u8 site_survey = 1;
|
|
u8 user_scan = 1;
|
|
u8 start_op = 0;
|
|
u8 scan_filter = SCAN_NO_ADHOC | SCAN_SAVE_WITHOUTRSSI | SCAN_CURRENTCH;
|
|
struct _nvt_probe_ssid ssid_list[MAX_PROBED_SSIDS];
|
|
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
if (chandef->chan == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((cfg80211_get_chandef_type(chandef) == NL80211_CHAN_HT40MINUS) ||
|
|
(cfg80211_get_chandef_type(chandef) == NL80211_CHAN_HT40PLUS)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (chandef->chan->band != IEEE80211_BAND_2GHZ) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
|
|
channel = ieee80211_frequency_to_channel(chandef->chan->center_freq);
|
|
#else
|
|
channel = ieee80211_freq_to_dsss_chan(chandef->chan->center_freq);
|
|
#endif
|
|
|
|
memset(ssid_list, 0x0, sizeof(struct _nvt_probe_ssid)*MAX_PROBED_SSIDS);
|
|
|
|
if ((channel > 0) && (channel <= 14)) {
|
|
ch_param = kzalloc(1*2+1, GFP_KERNEL);
|
|
if (ch_param == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
ch_param[ch_index++] = 1;
|
|
ch_param[ch_index++] = IEEE80211_BAND_2GHZ;
|
|
ch_param[ch_index++] = channel;
|
|
|
|
ret = nvt_icfg_lock(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_reset(nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_USER_PREF_CHANNEL, (u8 *)&channel, 2);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set scan_type */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_TYPE, (u8 *)&scan_type, sizeof(scan_type));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set scan filter */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_FILTER, (u8 *)&scan_filter,
|
|
sizeof(scan_filter));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_PASSIVE_SCAN_TIME, (u8 *)&scan_time,
|
|
sizeof(scan_time));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_MULTICH, (u8 *)ch_param, 1*2+1);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set enable site survey */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SITE_SURVEY, (u8 *)&site_survey, sizeof(u8));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set ssid list */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_SCAN_MULTISSID, (u8 *)ssid_list, list_len);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set user scan */
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_START_SCAN_REQ, (u8 *)&user_scan,
|
|
sizeof(user_scan));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
/* set reset mode */
|
|
start_op = DO_RESTART;
|
|
ret = nvt_icfg_add(nvt_adapter->nvt_bus, NVT_ICFG_SET,
|
|
WID_START_OP, (u8 *)&start_op, sizeof(start_op));
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
|
|
ret = nvt_icfg_send_and_get_resp(nvt_adapter->nvt_bus, NULL, 0);
|
|
if (ret < 0) {
|
|
goto err_ret;
|
|
}
|
|
err_ret:
|
|
nvt_icfg_unlock(nvt_adapter->nvt_bus);
|
|
|
|
kfree(ch_param);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
static void nvt_reg_notifier(struct wiphy *wiphy,
|
|
struct regulatory_request *request)
|
|
#else
|
|
static int nvt_reg_notifier(struct wiphy *wiphy,
|
|
struct regulatory_request *request)
|
|
#endif
|
|
{
|
|
struct _nvt_cfg80211 *cfg = (struct _nvt_cfg80211 *)wiphy_priv(wiphy);
|
|
//struct _nvt_adapter *nvt_adapter = cfg->nvt_adapter;
|
|
u8 no_of_triplet = 0;
|
|
struct ieee80211_country_ie_triplet *t;
|
|
u8 no_of_parsed_chan = 0;
|
|
u8 first_chan = 0, next_chan = 0, max_pwr = 0;
|
|
u8 i, flag = 0;
|
|
enum ieee80211_band band;
|
|
struct ieee80211_supported_band *sband;
|
|
struct ieee80211_channel *ch;
|
|
struct _nvt_802_11d_reg *domain_info = &cfg->domain_reg;
|
|
|
|
/* ignore non-ISO3166 country codes */
|
|
for (i = 0; i < sizeof(request->alpha2); i++) {
|
|
if (request->alpha2[i] < 'A' || request->alpha2[i] > 'Z') {
|
|
nvt_dbg(ERROR, "%s\n", __func__);
|
|
nvt_dbg(ERROR, "not a ISO3166 code (0x%02x 0x%02x)\n",
|
|
request->alpha2[0], request->alpha2[1]);
|
|
nvt_dbg(ERROR, "regulatory request pointer=%p)\n",
|
|
request);
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
return;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
nvt_dbg(CFG80211,
|
|
"%s, cfg reg_notify %c%c%s%s initiator %d hint_type %d\n",
|
|
__func__,
|
|
request->alpha2[0], request->alpha2[1],
|
|
request->intersect ? " intersect" : "",
|
|
request->processed ? " processed" : "",
|
|
request->initiator, request->user_reg_hint_type);
|
|
#else
|
|
nvt_dbg(CFG80211,
|
|
"%s, cfg reg_notify %c%c%s%s initiator %d\n",
|
|
__func__,
|
|
request->alpha2[0], request->alpha2[1],
|
|
request->intersect ? " intersect" : "",
|
|
request->processed ? " processed" : "",
|
|
request->initiator);
|
|
#endif
|
|
|
|
switch (request->initiator) {
|
|
case NL80211_REGDOM_SET_BY_DRIVER:
|
|
case NL80211_REGDOM_SET_BY_CORE:
|
|
case NL80211_REGDOM_SET_BY_USER:
|
|
break;
|
|
/* Todo: apply driver specific changes in channel flags based
|
|
on the request initiator if necessary. */
|
|
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
|
|
break;
|
|
}
|
|
|
|
/* Set country code */
|
|
domain_info->country_code[0] = request->alpha2[0];
|
|
domain_info->country_code[1] = request->alpha2[1];
|
|
domain_info->country_code[2] = ' ';
|
|
|
|
band = IEEE80211_BAND_2GHZ;
|
|
sband = wiphy->bands[band];
|
|
|
|
for (i = 0; i < sband->n_channels; i++) {
|
|
ch = &sband->channels[i];
|
|
nvt_dbg(CFG80211, "i=%d, n_channels=%d, flag=%d\n",
|
|
i, sband->n_channels, flag);
|
|
nvt_dbg(CFG80211, "beacon_found=%d, flags=%d, orig_flag=%d\n",
|
|
ch->beacon_found, ch->flags, ch->orig_flags);
|
|
nvt_dbg(CFG80211, "max_pwr=%d, orig_pwr=%d\n",
|
|
ch->max_power, ch->orig_mpwr);
|
|
if (ch->flags & IEEE80211_CHAN_DISABLED)
|
|
continue;
|
|
|
|
if (!flag) {
|
|
flag = 1;
|
|
first_chan = (u32) ch->hw_value;
|
|
next_chan = first_chan;
|
|
max_pwr = ch->max_power;
|
|
no_of_parsed_chan = 1;
|
|
continue;
|
|
}
|
|
|
|
if (ch->hw_value == next_chan + 1 && ch->max_power == max_pwr) {
|
|
next_chan++;
|
|
no_of_parsed_chan++;
|
|
} else {
|
|
t = &domain_info->triplet[no_of_triplet];
|
|
t->chans.first_channel = first_chan;
|
|
t->chans.num_channels = no_of_parsed_chan;
|
|
t->chans.max_power = max_pwr;
|
|
no_of_triplet++;
|
|
first_chan = (u32) ch->hw_value;
|
|
next_chan = first_chan;
|
|
max_pwr = ch->max_power;
|
|
no_of_parsed_chan = 1;
|
|
}
|
|
}
|
|
|
|
if (flag) {
|
|
t = &domain_info->triplet[no_of_triplet];
|
|
t->chans.first_channel = first_chan;
|
|
t->chans.num_channels = no_of_parsed_chan;
|
|
t->chans.max_power = max_pwr;
|
|
no_of_triplet++;
|
|
}
|
|
|
|
domain_info->no_of_triplet = no_of_triplet;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
return;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
static struct cfg80211_ops nvt_cfg80211_ops = {
|
|
.add_key = nvt_cfg80211_add_key,
|
|
.del_key = nvt_cfg80211_del_key,
|
|
.set_default_key = nvt_cfg80211_set_default_key,
|
|
.set_wiphy_params = nvt_cfg80211_set_wiphy_params,
|
|
.get_station = nvt_get_station,
|
|
.dump_station = nvt_dump_station,
|
|
.set_tx_power = nvt_cfg80211_set_txpower,
|
|
.set_bitrate_mask = nvt_cfg80211_set_bitrate,
|
|
.set_power_mgmt = nvt_cfg80211_set_power_mgmt,
|
|
.scan = nvt_cfg80211_scan,
|
|
.connect = nvt_cfg80211_connect,
|
|
.disconnect = nvt_cfg80211_disconnect,
|
|
.mgmt_frame_register = nvt_cfg80211_mgmt_frame_register,
|
|
.mgmt_tx = nvt_cfg80211_mgmt_tx,
|
|
.remain_on_channel = nvt_cfg80211_remain_on_channel,
|
|
.cancel_remain_on_channel = nvt_cfg80211_cancel_remain_on_channel,
|
|
.del_station = nvt_cfg80211_del_station,
|
|
.change_virtual_intf = nvt_cfg80211_change_iface,
|
|
.set_default_mgmt_key = nvt_cfg80211_set_default_mgmt_key,
|
|
#ifdef CONFIG_PM
|
|
.suspend = nvt_cfg80211_suspend,
|
|
.resume = nvt_cfg80211_resume,
|
|
#endif
|
|
.set_rekey_data = nvt_set_gtk_rekey_data,
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
.change_beacon = nvt_cfg80211_change_beacon,
|
|
.start_ap = nvt_cfg80211_start_ap,
|
|
.stop_ap = nvt_cfg80211_stop_ap,
|
|
#else
|
|
.set_channel = nvt_cfg80211_set_channel,
|
|
.add_beacon = nvt_cfg80211_add_beacon,
|
|
.set_beacon = nvt_cfg80211_set_beacon,
|
|
.del_beacon = nvt_cfg80211_del_beacon,
|
|
#endif
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
.set_monitor_channel = nvt_set_monitor_channel,
|
|
#endif
|
|
};
|
|
|
|
static void nvt_scan_timeout(unsigned long data)
|
|
{
|
|
struct _nvt_cfg80211 *cfg = (struct _nvt_cfg80211 *)data;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
if (test_bit(SCAN_PROCESS, &cfg->scan_sts)) {
|
|
schedule_work(&cfg->scan_timeout_work);
|
|
}
|
|
}
|
|
|
|
static void nvt_scan_timeout_handle(struct work_struct *work)
|
|
{
|
|
struct _nvt_cfg80211 *nvt_cfg =
|
|
container_of(work, struct _nvt_cfg80211, scan_timeout_work);
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
nvt_scan_complete(nvt_cfg->nvt_adapter, 1, 0);
|
|
}
|
|
|
|
static void nvt_cfg_priv_init(struct _nvt_cfg80211 *nvt_cfg)
|
|
{
|
|
init_timer(&nvt_cfg->scan_timer);
|
|
nvt_cfg->scan_timer.data = (unsigned long)nvt_cfg;
|
|
nvt_cfg->scan_timer.function = nvt_scan_timeout;
|
|
mutex_init(&nvt_cfg->scan_lock);
|
|
INIT_WORK(&nvt_cfg->scan_timeout_work, nvt_scan_timeout_handle);
|
|
spin_lock_init(&nvt_cfg->bss_list_lock);
|
|
INIT_LIST_HEAD(&nvt_cfg->bss_list);
|
|
}
|
|
|
|
void nvt_abort_scanning(struct _nvt_cfg80211 *cfg)
|
|
{
|
|
if (test_bit(SCAN_PROCESS, &cfg->scan_sts)) {
|
|
nvt_scan_complete(cfg->nvt_adapter, 1, 1);
|
|
}
|
|
}
|
|
|
|
static void nvt_cfg_priv_deinit(struct _nvt_cfg80211 *nvt_cfg)
|
|
{
|
|
nvt_abort_scanning(nvt_cfg);
|
|
}
|
|
|
|
/**
|
|
* nvt_cfg80211_init - register cfg80211 and init relative parameters
|
|
*
|
|
* @nvt_adapter: struct _nvt_adapter
|
|
*
|
|
* Return: return 0 success, return < 0 error
|
|
*/
|
|
s32 nvt_cfg80211_init(struct _nvt_adapter *nvt_adapter)
|
|
{
|
|
struct wiphy *wiphy;
|
|
struct _nvt_cfg80211 *nvt_cfg;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
wiphy = wiphy_new(&nvt_cfg80211_ops, sizeof(struct _nvt_cfg80211));
|
|
|
|
if (!wiphy) {
|
|
nvt_dbg(CFG80211, "allocate wiphy fail\n");
|
|
return -1;
|
|
}
|
|
|
|
wiphy->max_scan_ssids = NVT_NUM_SCAN_MAX;
|
|
wiphy->max_scan_ie_len = NVT_SCAN_VSIE_LEN_MAX;
|
|
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
|
BIT(NL80211_IFTYPE_AP) |
|
|
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
BIT(NL80211_IFTYPE_P2P_GO) |
|
|
BIT(NL80211_IFTYPE_MONITOR);
|
|
|
|
//add for suppoting the iw set bitrate command
|
|
nvt_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff;
|
|
nvt_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff;
|
|
nvt_band_2ghz.ht_cap.mcs.rx_mask[4] = 0x01;
|
|
|
|
wiphy->bands[IEEE80211_BAND_2GHZ] = &nvt_band_2ghz;
|
|
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
|
|
wiphy->cipher_suites = nvt_cipher_suites;
|
|
wiphy->n_cipher_suites = ARRAY_SIZE(nvt_cipher_suites);
|
|
wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
|
|
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME |
|
|
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
|
|
wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
|
|
// WIPHY_FLAG_STRICT_REGULATORY |
|
|
#else
|
|
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
|
|
#endif
|
|
wiphy_apply_custom_regulatory(wiphy, &nvt_regdom_custom);
|
|
wiphy->mgmt_stypes = nvt_txrx_stypes;
|
|
wiphy->max_remain_on_channel_duration = 5000;
|
|
#ifdef CONFIG_PM
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 10, 0)
|
|
wiphy->wowlan = &nvt_wowlan_support;
|
|
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 0)
|
|
wiphy->wowlan = nvt_wowlan_support;
|
|
#endif
|
|
#endif/*CONFIG_PM*/
|
|
/* get the f/w capability */
|
|
nvt_set_firmware_capability(nvt_adapter);
|
|
/* AT vendor command for PTK offload */
|
|
if (nvt_ptk_offload_enable &&
|
|
(nvt_adapter->nvt_fw_cap.mac_cap_1 &
|
|
FW_MAC_CAP_1_INTERNAL_SUPP_CAP)) {
|
|
nvt_dbg(CFG80211, "%s PTK offload enable\n", __func__);
|
|
#ifdef VENDOR_CMD_SUPPORT
|
|
wiphy->vendor_commands = nvt_vendor_commands;
|
|
wiphy->n_vendor_commands = ARRAY_SIZE(nvt_vendor_commands);
|
|
wiphy->extended_capabilities = extended_capabilities;
|
|
wiphy->extended_capabilities_mask = extended_capabilities;
|
|
wiphy->extended_capabilities_len =
|
|
ARRAY_SIZE(extended_capabilities);
|
|
#else
|
|
nvt_dbg(ERROR, "%s VENDOR CMD Compile Option is off!\n",
|
|
__func__);
|
|
#endif
|
|
}
|
|
|
|
wiphy->reg_notifier = nvt_reg_notifier;
|
|
|
|
set_wiphy_dev(wiphy, nvt_adapter->nvt_bus->dev);
|
|
|
|
nvt_cfg = wiphy_priv(wiphy);
|
|
nvt_cfg->wiphy = wiphy;
|
|
nvt_cfg->nvt_adapter = nvt_adapter;
|
|
|
|
if (wiphy_register(wiphy) < 0) {
|
|
wiphy_free(wiphy);
|
|
return -1;
|
|
}
|
|
|
|
nvt_cfg_priv_init(nvt_cfg);
|
|
|
|
nvt_adapter->nvt_cfg80211 = nvt_cfg;
|
|
|
|
//regulatory_hint(wiphy, "TW ");
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_cfg80211_deinit - unregister cfg80211 and free relative parameters
|
|
*
|
|
* @nvt_adapter: struct _nvt_adapter
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_cfg80211_deinit(struct _nvt_adapter *nvt_adapter)
|
|
{
|
|
struct _nvt_cfg80211 *nvt_cfg = nvt_adapter->nvt_cfg80211;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
nvt_cfg_priv_deinit(nvt_cfg);
|
|
wiphy_unregister(nvt_cfg->wiphy);
|
|
wiphy_free(nvt_cfg->wiphy);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_scan_result - scan bss list and scan done evnet handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success, return < 0 error
|
|
*/
|
|
s32 nvt_scan_result(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_cfg80211 *cfg = nvt_adapter->nvt_cfg80211;
|
|
struct _nvt_bss_info *bss = NULL;
|
|
s32 ret = 0;
|
|
u32 size = 0;
|
|
u32 ie_len = 0;
|
|
u32 ie_offset = 0;
|
|
u16 wid = data[0]+(data[1] << 8);
|
|
u16 reason = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
|
u8 *buf = NULL;
|
|
ulong flags = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s, wid=0x%x\n", __func__, wid);
|
|
|
|
if (!test_bit(SCAN_PROCESS, &cfg->scan_sts)) {
|
|
nvt_dbg(CFG80211, "no scan processing\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
if (wid == WID_EVENT_BSS_INFO) {
|
|
if ((!cfg->scan_request) &&
|
|
(!test_bit(NVT_IF_CONNECTING, &nvt_if->state_flags))) {
|
|
nvt_dbg(CFG80211, "no scan request\n");
|
|
return 0;
|
|
}
|
|
|
|
if (cfg->bss_count <= MAX_SCAN_COUNT) {
|
|
size = msg_len-2+sizeof(struct _nvt_bss_info);
|
|
ie_len = data[msg_len-4] | (data[msg_len-3] << 8);
|
|
ie_offset = msg_len-2-ie_len-4;
|
|
|
|
buf = kzalloc(size, GFP_KERNEL);
|
|
if (buf == NULL) {
|
|
nvt_dbg(CFG80211, "allocate bss buffer fail\n");
|
|
return 0;
|
|
}
|
|
|
|
/* fill bss info */
|
|
bss = (struct _nvt_bss_info *)buf;
|
|
bss->length = size;
|
|
memcpy(bss->bssid, &data[18], ETH_ALEN);
|
|
bss->ssid_len = strlen(&data[30]);
|
|
memcpy(bss->ssid, &data[30], bss->ssid_len);
|
|
bss->channel = data[7];
|
|
bss->dot11i_info = data[63];
|
|
bss->rssi = data[9];
|
|
bss->beacon_period = data[11] | (data[10] << 8);
|
|
bss->cap_info = data[13] | (data[12] << 8);
|
|
|
|
bss->ie_offset = ie_offset+sizeof(struct _nvt_bss_info);
|
|
bss->ie_length = ie_len;
|
|
memcpy(&buf[sizeof(struct _nvt_bss_info)], &data[2],
|
|
msg_len-2);
|
|
|
|
spin_lock_irqsave(&cfg->bss_list_lock, flags);
|
|
list_add_tail(&bss->list, &cfg->bss_list);
|
|
cfg->bss_count++;
|
|
spin_unlock_irqrestore(&cfg->bss_list_lock, flags);
|
|
} else {
|
|
nvt_dbg(CFG80211, "bss count[%d] reach max limit\n",
|
|
cfg->bss_count);
|
|
|
|
return 0;
|
|
}
|
|
} else if (wid == WID_EVENT_SCAN_DONE) {
|
|
if (data[2] == SUCCESS) {
|
|
if (test_bit(NVT_IF_CONNECTING, &nvt_if->state_flags)) {
|
|
nvt_scan_complete(cfg->nvt_adapter, 0, 0);
|
|
|
|
if (nvt_find_bss(nvt_if)) {
|
|
if (nvt_connect_action(nvt_if) < 0) {
|
|
nvt_dbg(CFG80211,
|
|
"connect action err\n");
|
|
nvt_bss_connect(nvt_if, reason,
|
|
data);
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211,
|
|
"connect fail, no found bss\n");
|
|
nvt_bss_connect(nvt_if, reason, data);
|
|
}
|
|
} else if (test_bit(SCAN_PROCESS, &cfg->scan_sts)) {
|
|
list_for_each_entry(bss, &cfg->bss_list, list) {
|
|
ret = nvt_update_bss(cfg, bss);
|
|
if (ret < 0) {
|
|
nvt_dbg(CFG80211,
|
|
"update fail, ret=%d\n",
|
|
ret);
|
|
break;
|
|
}
|
|
}
|
|
nvt_scan_complete(cfg->nvt_adapter, 0, 0);
|
|
}
|
|
} else {
|
|
nvt_scan_complete(cfg->nvt_adapter, 1, 0);
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "unknown wid=0x%x\n", wid);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_connect_result - station mode connect result event handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_connect_result(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
struct _nvt_cfg80211 *cfg = nvt_adapter->nvt_cfg80211;
|
|
u16 wid = data[0]+(data[1] << 8);
|
|
u16 reason = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
|
|
|
nvt_dbg(CFG80211, "%s, wid=0x%x\n", __func__, wid);
|
|
|
|
if (wid == WID_EVENT_CONNECT_INFO) {
|
|
if (test_bit(NVT_IF_CONNECTING, &nvt_if->state_flags)) {
|
|
if (test_bit(SCAN_PROCESS, &cfg->scan_sts)) {
|
|
nvt_dbg(CFG80211, "scanning, no connecting\n");
|
|
return 0;
|
|
}
|
|
|
|
if (data[2] == SUCCESS) {
|
|
reason = WLAN_STATUS_SUCCESS;
|
|
nvt_bss_connect(nvt_if, reason, data);
|
|
/* Reset BA handlers */
|
|
nvt_reset_ba_handle(nvt_adapter, 1);
|
|
} else {
|
|
nvt_dbg(CFG80211,
|
|
"connect result fail, status=%d\n",
|
|
data[2]);
|
|
|
|
nvt_bss_connect(nvt_if, reason, data);
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211,
|
|
"sme not connecting state, sme=%lu\n",
|
|
nvt_if->state_flags);
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "unknown wid=0x%x\n", wid);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_connect_status_station - station mode disconnect event handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_connect_status_station(struct _nvt_if *nvt_if,
|
|
u16 msg_len, u8 *data)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
u16 wid = data[0] + (data[1] << 8);
|
|
|
|
if (wid == WID_STATUS) {
|
|
if (test_bit(NVT_IF_CONNECTED, &nvt_if->state_flags) &&
|
|
(data[2] == MAC_DISCONNECTED)) {
|
|
clear_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
if (nvt_if->filter_disconnect == false) {
|
|
nvt_scan_init(nvt_adapter->nvt_cfg80211);
|
|
cfg80211_disconnected(nvt_if->ndev, 0,
|
|
NULL, 0, GFP_KERNEL);
|
|
}
|
|
atomic_inc(&nvt_if->disconnect_cnt);
|
|
if (waitqueue_active(&nvt_if->disconnect_wait)) {
|
|
wake_up(&nvt_if->disconnect_wait);
|
|
}
|
|
/* Reset BA handlers */
|
|
nvt_reset_ba_handle(nvt_adapter, 1);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_enable_status_ap - ap mode enable/disable event handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_enable_status_ap(struct _nvt_if *nvt_if,
|
|
u16 msg_len, u8 *data)
|
|
{
|
|
u16 wid = data[0] + (data[1] << 8);
|
|
|
|
if (wid == WID_STATUS) {
|
|
if (data[2] == MAC_CONNECTED) {
|
|
set_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
nvt_dbg(CFG80211, "Notify AP Enabled\n");
|
|
} else {
|
|
clear_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
nvt_dbg(CFG80211, "Notify AP Disabled\n");
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_connect_status_ap - ap mode connect and disconnect event handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_connect_status_ap(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
u16 wid = data[0] + (data[1] << 8);
|
|
struct station_info sinfo;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
u8 mac_addr[6];
|
|
struct _sta_sts_in_ap *sta_sts_in_ap;
|
|
struct _nvt_priv *nvt_priv = &(nvt_if->nvt_adapter->nvt_priv);
|
|
#endif
|
|
struct _ba_struct_t *ba_ctxt = NULL;
|
|
static s32 generation;
|
|
u8 i = 0;
|
|
u8 idx;
|
|
|
|
if (wid == WID_EVENT_ASSOC_REQ) {
|
|
memset(&sinfo, 0, sizeof(sinfo));
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
|
|
sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
|
|
#endif
|
|
sinfo.assoc_req_ies = data + 8;
|
|
sinfo.assoc_req_ies_len = msg_len - 8;
|
|
generation++;
|
|
sinfo.generation = generation;
|
|
cfg80211_new_sta(nvt_if->ndev, data + 2, &sinfo, GFP_KERNEL);
|
|
nvt_dbg(CFG80211, "Notify connect STA Info\n");
|
|
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
memcpy(mac_addr, data + 2, ETH_ALEN);
|
|
sta_sts_in_ap = kzalloc(sizeof(struct _sta_sts_in_ap),
|
|
GFP_KERNEL);
|
|
|
|
INIT_LIST_HEAD(&sta_sts_in_ap->list);
|
|
|
|
if (sta_sts_in_ap != NULL) {
|
|
nvt_dbg(CLEARVIEW, "%s: Append into sta("
|
|
"%02x:%02x:%02x:%02x:%02x:%02x) list,%p size:%d\n",
|
|
__func__, MAC2STR(mac_addr),
|
|
sta_sts_in_ap, sizeof(struct _sta_sts_in_ap));
|
|
|
|
memset(sta_sts_in_ap, 0, sizeof(struct _sta_sts_in_ap));
|
|
memcpy(sta_sts_in_ap->mac_addr, mac_addr, ETH_ALEN);
|
|
|
|
list_add_tail(&sta_sts_in_ap->list,
|
|
&nvt_priv->sta_list_in_ap);
|
|
|
|
// debug
|
|
list_for_each_entry(sta_sts_in_ap, &nvt_priv->sta_list_in_ap,
|
|
list) {
|
|
nvt_dbg(CLEARVIEW, "element mac:"
|
|
"%02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
MAC2STR(sta_sts_in_ap->mac_addr));
|
|
}
|
|
|
|
} else {
|
|
nvt_dbg(CLEARVIEW, "%s: sta_sts_in_ap can't be created\n",
|
|
__func__);
|
|
}
|
|
#endif
|
|
|
|
/* Reset BA handlers */
|
|
for (i = 0; i < ASSOC_NUM; i++) {
|
|
idx = i * NUM_TIDS;
|
|
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
|
|
if (memcmp(ba_ctxt->dst_addr,
|
|
data + 2, ETH_ALEN) == 0) {
|
|
nvt_reset_ba_handle(nvt_adapter, i + 1);
|
|
}
|
|
}
|
|
} else if (wid == WID_STA_JOIN_INFO) {
|
|
cfg80211_del_sta(nvt_if->ndev, data + 3, GFP_KERNEL);
|
|
nvt_dbg(CFG80211, "Notify Disconnect STA Info\n");
|
|
/* Reset BA handlers */
|
|
for (i = 0; i < ASSOC_NUM; i++) {
|
|
idx = i * NUM_TIDS;
|
|
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
|
|
if (memcmp(ba_ctxt->dst_addr,
|
|
data + 3, ETH_ALEN) == 0) {
|
|
nvt_reset_ba_handle(nvt_adapter, i + 1);
|
|
}
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "unknown wid=0x%x\n", wid);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_micfail_status - mic fail event handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_micfail_status(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
enum nl80211_key_type key_type;
|
|
nvt_dbg(CFG80211, "Notify_MIC_Failure\n");
|
|
|
|
if (data[8] == 0) {
|
|
key_type = NL80211_KEYTYPE_GROUP;
|
|
} else {
|
|
key_type = NL80211_KEYTYPE_PAIRWISE;
|
|
}
|
|
|
|
cfg80211_michael_mic_failure(nvt_if->ndev,
|
|
&data[2], key_type, -1, NULL, GFP_KERNEL);
|
|
return 0;
|
|
|
|
}
|
|
|
|
/**
|
|
* nvt_rx_mgmt_probereq - receive probe request frame from Firmware
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: zero - success.
|
|
* negative - failure.
|
|
*/
|
|
s32 nvt_rx_mgmt_probereq(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
u8 *mgmt_frame;
|
|
u32 mgmt_frame_len;
|
|
s32 freq;
|
|
u16 mgmt_type;
|
|
|
|
nvt_dbg(P2P, "%s", __func__);
|
|
|
|
/* Check if wpa_supplicant has registered for this frame */
|
|
nvt_dbg(P2P, "mgmt_rx_reg=%04x\n", nvt_if->mgmt_rx_reg);
|
|
mgmt_type = (IEEE80211_STYPE_PROBE_REQ & IEEE80211_FCTL_STYPE) >> 4;
|
|
if ((nvt_if->mgmt_rx_reg & BIT(mgmt_type)) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
mgmt_frame = (u8 *)(data + 2);
|
|
/* WID_Len + Freq_Len = 2 + 1 = 3 */
|
|
mgmt_frame_len = msg_len - 3;
|
|
freq = ieee80211_channel_to_frequency(data[msg_len - 1],
|
|
(data[msg_len - 1] <= 14) ?
|
|
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ);
|
|
nvt_dbg(P2P, "freq = %d\n", freq);
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 0)
|
|
cfg80211_rx_mgmt(&nvt_if->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0);
|
|
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3, 10, 0)
|
|
cfg80211_rx_mgmt(&nvt_if->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0,
|
|
GFP_ATOMIC);
|
|
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
|
|
cfg80211_rx_mgmt(&nvt_if->wdev, freq, 0, mgmt_frame, mgmt_frame_len,
|
|
GFP_ATOMIC);
|
|
#else
|
|
cfg80211_rx_mgmt(nvt_if->ndev, freq, mgmt_frame, mgmt_frame_len,
|
|
GFP_ATOMIC);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_rx_action_frame - receive action frame from Firmware
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: zero - success.
|
|
* negative - failure.
|
|
*/
|
|
s32 nvt_rx_action_frame(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
u8 *mgmt_frame;
|
|
u32 mgmt_frame_len;
|
|
s32 freq;
|
|
u16 mgmt_type;
|
|
s32 ie_offset;
|
|
s32 ie_len;
|
|
|
|
nvt_dbg(P2P, "%s", __func__);
|
|
|
|
/* Check if wpa_supplicant has registered for this frame */
|
|
nvt_dbg(P2P, "mgmt_rx_reg=%04x\n", nvt_if->mgmt_rx_reg);
|
|
mgmt_type = (IEEE80211_STYPE_ACTION & IEEE80211_FCTL_STYPE) >> 4;
|
|
if ((nvt_if->mgmt_rx_reg & BIT(mgmt_type)) == 0) {
|
|
return 0;
|
|
}
|
|
|
|
mgmt_frame = (u8 *)(data + 2);
|
|
/* WID_Len + Freq_Len = 2 + 1 = 3 */
|
|
mgmt_frame_len = msg_len - 3;
|
|
freq = ieee80211_channel_to_frequency(data[msg_len - 1],
|
|
(data[msg_len - 1] <= 14) ?
|
|
IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ);
|
|
nvt_dbg(P2P, "freq = %d\n", freq);
|
|
|
|
ie_offset = IEEE80211_MGMT_HDR_LEN;
|
|
ie_len = cpu_to_le16(mgmt_frame_len - ie_offset);
|
|
nvt_dbg_p2p(false, &mgmt_frame[ie_offset], ie_len);
|
|
|
|
//JERRY
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 0, 0)
|
|
cfg80211_rx_mgmt(&nvt_if->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0);
|
|
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3, 10, 0)
|
|
cfg80211_rx_mgmt(&nvt_if->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0,
|
|
GFP_ATOMIC);
|
|
#elif LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)
|
|
cfg80211_rx_mgmt(&nvt_if->wdev, freq, 0, mgmt_frame, mgmt_frame_len,
|
|
GFP_ATOMIC);
|
|
#else
|
|
cfg80211_rx_mgmt(nvt_if->ndev, freq, mgmt_frame, mgmt_frame_len,
|
|
GFP_ATOMIC);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u8 nvt_set_country_info_elem(u8 *data,
|
|
struct _nvt_802_11d_reg *domain_info)
|
|
{
|
|
struct ieee80211_country_ie_triplet *t;
|
|
u8 ch_idx = 0;
|
|
u8 index_len = 0;
|
|
u8 ie_len = 0;
|
|
u8 index = 0;
|
|
|
|
/* ignore non-ISO3166 country codes */
|
|
if (domain_info->country_code[0] < 'A' ||
|
|
domain_info->country_code[0] > 'Z' ||
|
|
domain_info->country_code[1] < 'A' ||
|
|
domain_info->country_code[1] > 'Z') {
|
|
nvt_dbg(ERROR, "%s, not a ISO3166 code (0x%02x 0x%02x)\n",
|
|
__func__, domain_info->country_code[0],
|
|
domain_info->country_code[1]);
|
|
return 0;
|
|
}
|
|
|
|
/* Country Element ID */
|
|
data[index++] = ICOUNTRY;
|
|
|
|
/* Save index for length field */
|
|
index_len = index++;
|
|
|
|
data[index++] = domain_info->country_code[0];
|
|
data[index++] = domain_info->country_code[1];
|
|
data[index++] = domain_info->country_code[2];
|
|
|
|
for (ch_idx = 0; ch_idx < domain_info->no_of_triplet; ch_idx++) {
|
|
t = &domain_info->triplet[ch_idx];
|
|
data[index++] = t->chans.first_channel;
|
|
data[index++] = t->chans.num_channels;
|
|
data[index++] = t->chans.max_power;
|
|
}
|
|
|
|
/* Update the length field */
|
|
ie_len = (index - 1 - index_len);
|
|
|
|
/* If IE length is not divisible by 2 then add 1byte zero Padding */
|
|
if ((ie_len % 2) == 1) {
|
|
data[index] = 0;
|
|
ie_len += 1;
|
|
}
|
|
|
|
data[index_len] = ie_len;
|
|
return IE_HDR_LEN + ie_len;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
s32 nvt_cfg80211_bcn_params(struct _nvt_if *nvt_if,
|
|
struct _BSS_CONFIG_INFO *bss_config,
|
|
struct cfg80211_ap_settings *settings)
|
|
#else
|
|
s32 nvt_cfg80211_bcn_params(struct _nvt_if *nvt_if,
|
|
struct _BSS_CONFIG_INFO *bss_config,
|
|
struct beacon_parameters *settings)
|
|
#endif
|
|
{
|
|
s32 err = 0;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_cfg80211 *cfg = nvt_if->nvt_adapter->nvt_cfg80211;
|
|
struct _nvt_802_11d_reg *domain_info = &cfg->domain_reg;
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
int beacon_interval = settings->beacon_interval;
|
|
struct cfg80211_beacon_data *bcon = &settings->beacon;
|
|
const u8 *proberesp_ies = bcon->proberesp_ies;
|
|
size_t proberesp_ies_len = bcon->proberesp_ies_len;
|
|
const u8 *beacon_ies = bcon->beacon_ies;
|
|
size_t beacon_ies_len = bcon->beacon_ies_len;
|
|
const u8 *assocresp_ies = bcon->assocresp_ies;
|
|
size_t assocresp_ies_len = bcon->assocresp_ies_len;
|
|
#else
|
|
int beacon_interval = settings->interval;
|
|
const u8 *proberesp_ies = settings->proberesp_ies;
|
|
size_t proberesp_ies_len = settings->proberesp_ies_len;
|
|
const u8 *beacon_ies = settings->beacon_ies;
|
|
size_t beacon_ies_len = settings->beacon_ies_len;
|
|
const u8 *assocresp_ies = settings->assocresp_ies;
|
|
size_t assocresp_ies_len = settings->assocresp_ies_len;
|
|
#endif
|
|
u8 *bcn_buf = NULL;
|
|
u8 *probe_rsp_buf = NULL;
|
|
u8 reg_buf[NVT_MAX_TRIPLET_802_11D * 3 + 3] = {0};
|
|
u8 reg_len = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
if ((settings->ssid != NULL) && (settings->ssid_len > 0) &&
|
|
(settings->ssid_len <= 32)) {
|
|
memset(bss_config->ssid_le.SSID, 0, 32);
|
|
memcpy(bss_config->ssid_le.SSID, settings->ssid,
|
|
settings->ssid_len);
|
|
bss_config->ssid_le.SSID_len =
|
|
cpu_to_le32((u32)settings->ssid_len);
|
|
} else {
|
|
nvt_dbg(ERROR, "[ssid]=%s,[len]=%zu\n", settings->ssid,
|
|
settings->ssid_len);
|
|
err = -EINVAL;
|
|
}
|
|
|
|
switch (settings->hidden_ssid) {
|
|
case NL80211_HIDDEN_SSID_NOT_IN_USE:
|
|
bss_config->hidden_ssid = 0;
|
|
break;
|
|
case NL80211_HIDDEN_SSID_ZERO_LEN:
|
|
bss_config->hidden_ssid = 1;
|
|
break;
|
|
case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
|
|
nvt_dbg(ERROR,
|
|
"not support hiddle_ssid type [%d]\n",
|
|
settings->hidden_ssid);
|
|
err = -EINVAL;
|
|
break;
|
|
default:
|
|
bss_config->hidden_ssid = 1;
|
|
break;
|
|
}
|
|
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
if (nvt_icfg_lock(nvt_bus) < 0 || nvt_icfg_reset(nvt_bus) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set beacon interval*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_BEACON_INTERVAL,
|
|
(u8 *)&beacon_interval, 2) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set dtim period*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_DTIM_PERIOD,
|
|
(u8 *)&settings->dtim_period, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set ssid*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_SSID,
|
|
(u8 *)&bss_config->ssid_le.SSID, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set hidden ssid*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_BCAST_SSID,
|
|
(u8 *)&bss_config->hidden_ssid, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
//probe rsp ies
|
|
// filter p2p IE
|
|
err = nvt_filter_p2p_ie(&proberesp_ies, &proberesp_ies_len);
|
|
if (err < 0)
|
|
goto exit;
|
|
// filter wfd IE
|
|
err = nvt_filter_wfd_ie(&proberesp_ies, &proberesp_ies_len);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
nvt_dbg(CFG80211, "%s, country_code: %c%c\n", __func__,
|
|
domain_info->country_code[0], domain_info->country_code[1]);
|
|
|
|
if (nvt_is_an_alpha2(domain_info->country_code)) {
|
|
nvt_dbg(CFG80211, "construct country element\n");
|
|
reg_len = nvt_set_country_info_elem(reg_buf, domain_info);
|
|
}
|
|
|
|
if (((beacon_ies_len > 0) && (beacon_ies != NULL)) &&
|
|
(reg_len != 0)) {
|
|
bss_config->vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "add country info. in extra beacon ies\n");
|
|
bcn_buf = kzalloc(reg_len + beacon_ies_len, GFP_KERNEL);
|
|
if (bcn_buf == NULL) {
|
|
nvt_dbg(CFG80211, "alloc bcn buf fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(&bcn_buf[0], ®_buf[0], reg_len);
|
|
memcpy(&bcn_buf[reg_len], beacon_ies,
|
|
beacon_ies_len);
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)bcn_buf, reg_len + beacon_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
kfree(bcn_buf);
|
|
return -EINVAL;
|
|
}
|
|
kfree(bcn_buf);
|
|
} else if ((beacon_ies_len > 0) && (beacon_ies != NULL)) {
|
|
bss_config->vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)beacon_ies, beacon_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else if (reg_len != 0) {
|
|
nvt_dbg(CFG80211, "only country info. in extra beacon ies\n");
|
|
bss_config->vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)reg_buf, reg_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "no ie in extra beacon ies\n");
|
|
}
|
|
|
|
if (((proberesp_ies_len > 0) && (proberesp_ies != NULL)) &&
|
|
(reg_len != 0)) {
|
|
bss_config->vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "add country info. in extra prob_rsp ies\n");
|
|
probe_rsp_buf = kzalloc(reg_len + proberesp_ies_len,
|
|
GFP_KERNEL);
|
|
if (probe_rsp_buf == NULL) {
|
|
nvt_dbg(CFG80211, "alloc prob_rsp buf fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(&probe_rsp_buf[0], ®_buf[0], reg_len);
|
|
memcpy(&probe_rsp_buf[reg_len], proberesp_ies,
|
|
proberesp_ies_len);
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)probe_rsp_buf,
|
|
reg_len + proberesp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
kfree(probe_rsp_buf);
|
|
return -EINVAL;
|
|
}
|
|
kfree(probe_rsp_buf);
|
|
} else if ((proberesp_ies_len > 0) && (proberesp_ies != NULL)) {
|
|
bss_config->vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)proberesp_ies, proberesp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else if (reg_len != 0) {
|
|
bss_config->vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "only country info. in extra prob_rsp ies\n");
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)reg_buf, reg_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "no ie in extra prob_rsp ies\n");
|
|
}
|
|
|
|
if ((assocresp_ies_len > 0) && (assocresp_ies != NULL)) {
|
|
bss_config->vsie_enable |= 0x08;
|
|
wconf->ctrl_flag |= ASSOC_RESP_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_ASSOCRSP,
|
|
(u8 *)assocresp_ies, assocresp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
/*set hidden ssid*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_FRAME,
|
|
(u8 *)&bss_config->vsie_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
exit:
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
} else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
s32 nvt_cfg80211_ap_ies(struct _nvt_if *nvt_if,
|
|
struct _BSS_CONFIG_INFO *bss_config, struct cfg80211_beacon_data *info,
|
|
struct cfg80211_ap_settings *settings)
|
|
#else
|
|
s32 nvt_cfg80211_ap_ies(struct _nvt_if *nvt_if,
|
|
struct _BSS_CONFIG_INFO *bss_config,
|
|
struct beacon_parameters *settings)
|
|
#endif
|
|
{
|
|
u8 *wps_ie, *p2p_ie, *rsn_ie, *wpa_ie;
|
|
u32 i;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
|
|
u8 *tail = settings->tail;
|
|
int tail_len = settings->tail_len;
|
|
#else
|
|
const u8 *tail = info->tail;
|
|
int tail_len = info->tail_len;
|
|
#endif
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
if (!settings->privacy) {
|
|
bss_config->security_mode &= ~WID_11I_MODE_Security_ON;
|
|
} else {
|
|
switch (settings->auth_type) {
|
|
case NL80211_AUTHTYPE_OPEN_SYSTEM:
|
|
bss_config->auth_mode = WID_AUTH_OPEN_PSK;
|
|
break;
|
|
case NL80211_AUTHTYPE_SHARED_KEY:
|
|
bss_config->auth_mode = WID_AUTH_SHARED_KEY;
|
|
break;
|
|
case NL80211_AUTHTYPE_NETWORK_EAP:
|
|
bss_config->auth_mode = WID_AUTH_802_1X;
|
|
break;
|
|
case NL80211_AUTHTYPE_AUTOMATIC:
|
|
nvt_dbg(CFG80211, "NL80211_AUTHTYPE_AUTOMATIC\n");
|
|
bss_config->auth_mode = WID_AUTH_OPEN_PSK
|
|
| WID_AUTH_SHARED_KEY;
|
|
break;
|
|
default:
|
|
bss_config->auth_mode = WID_AUTH_OPEN_PSK;
|
|
break;
|
|
}
|
|
bss_config->security_mode |= WID_11I_MODE_Security_ON;
|
|
|
|
if (settings->crypto.n_ciphers_pairwise) {
|
|
switch (settings->crypto.ciphers_pairwise[0]) {
|
|
case WLAN_CIPHER_SUITE_USE_GROUP:
|
|
bss_config->security_mode
|
|
&= ~WID_11I_MODE_Security_ON;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
bss_config->security_mode |= WID_11I_MODE_WEP40;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
bss_config->security_mode
|
|
|= WID_11I_MODE_WEP104;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
if ((settings->crypto.wpa_versions &
|
|
NL80211_WPA_VERSION_1) ||
|
|
(settings->crypto.wpa_versions &
|
|
NL80211_WPA_VERSION_2))
|
|
bss_config->security_mode
|
|
|= WID_11I_MODE_TKIP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
if ((settings->crypto.wpa_versions &
|
|
NL80211_WPA_VERSION_1) ||
|
|
(settings->crypto.wpa_versions &
|
|
NL80211_WPA_VERSION_2))
|
|
bss_config->security_mode
|
|
|= WID_11I_MODE_CCMP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
bss_config->security_mode
|
|
|= WID_11I_SECURITY_ON;
|
|
bss_config->ext_security_mode
|
|
|= (WID_EXT_11I_WAPI |
|
|
WID_EXT_11I_SMS4);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
switch (settings->crypto.cipher_group) {
|
|
case WLAN_CIPHER_SUITE_USE_GROUP:
|
|
bss_config->security_mode &= ~WID_11I_MODE_Security_ON;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
bss_config->security_mode |= WID_11I_MODE_WEP40;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
bss_config->security_mode |= WID_11I_MODE_WEP104;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
bss_config->security_mode |= WID_11I_MODE_TKIP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
bss_config->security_mode |= WID_11I_MODE_CCMP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
bss_config->security_mode |= WID_11I_SECURITY_ON;
|
|
bss_config->ext_security_mode |= (WID_EXT_11I_WAPI |
|
|
WID_EXT_11I_SMS4);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < settings->crypto.n_akm_suites; i++) {
|
|
switch (settings->crypto.akm_suites[i]) {
|
|
case WLAN_AKM_SUITE_8021X:
|
|
return -EINVAL;
|
|
break;
|
|
case WLAN_AKM_SUITE_PSK:
|
|
if (settings->crypto.wpa_versions &
|
|
NL80211_WPA_VERSION_1)
|
|
bss_config->wpa_proto = PROTO_WPA1;
|
|
if (settings->crypto.wpa_versions &
|
|
NL80211_WPA_VERSION_2)
|
|
bss_config->wpa_proto = PROTO_WPA2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < settings->crypto.n_ciphers_pairwise; i++) {
|
|
switch (settings->crypto.ciphers_pairwise[i]) {
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
bss_config->pairwise_cipher = CIPHER_TKIP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
bss_config->pairwise_cipher = CIPHER_CCMP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
bss_config->pairwise_cipher = CIPHER_SMS4;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (bss_config->ext_security_mode & WID_EXT_11I_WAPI) {
|
|
bss_config->auth_mode |= WID_EXT_11I_WAPI;
|
|
}
|
|
}
|
|
|
|
if (tail && tail_len) {
|
|
|
|
/* find the RSN_IE */
|
|
rsn_ie = (u8 *)cfg80211_find_ie(WLAN_EID_RSN, tail, tail_len);
|
|
|
|
/* find the WPA_IE */
|
|
wpa_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPA, tail, tail_len);
|
|
|
|
if ((wpa_ie != NULL || rsn_ie != NULL)) {
|
|
if (wpa_ie != NULL) {
|
|
/* WPA IE */
|
|
bss_config->security_mode |= WID_11I_MODE_WPA;
|
|
}
|
|
if (rsn_ie != NULL) {
|
|
/* RSN IE */
|
|
bss_config->security_mode |= WID_11I_MODE_RSN;
|
|
}
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
|
|
if (rsn_ie != NULL) {
|
|
bss_config->pmf_enable = nvt_pmf_check(rsn_ie,
|
|
rsn_ie+1);
|
|
}
|
|
#endif
|
|
/* find the WPS IE */
|
|
wps_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPS, tail, tail_len);
|
|
|
|
if (wps_ie != NULL)
|
|
bss_config->wps_enable = 1;
|
|
else
|
|
bss_config->wps_enable = 0;
|
|
|
|
/* find the P2P IE */
|
|
p2p_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_WFA,
|
|
WLAN_OUI_TYPE_WFA_P2P, tail, tail_len);
|
|
|
|
if (p2p_ie != NULL) {
|
|
bss_config->p2p_enable = 1;
|
|
bss_config->op_11g_mode = OP_11G_HP_MODE;
|
|
} else {
|
|
bss_config->p2p_enable = 0;
|
|
if (cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, tail
|
|
, tail_len) != NULL)
|
|
bss_config->op_11g_mode = OP_11G_COMP_MODE_1;
|
|
else
|
|
bss_config->op_11g_mode = OP_11B_ONLY_MODE;
|
|
}
|
|
}
|
|
|
|
if (nvt_icfg_lock(nvt_bus) < 0 || nvt_icfg_reset(nvt_bus) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set op mode*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_11G_OPERATING_MODE,
|
|
(u8 *)&bss_config->op_11g_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set security*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_11I_MODE,
|
|
(u8 *)&bss_config->security_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_EXT_MODE_11I,
|
|
(u8 *)&bss_config->ext_security_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_AUTH_TYPE,
|
|
(u8 *)&bss_config->auth_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set pmf capable*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_PMF_ENABLE,
|
|
(u8 *)&bss_config->pmf_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*set WPS_enable*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_WPS_REGISTRATION_START,
|
|
(u8 *)&bss_config->wps_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
/*set P2P_enable*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_P2P_ENABLE,
|
|
(u8 *)&bss_config->p2p_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
} else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
s32 nvt_cfg80211_set_chan_ie(struct wiphy *wiphy,
|
|
struct _nvt_if *nvt_if ,
|
|
struct _BSS_CONFIG_INFO *bss_config,
|
|
struct cfg80211_ap_settings *settings)
|
|
#else
|
|
s32 nvt_cfg80211_set_chan_ie(struct wiphy *wiphy,
|
|
struct _nvt_if *nvt_if ,
|
|
struct _BSS_CONFIG_INFO *bss_config,
|
|
struct beacon_parameters *beacon_data)
|
|
#endif
|
|
{
|
|
s32 err = 0;
|
|
u8 *ht_ie;
|
|
u8 *ssid_ie = 0;
|
|
u8 *capability_info = 0;
|
|
u8 ba_enable = 1;
|
|
struct ieee80211_ht_cap ht_cap;
|
|
const u8 *vendor_ie;
|
|
struct ieee_types_header *wmm_ie = NULL;
|
|
struct Nova_WID_WMM_STA_AC_PARAMS Nova_WMM_AC_PARAMS;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
struct _nvt_wlan_conf_ap *wconf_ap = &nvt_if->nvt_wconf_ap;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
enum ieee80211_band band;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
bss_config->channel =
|
|
ieee80211_frequency_to_channel(settings->chandef.chan->center_freq);
|
|
#else
|
|
bss_config->channel =
|
|
ieee80211_frequency_to_channel(nvt_if->
|
|
ch_info.chan->center_freq);
|
|
#endif
|
|
for (band = 0; band < IEEE80211_NUM_BANDS; band++)
|
|
err = nvt_handle_reg_channel(nvt_adapter,
|
|
wiphy, wiphy->bands[band]);
|
|
if (err < 0) {
|
|
return err;
|
|
}
|
|
|
|
if (nvt_icfg_lock(nvt_bus) < 0 || nvt_icfg_reset(nvt_bus) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
bss_config->channel_type =
|
|
cfg80211_get_chandef_type(&settings->chandef);
|
|
#else
|
|
bss_config->channel_type = nvt_if->ch_info.channel_type;
|
|
#endif
|
|
if (bss_config->channel_type == NL80211_CHAN_NO_HT) {
|
|
if (bss_config->channel > 14)
|
|
bss_config->channel |= BAND_5G;
|
|
else
|
|
bss_config->channel |= BAND_2G;
|
|
|
|
bss_config->bw40_enable = 0;
|
|
bss_config->op_11n_enable = 0;
|
|
/*if (bss_config->op_11g_mode == 0) {
|
|
bss_config->preamble = PREAMBLE_LONG;
|
|
bss_config->slot = 0;
|
|
} else {
|
|
bss_config->preamble = PREAMBLE_AUTO;
|
|
bss_config->slot = 1;
|
|
}*/
|
|
} else {
|
|
if (bss_config->channel > 14)
|
|
bss_config->channel |= BAND_5G;
|
|
else
|
|
bss_config->channel |= BAND_2G;
|
|
|
|
if (bss_config->channel_type == NL80211_CHAN_HT20) {
|
|
bss_config->bw40_enable = 0;
|
|
} else if (bss_config->channel_type == NL80211_CHAN_HT40PLUS) {
|
|
bss_config->bw40_enable = 1;
|
|
bss_config->channel |= SCA;
|
|
} else if (bss_config->channel_type == NL80211_CHAN_HT40MINUS) {
|
|
bss_config->bw40_enable = 1;
|
|
bss_config->channel |= SCB;
|
|
} else {
|
|
nvt_dbg(ERROR,
|
|
"chane type error [%x], channel [%d]\n",
|
|
bss_config->channel_type, bss_config->channel);
|
|
err = -EINVAL;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
ht_ie = (u8 *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
|
|
settings->beacon.tail, settings->beacon.tail_len);
|
|
#else
|
|
ht_ie = (u8 *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY,
|
|
beacon_data->tail, beacon_data->tail_len);
|
|
#endif
|
|
if (ht_ie) {
|
|
bss_config->sgi = LGI;
|
|
|
|
memcpy(&ht_cap, ht_ie+2,
|
|
sizeof(struct ieee80211_ht_cap));
|
|
if (ht_cap.cap_info & IEEE80211_HT_CAP_SGI_20) {
|
|
bss_config->sgi = SGI_20;
|
|
}
|
|
if (ht_cap.cap_info & IEEE80211_HT_CAP_SGI_40) {
|
|
bss_config->sgi |= SGI_40;
|
|
}
|
|
switch (ht_cap.cap_info & IEEE80211_HT_CAP_RX_STBC) {
|
|
case NVT_RX_STBC1:
|
|
bss_config->stbc |= (RX_STBC_1 << 1);
|
|
break;
|
|
case NVT_RX_STBC12:
|
|
bss_config->stbc |= (RX_STBC_2 << 1);
|
|
break;
|
|
case NVT_RX_STBC123:
|
|
bss_config->stbc |= (RX_STBC_3 << 1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (ht_cap.cap_info & IEEE80211_HT_CAP_TX_STBC) {
|
|
bss_config->stbc |= TX_STBC;
|
|
}
|
|
if (ht_cap.cap_info & IEEE80211_HT_CAP_GRN_FLD) {
|
|
bss_config->greenfield = 1;
|
|
}
|
|
} else {
|
|
bss_config->sgi = LGI;
|
|
}
|
|
|
|
bss_config->op_11n_enable = 1;
|
|
bss_config->preamble = PREAMBLE_AUTO;
|
|
bss_config->slot = 1;
|
|
}
|
|
/* Check Auto Channel Selection */
|
|
if (wconf_ap->acs_channel != 0xFF) {
|
|
/* apply iwpriv acs_channel value instead of hostapd conf. */
|
|
bss_config->channel =
|
|
(bss_config->channel & 0xFF00) | wconf_ap->acs_channel;
|
|
nvt_dbg(CFG80211, "%s:apply iwpriv ch=%d\n",
|
|
__func__, bss_config->channel);
|
|
nvt_dbg(CFG80211, "%s:cs_mask=0x%x\n", __func__,
|
|
wconf_ap->acs_mask);
|
|
} else {
|
|
/* apply hostapd conf.'s channel */
|
|
nvt_dbg(CFG80211, "%s:apply hostapd conf.'s ch=%d\n",
|
|
__func__, bss_config->channel);
|
|
}
|
|
// get wmm parameter
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WMM, settings->beacon.tail,
|
|
settings->beacon.tail_len);
|
|
#else
|
|
vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WMM, beacon_data->tail,
|
|
beacon_data->tail_len);
|
|
#endif
|
|
if (vendor_ie) {
|
|
bss_config->qos_enable = 1;
|
|
wmm_ie = (struct ieee_types_header *)vendor_ie;
|
|
|
|
memcpy(&bss_config->wmm_info, wmm_ie + 1,
|
|
sizeof(struct wmm_info));
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BE.TXOP =
|
|
bss_config->wmm_info.ac_params[0].tx_op_limit;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BE.TXOP =
|
|
Nova_WMM_AC_PARAMS.AC_BE.TXOP << 5;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BE.CWmax =
|
|
(bss_config->wmm_info.ac_params[0].ecw_bitmap & 0xF0) >> 4;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BE.CWmin =
|
|
bss_config->wmm_info.ac_params[0].ecw_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BE.AIFSN =
|
|
bss_config->wmm_info.ac_params[0].aci_aifsn_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BE.ACM =
|
|
(bss_config->wmm_info.ac_params[0].aci_aifsn_bitmap &
|
|
0x10) >> 4;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BK.TXOP =
|
|
bss_config->wmm_info.ac_params[1].tx_op_limit;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BK.TXOP =
|
|
Nova_WMM_AC_PARAMS.AC_BK.TXOP << 5;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BK.CWmax =
|
|
(bss_config->wmm_info.ac_params[1].ecw_bitmap & 0xF0) >> 4;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BK.CWmin =
|
|
bss_config->wmm_info.ac_params[1].ecw_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BK.AIFSN =
|
|
bss_config->wmm_info.ac_params[1].aci_aifsn_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_BK.ACM =
|
|
(bss_config->wmm_info.ac_params[1].aci_aifsn_bitmap &
|
|
0x10) >> 4;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VI.TXOP =
|
|
bss_config->wmm_info.ac_params[2].tx_op_limit;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VI.TXOP =
|
|
Nova_WMM_AC_PARAMS.AC_VI.TXOP << 5;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VI.CWmax =
|
|
(bss_config->wmm_info.ac_params[2].ecw_bitmap & 0xF0) >> 4;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VI.CWmin =
|
|
bss_config->wmm_info.ac_params[2].ecw_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VI.AIFSN =
|
|
bss_config->wmm_info.ac_params[2].aci_aifsn_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VI.ACM =
|
|
(bss_config->wmm_info.ac_params[2].aci_aifsn_bitmap &
|
|
0x10) >> 4;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VO.TXOP =
|
|
bss_config->wmm_info.ac_params[3].tx_op_limit;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VO.TXOP =
|
|
Nova_WMM_AC_PARAMS.AC_VO.TXOP << 5;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VO.CWmax =
|
|
(bss_config->wmm_info.ac_params[3].ecw_bitmap & 0xF0) >> 4;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VO.CWmin =
|
|
bss_config->wmm_info.ac_params[3].ecw_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VO.AIFSN =
|
|
bss_config->wmm_info.ac_params[3].aci_aifsn_bitmap & 0x0F;
|
|
|
|
Nova_WMM_AC_PARAMS.AC_VO.ACM =
|
|
(bss_config->wmm_info.ac_params[3].aci_aifsn_bitmap &
|
|
0x10) >> 4;
|
|
|
|
} else {
|
|
bss_config->qos_enable = 0;
|
|
}
|
|
|
|
/* find the SSID_IE */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, settings->beacon.head +
|
|
DOT_80211_BEACON_HEADER +
|
|
DOT_80211_WIRELESS_MGMT_FIX_HEADER,
|
|
settings->beacon.head_len);
|
|
#else
|
|
ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, beacon_data->head +
|
|
DOT_80211_BEACON_HEADER +
|
|
DOT_80211_WIRELESS_MGMT_FIX_HEADER, beacon_data->head_len);
|
|
#endif
|
|
capability_info = ssid_ie - 2;
|
|
|
|
if (capability_info[0] & 0x20) {
|
|
// short preamble bit
|
|
nvt_dbg(CFG80211, "short preamble bit is enable\n");
|
|
bss_config->preamble = PREAMBLE_AUTO;
|
|
} else {
|
|
nvt_dbg(CFG80211, "short preamble bit is disable\n");
|
|
bss_config->preamble = PREAMBLE_LONG;
|
|
}
|
|
if (capability_info[1] & 0x04) {
|
|
// short slot time bit
|
|
nvt_dbg(CFG80211, "short slot time bit is enable\n");
|
|
bss_config->slot = 1;
|
|
} else {
|
|
nvt_dbg(CFG80211, "short slot time bit is disable\n");
|
|
bss_config->slot = 0;
|
|
}
|
|
|
|
#if 0
|
|
if (ba_enable > 3) {
|
|
// user input to enable/disable immediate block ack bit
|
|
nvt_dbg(CFG80211, "User disable immediate block ack\n");
|
|
ba_enable = 0;
|
|
} else {
|
|
nvt_dbg(CFG80211, "immediate block ack is enable\n");
|
|
ba_enable = 1;
|
|
}
|
|
#endif
|
|
|
|
/*set ba*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_11N_IMMEDIATE_BA_ENABLED,
|
|
(u8 *)&ba_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
/*set preamble*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_PREAMBLE,
|
|
(u8 *)&bss_config->preamble, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
/*set shortslot*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_SHORT_SLOT_ALLOWED,
|
|
(u8 *)&bss_config->slot, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
/*set wmm*/
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_QOS_ENABLE,
|
|
(u8 *)&bss_config->qos_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (wmm_ie)
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_WMM_STA_AC_PARAMS,
|
|
(u8 *)&Nova_WMM_AC_PARAMS,
|
|
sizeof(Nova_WMM_AC_PARAMS)) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
//channel and 11n
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_2040_ENABLE,
|
|
(u8 *)&bss_config->bw40_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_11N_ENABLE,
|
|
(u8 *)&bss_config->op_11n_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
//channel
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_USER_PREF_CHANNEL,
|
|
(u8 *)&bss_config->channel, 2) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
//acs_mask
|
|
if (wconf_ap->acs_channel == 0x00) {
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_ACS_MASK,
|
|
(u8 *)&wconf_ap->acs_mask,
|
|
sizeof(wconf_ap->acs_mask)) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
//acs_weight
|
|
if (wconf_ap->acs_weight[0] != 0xFF) {
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_SET_ACS_WEIGHT,
|
|
(u8 *)wconf_ap->acs_weight,
|
|
sizeof(wconf_ap->acs_weight)) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
//Greenfield
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_ENABLE_GREENFIELD,
|
|
(u8 *)&bss_config->greenfield, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
//SGI
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_11N_SHORT_GI_ENABLE,
|
|
(u8 *)&bss_config->sgi, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
//STBC
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_ENABLE_STBC,
|
|
(u8 *)&bss_config->stbc, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
} else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
static s32 nvt_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
|
|
struct cfg80211_ap_settings *settings)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _BSS_CONFIG_INFO bss_config;
|
|
struct cfg80211_beacon_data *bcon = &settings->beacon;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
struct _nvt_priv *nvt_priv = &(nvt_if->nvt_adapter->nvt_priv);
|
|
#endif
|
|
s32 err = 0;
|
|
u32 dev_role = 0;
|
|
u8 enable = 1;
|
|
dev_role = nvt_if->wdev.iftype;
|
|
nvt_dbg(CFG80211, "cfg80211_start_ap\n");
|
|
memset(&bss_config, 0, sizeof(struct _BSS_CONFIG_INFO));
|
|
g_start_op = DO_RESTART;
|
|
if (dev_role == NL80211_IFTYPE_AP ||
|
|
dev_role == NL80211_IFTYPE_P2P_GO) {
|
|
bss_config.bss_mode = AP;
|
|
} else {
|
|
nvt_dbg(CFG80211, "not support dev role [%x]\n",
|
|
dev_role);
|
|
return -EINVAL;
|
|
}
|
|
if (nvt_cfg80211_bcn_params(nvt_if, &bss_config, settings) < 0)
|
|
nvt_dbg(ERROR, "nvt_cfg80211_bcn_params error!!!\n");
|
|
else if (nvt_cfg80211_ap_ies(
|
|
nvt_if, &bss_config, bcon, settings) < 0)
|
|
nvt_dbg(ERROR, "nvt_cfg80211_ap_ies error!!!\n");
|
|
else if (nvt_cfg80211_set_chan_ie(
|
|
wiphy, nvt_if, &bss_config, settings) < 0)
|
|
nvt_dbg(ERROR, "nvt_cfg80211_set_chan_ie error!!!\n");
|
|
else if (nvt_icfg_lock(nvt_bus) < 0 ||
|
|
nvt_icfg_reset(nvt_bus) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_AP_ENABLE, &enable, 1) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_START_OP, &g_start_op, 1) < 0 ||
|
|
nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_dbg(ERROR, "AP ENABLE Failed!!!\n");
|
|
nvt_icfg_unlock(nvt_bus);
|
|
} else {
|
|
nvt_dbg(CFG80211, "AP ENABLE Sucess!!!,ap_start_evt=%d\n",
|
|
nvt_if->wait_ap_start_event);
|
|
nvt_icfg_unlock(nvt_bus);
|
|
if (nvt_if->wait_ap_start_event == 0) {
|
|
set_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
}
|
|
nvt_if->start_ap_flag = 1;
|
|
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
INIT_LIST_HEAD(&nvt_priv->sta_list_in_ap);
|
|
#endif
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static s32 nvt_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
|
|
{
|
|
u8 enable = 0;
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
struct _nvt_priv *nvt_priv = &(nvt_adapter->nvt_priv);
|
|
struct _sta_sts_in_ap *sta_sts_in_ap;
|
|
#endif
|
|
u8 i = 0;
|
|
g_start_op = DO_RESET;
|
|
|
|
nvt_if->wait_ap_start_event = 0;
|
|
nvt_if->start_ap_flag = 0;
|
|
nvt_if->nvt_wconf_ap.acs_channel = 0xFF;
|
|
nvt_if->nvt_wconf_ap.acs_mask = 0;
|
|
nvt_if->nvt_wconf_ap.acs_weight[0] = 0xFF;
|
|
clear_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
|
|
if (nvt_icfg_lock(nvt_bus) < 0 ||
|
|
nvt_icfg_reset(nvt_bus) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_AP_ENABLE, &enable, 1) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_START_OP, &g_start_op, 1) < 0 ||
|
|
nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0)
|
|
nvt_icfg_unlock(nvt_bus);
|
|
else {
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
// Recycle the all sta info stored in sta_list
|
|
list_for_each_entry(sta_sts_in_ap, &nvt_priv->sta_list_in_ap, list) {
|
|
nvt_dbg(CLEARVIEW, "%s: remove element:%p\n", __func__,
|
|
sta_sts_in_ap);
|
|
list_del(&sta_sts_in_ap->list);
|
|
kfree(sta_sts_in_ap);
|
|
}
|
|
#endif
|
|
|
|
/* Reset BA handlers */
|
|
for (i = 0; i < ASSOC_NUM; i++) {
|
|
nvt_reset_ba_handle(nvt_adapter, i + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
//AT Define AP related CFG80211 ops for GM Linux Kenel V3.3
|
|
#else
|
|
static int nvt_cfg80211_set_channel(struct wiphy *wiphy,
|
|
struct net_device *ndev, struct ieee80211_channel *chan,
|
|
enum nl80211_channel_type channel_type) {
|
|
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
if (nvt_if == 0)
|
|
return -EIO;
|
|
nvt_if->ch_info.chan = chan;
|
|
nvt_if->ch_info.channel_type = channel_type;
|
|
return 0;
|
|
}
|
|
|
|
static int nvt_cfg80211_add_beacon(struct wiphy *wiphy,
|
|
struct net_device *ndev,
|
|
struct beacon_parameters *info)
|
|
{
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _BSS_CONFIG_INFO bss_config;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
struct _nvt_priv *nvt_priv = &(nvt_if->nvt_adapter->nvt_priv);
|
|
#endif
|
|
s32 err = 0;
|
|
u32 dev_role = 0;
|
|
u8 enable = 1;
|
|
dev_role = nvt_if->wdev.iftype;
|
|
nvt_dbg(CFG80211, "cfg80211_start_ap\n");
|
|
memset(&bss_config, 0, sizeof(struct _BSS_CONFIG_INFO));
|
|
g_start_op = DO_RESTART;
|
|
if (dev_role == NL80211_IFTYPE_AP ||
|
|
dev_role == NL80211_IFTYPE_P2P_GO) {
|
|
bss_config.bss_mode = AP;
|
|
} else {
|
|
nvt_dbg(CFG80211, "not support dev role [%x]\n",
|
|
dev_role);
|
|
return -EINVAL;
|
|
}
|
|
if (nvt_cfg80211_bcn_params(nvt_if, &bss_config, info) < 0)
|
|
nvt_dbg(ERROR, "nvt_cfg80211_bcn_params error!!!\n");
|
|
else if (nvt_cfg80211_ap_ies(nvt_if, &bss_config, info) < 0)
|
|
nvt_dbg(ERROR, "nvt_cfg80211_ap_ies error!!!\n");
|
|
else if (nvt_cfg80211_set_chan_ie(wiphy, nvt_if,
|
|
&bss_config, info) < 0)
|
|
nvt_dbg(ERROR, "nvt_cfg80211_set_chan_ie error!!!\n");
|
|
else if (nvt_icfg_lock(nvt_bus) < 0 ||
|
|
nvt_icfg_reset(nvt_bus) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_AP_ENABLE, &enable, 1) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_START_OP, &g_start_op, 1) < 0 ||
|
|
nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_dbg(ERROR, "AP ENABLE Failed!!!\n");
|
|
nvt_icfg_unlock(nvt_bus);
|
|
} else {
|
|
nvt_dbg(CFG80211, "AP ENABLE Sucess!!!,ap_start_evt=%d\n",
|
|
nvt_if->wait_ap_start_event);
|
|
nvt_icfg_unlock(nvt_bus);
|
|
if (nvt_if->wait_ap_start_event == 0) {
|
|
set_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
}
|
|
nvt_if->start_ap_flag = 1;
|
|
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
nvt_dbg(CLEARVIEW, "init sta_list_in_ap\n");
|
|
INIT_LIST_HEAD(&nvt_priv->sta_list_in_ap);
|
|
#endif
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int nvt_cfg80211_set_beacon(struct wiphy *wiphy,
|
|
struct net_device *ndev,
|
|
struct beacon_parameters *bcon)
|
|
{
|
|
u8 *wps_ie, *p2p_ie, *rsn_ie, *wpa_ie;
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _BSS_CONFIG_INFO bss_config;
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
struct nvt_tlv *ssid_ie;
|
|
u8 vsie_enable = 0;
|
|
u8 *capability_info = 0;
|
|
u8 ie_offset;
|
|
s32 err = 0;
|
|
struct _nvt_cfg80211 *cfg = nvt_if->nvt_adapter->nvt_cfg80211;
|
|
struct _nvt_802_11d_reg *domain_info = &cfg->domain_reg;
|
|
u8 *bcn_buf = NULL;
|
|
u8 *probe_rsp_buf = NULL;
|
|
u8 reg_buf[NVT_MAX_TRIPLET_802_11D * 3 + 3] = {0};
|
|
u8 reg_len = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
//clean ap relative flags
|
|
wconf->ctrl_flag &= ~(BEACON_EXTRA_IE_FLAG|PROBE_RESP_EXTRA_IE_FLAG|
|
|
ASSOC_RESP_EXTRA_IE_FLAG);
|
|
|
|
memset(&bss_config, 0, sizeof(struct _BSS_CONFIG_INFO));
|
|
|
|
/* find the SSID_IE */
|
|
ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
|
|
ssid_ie = nvt_parse_tlvs((u8 *)&bcon->head[ie_offset],
|
|
bcon->head_len - ie_offset, WLAN_EID_SSID);
|
|
|
|
if (!ssid_ie)
|
|
return -EINVAL;
|
|
|
|
bss_config.ssid_le.SSID_len = ssid_ie->len;
|
|
memcpy(bss_config.ssid_le.SSID, ssid_ie->data, ssid_ie->len);
|
|
|
|
// get capability info
|
|
capability_info = (u8 *)ssid_ie;
|
|
capability_info = capability_info - 2;
|
|
|
|
/* find the WPS IE */
|
|
wps_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPS,
|
|
bcon->tail,
|
|
bcon->tail_len);
|
|
if (wps_ie != NULL)
|
|
bss_config.wps_enable = 1;
|
|
else
|
|
bss_config.wps_enable = 0;
|
|
if (wps_ie != NULL)
|
|
nvt_dbg(CFG80211, "[%s]WPS_OK\n", __func__);
|
|
/* find the P2P IE */
|
|
p2p_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_WFA,
|
|
WLAN_OUI_TYPE_WFA_P2P,
|
|
bcon->tail,
|
|
bcon->tail_len);
|
|
if (p2p_ie != NULL)
|
|
bss_config.p2p_enable = 1;
|
|
else
|
|
bss_config.p2p_enable = 0;
|
|
|
|
/* find the RSN_IE */
|
|
rsn_ie = (u8 *)cfg80211_find_ie(WLAN_EID_RSN,
|
|
bcon->tail, bcon->tail_len);
|
|
|
|
/* find the WPA_IE */
|
|
wpa_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPA,
|
|
bcon->tail, bcon->tail_len);
|
|
|
|
if ((wpa_ie != NULL || rsn_ie != NULL)) {
|
|
if (wpa_ie != NULL) {
|
|
err = nvt_configure_wpaie((struct nvt_vs_tlv *)wpa_ie,
|
|
false, &bss_config.security_mode,
|
|
&bss_config.auth_mode);
|
|
/* WPA IE */
|
|
bss_config.security_mode |= WID_11I_MODE_WPA;
|
|
nvt_dbg(CFG80211, "WPA_IE OK\n");
|
|
if (err < 0)
|
|
goto exit;
|
|
}
|
|
if (rsn_ie != NULL) {
|
|
err = nvt_configure_wpaie((struct nvt_vs_tlv *)rsn_ie,
|
|
true, &bss_config.security_mode,
|
|
&bss_config.auth_mode);
|
|
/* RSN IE */
|
|
bss_config.security_mode |= WID_11I_MODE_RSN;
|
|
if (err < 0)
|
|
goto exit;
|
|
}
|
|
} else {
|
|
if (capability_info[0] & 0x10) {
|
|
// privacy flag
|
|
err = -EINVAL;
|
|
goto exit;
|
|
} else {
|
|
bss_config.auth_mode = WID_AUTH_OPEN_PSK;
|
|
bss_config.security_mode = 0;
|
|
}
|
|
}
|
|
if (nvt_icfg_lock(nvt_bus) < 0 || nvt_icfg_reset(nvt_bus) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
//ssid
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_SSID,
|
|
(u8 *)&bss_config.ssid_le.SSID, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
//security
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_11I_MODE,
|
|
(u8 *)&bss_config.security_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_AUTH_TYPE,
|
|
(u8 *)&bss_config.auth_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// WPS_enable
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_WPS_REGISTRATION_START,
|
|
(u8 *)&bss_config.wps_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// P2P_enable
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_P2P_ENABLE,
|
|
(u8 *)&bss_config.p2p_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
} else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
if (nvt_icfg_lock(nvt_bus) < 0 || nvt_icfg_reset(nvt_bus) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
nvt_dbg(CFG80211, "%s, country_code: %c%c\n", __func__,
|
|
domain_info->country_code[0],
|
|
domain_info->country_code[1]);
|
|
|
|
if (nvt_is_an_alpha2(domain_info->country_code)) {
|
|
nvt_dbg(CFG80211, "construct country element\n");
|
|
reg_len = nvt_set_country_info_elem(reg_buf, domain_info);
|
|
}
|
|
|
|
//beacon ies
|
|
if (((bcon->beacon_ies_len > 0) && (bcon->beacon_ies != NULL)) &&
|
|
(reg_len != 0)) {
|
|
vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "add country info. in extra beacon ies\n");
|
|
bcn_buf = kzalloc(reg_len + bcon->beacon_ies_len, GFP_KERNEL);
|
|
if (bcn_buf == NULL) {
|
|
nvt_dbg(CFG80211, "alloc bcn buf fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(&bcn_buf[0], ®_buf[0], reg_len);
|
|
memcpy(&bcn_buf[reg_len], bcon->beacon_ies,
|
|
bcon->beacon_ies_len);
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)bcn_buf, reg_len + bcon->beacon_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
kfree(bcn_buf);
|
|
return -EINVAL;
|
|
}
|
|
kfree(bcn_buf);
|
|
} else if ((bcon->beacon_ies_len > 0) && (bcon->beacon_ies != NULL)) {
|
|
vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)bcon->beacon_ies, bcon->beacon_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else if (reg_len != 0) {
|
|
nvt_dbg(CFG80211, "only country info. in extra beacon ies\n");
|
|
vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)reg_buf, reg_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "no ie in extra beacon ies\n");
|
|
}
|
|
|
|
//probe rsp ies
|
|
// filter p2p IE
|
|
err = nvt_filter_p2p_ie(&bcon->proberesp_ies, &bcon->proberesp_ies_len);
|
|
if (err < 0)
|
|
goto exit;
|
|
// filter wfd IE
|
|
err = nvt_filter_wfd_ie(&bcon->proberesp_ies, &bcon->proberesp_ies_len);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
if (((bcon->proberesp_ies_len > 0) && (bcon->proberesp_ies != NULL)) &&
|
|
(reg_len != 0)) {
|
|
vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "add country info. in extra prob_rsp ies\n");
|
|
probe_rsp_buf = kzalloc(reg_len + bcon->proberesp_ies_len,
|
|
GFP_KERNEL);
|
|
if (probe_rsp_buf == NULL) {
|
|
nvt_dbg(CFG80211, "alloc prob_rsp buf fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(&probe_rsp_buf[0], ®_buf[0], reg_len);
|
|
memcpy(&probe_rsp_buf[reg_len], bcon->proberesp_ies,
|
|
bcon->proberesp_ies_len);
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)probe_rsp_buf,
|
|
reg_len + bcon->proberesp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
kfree(probe_rsp_buf);
|
|
return -EINVAL;
|
|
}
|
|
kfree(probe_rsp_buf);
|
|
} else if ((bcon->proberesp_ies_len > 0) &&
|
|
(bcon->proberesp_ies != NULL)) {
|
|
vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)bcon->proberesp_ies,
|
|
bcon->proberesp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else if (reg_len != 0) {
|
|
vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "only country info. in extra prob_rsp ies\n");
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)reg_buf, reg_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "no ie in extra prob_rsp ies\n");
|
|
}
|
|
|
|
//assoc rsp ies
|
|
if ((bcon->assocresp_ies_len > 0) && (bcon->assocresp_ies != NULL)) {
|
|
vsie_enable |= 0x08;
|
|
wconf->ctrl_flag |= ASSOC_RESP_EXTRA_IE_FLAG;
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_ASSOCRSP,
|
|
(u8 *)bcon->assocresp_ies, bcon->assocresp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_FRAME,
|
|
(u8 *)&vsie_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
exit:
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
} else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int nvt_cfg80211_del_beacon(struct wiphy *wiphy,
|
|
struct net_device *ndev)
|
|
{
|
|
u8 enable = 0;
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
struct _nvt_priv *nvt_priv = &(nvt_adapter->nvt_priv);
|
|
struct _sta_sts_in_ap *sta_sts_in_ap;
|
|
#endif
|
|
u8 i = 0;
|
|
g_start_op = DO_RESET;
|
|
|
|
nvt_if->wait_ap_start_event = 0;
|
|
nvt_if->start_ap_flag = 0;
|
|
nvt_if->nvt_wconf_ap.acs_channel = 0xFF;
|
|
nvt_if->nvt_wconf_ap.acs_mask = 0;
|
|
nvt_if->nvt_wconf_ap.acs_weight[0] = 0xFF;
|
|
clear_bit(NVT_IF_CONNECTED, &nvt_if->state_flags);
|
|
|
|
if (nvt_icfg_lock(nvt_bus) < 0 ||
|
|
nvt_icfg_reset(nvt_bus) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_AP_ENABLE, &enable, 1) < 0 ||
|
|
nvt_icfg_add(nvt_bus, NVT_ICFG_SET,
|
|
WID_START_OP, &g_start_op, 1) < 0 ||
|
|
nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0)
|
|
nvt_icfg_unlock(nvt_bus);
|
|
else {
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
// Recycle the all sta info stored in sta_list
|
|
list_for_each_entry(sta_sts_in_ap, &nvt_priv->sta_list_in_ap, list) {
|
|
nvt_dbg(CLEARVIEW, "%s: remove element:%p\n", __func__,
|
|
sta_sts_in_ap);
|
|
list_del(&sta_sts_in_ap->list);
|
|
kfree(sta_sts_in_ap);
|
|
}
|
|
#endif
|
|
|
|
/* Reset BA handlers */
|
|
for (i = 0; i < ASSOC_NUM; i++) {
|
|
nvt_reset_ba_handle(nvt_adapter, i + 1);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static s32 nvt_filter_p2p_ie(const u8 **ies, size_t *ies_len)
|
|
{
|
|
const u8 *pos;
|
|
u8 *buf = NULL;
|
|
size_t len = 0;
|
|
|
|
if (*ies && *ies_len) {
|
|
buf = kmalloc(*ies_len, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
pos = *ies;
|
|
|
|
while (pos + 1 < *ies + *ies_len) {
|
|
if (pos + 2 + pos[1] > *ies + *ies_len)
|
|
break;
|
|
if (!(pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
|
|
pos[1] >= 4 && pos[2] == 0x50 &&
|
|
pos[3] == 0x6f && pos[4] == 0x9a &&
|
|
pos[5] == 0x09)) {
|
|
memcpy(buf + len, pos, 2 + pos[1]);
|
|
len += 2 + pos[1];
|
|
}
|
|
pos += 2 + pos[1];
|
|
}
|
|
}
|
|
*ies = buf;
|
|
*ies_len = len;
|
|
|
|
kfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
static s32 nvt_filter_wfd_ie(const u8 **ies, size_t *ies_len)
|
|
{
|
|
const u8 *pos;
|
|
u8 *buf = NULL;
|
|
size_t len = 0;
|
|
|
|
if (*ies && *ies_len) {
|
|
buf = kmalloc(*ies_len, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
pos = *ies;
|
|
|
|
while (pos + 1 < *ies + *ies_len) {
|
|
if (pos + 2 + pos[1] > *ies + *ies_len)
|
|
break;
|
|
if (!(pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
|
|
pos[1] >= 4 && pos[2] == 0x50 &&
|
|
pos[3] == 0x6f && pos[4] == 0x9a &&
|
|
pos[5] == 0x0A)) {
|
|
memcpy(buf + len, pos, 2 + pos[1]);
|
|
len += 2 + pos[1];
|
|
}
|
|
pos += 2 + pos[1];
|
|
}
|
|
}
|
|
*ies = buf;
|
|
*ies_len = len;
|
|
|
|
kfree(buf);
|
|
return 0;
|
|
}
|
|
|
|
struct nvt_tlv *nvt_parse_tlvs(void *buf, int buflen, uint key)
|
|
{
|
|
struct nvt_tlv *elt;
|
|
int totlen;
|
|
|
|
elt = (struct nvt_tlv *)buf;
|
|
totlen = buflen;
|
|
|
|
/* find tagged parameter */
|
|
while (totlen >= TLV_HDR_LEN) {
|
|
int len = elt->len;
|
|
|
|
/* validate remaining totlen */
|
|
if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
|
|
return elt;
|
|
|
|
elt = (struct nvt_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
|
|
totlen -= (len + TLV_HDR_LEN);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
|
|
#define WPA_OUI_TYPE 1
|
|
#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
|
|
#define WME_OUI_TYPE 2
|
|
#define WPS_OUI_TYPE 4
|
|
|
|
#define VS_IE_FIXED_HDR_LEN 6
|
|
#define WPA_IE_VERSION_LEN 2
|
|
#define WPA_IE_MIN_OUI_LEN 4
|
|
#define WPA_IE_SUITE_COUNT_LEN 2
|
|
|
|
#define WPA_CIPHER_NONE 0 /* None */
|
|
#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
|
|
#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
|
|
#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
|
|
#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
|
|
|
|
#define RSN_AKM_NONE 0 /* None (IBSS) */
|
|
#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
|
|
#define RSN_AKM_PSK 2 /* Pre-shared Key */
|
|
#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
|
|
#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
|
|
#define RSN_CAP_MASK 0x000C
|
|
|
|
static bool nvt_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
|
|
{
|
|
if (is_rsn_ie)
|
|
return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
|
|
|
|
return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
|
|
}
|
|
|
|
s32 nvt_configure_wpaie(struct nvt_vs_tlv *wpa_ie, bool is_rsn_ie,
|
|
u8 *Nova_11I, u8 *Nova_AUTH)
|
|
{
|
|
u16 count;
|
|
s32 err = 0;
|
|
s32 len = 0;
|
|
u32 i;
|
|
u32 pval = 0;
|
|
u32 gval = 0;
|
|
u32 wpa_auth = 0;
|
|
u32 offset;
|
|
u8 *data;
|
|
u16 rsn_cap;
|
|
|
|
if (wpa_ie == NULL)
|
|
goto exit;
|
|
|
|
len = wpa_ie->len + TLV_HDR_LEN;
|
|
data = (u8 *)wpa_ie;
|
|
offset = TLV_HDR_LEN;
|
|
if (!is_rsn_ie)
|
|
offset += VS_IE_FIXED_HDR_LEN;
|
|
else
|
|
offset += WPA_IE_VERSION_LEN;
|
|
|
|
/* check for multicast cipher suite */
|
|
if (offset + WPA_IE_MIN_OUI_LEN > len) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if (!nvt_valid_wpa_oui(&data[offset], is_rsn_ie)) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
offset += TLV_OUI_LEN;
|
|
|
|
/* pick up multicast cipher */
|
|
switch (data[offset]) {
|
|
case WPA_CIPHER_NONE:
|
|
gval = 0;
|
|
break;
|
|
case WPA_CIPHER_WEP_40:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON | WID_11I_MODE_WEP40);
|
|
gval = 0x1;
|
|
break;
|
|
case WPA_CIPHER_WEP_104:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON | WID_11I_MODE_WEP104);
|
|
gval = 0x1;
|
|
break;
|
|
case WPA_CIPHER_TKIP:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON | WID_11I_MODE_TKIP);
|
|
gval = 0x2;
|
|
break;
|
|
case WPA_CIPHER_AES_CCM:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON | WID_11I_MODE_CCMP);
|
|
gval = 0x4;
|
|
break;
|
|
default:
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
offset++;
|
|
/* walk thru unicast cipher list and pick up what we recognize */
|
|
count = data[offset] + (data[offset + 1] << 8);
|
|
offset += WPA_IE_SUITE_COUNT_LEN;
|
|
/* Check for unicast suite(s) */
|
|
if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
if (!nvt_valid_wpa_oui(&data[offset], is_rsn_ie)) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
offset += TLV_OUI_LEN;
|
|
switch (data[offset]) {
|
|
case WPA_CIPHER_NONE:
|
|
break;
|
|
case WPA_CIPHER_WEP_40:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON |
|
|
WID_11I_MODE_WEP40);
|
|
pval |= 0x1;
|
|
break;
|
|
case WPA_CIPHER_WEP_104:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON |
|
|
WID_11I_MODE_WEP104);
|
|
pval |= 0x1;
|
|
break;
|
|
case WPA_CIPHER_TKIP:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON |
|
|
WID_11I_MODE_TKIP);
|
|
pval |= 0x2;
|
|
break;
|
|
case WPA_CIPHER_AES_CCM:
|
|
*Nova_11I |= (WID_11I_MODE_Security_ON |
|
|
WID_11I_MODE_CCMP);
|
|
pval |= 0x4;
|
|
break;
|
|
default:
|
|
nvt_dbg(CFG80211, "Ivalid unicast security info\n");
|
|
}
|
|
offset++;
|
|
}
|
|
/* walk thru auth management suite list and pick up what we recognize */
|
|
count = data[offset] + (data[offset + 1] << 8);
|
|
offset += WPA_IE_SUITE_COUNT_LEN;
|
|
/* Check for auth key management suite(s) */
|
|
if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
if (!nvt_valid_wpa_oui(&data[offset], is_rsn_ie)) {
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
offset += TLV_OUI_LEN;
|
|
switch (data[offset]) {
|
|
case RSN_AKM_NONE:
|
|
*Nova_AUTH |= WID_AUTH_OPEN_PSK;
|
|
wpa_auth |= 0x1;
|
|
break;
|
|
case RSN_AKM_UNSPECIFIED:
|
|
*Nova_AUTH |= WID_AUTH_802_1X;
|
|
is_rsn_ie ? (wpa_auth |= 0x40) :
|
|
(wpa_auth |= 0x2);
|
|
break;
|
|
case RSN_AKM_PSK:
|
|
*Nova_AUTH |= WID_AUTH_OPEN_PSK;
|
|
is_rsn_ie ? (wpa_auth |= 0x80) :
|
|
(wpa_auth |= 0x4);
|
|
break;
|
|
default:
|
|
nvt_dbg(CFG80211, "Ivalid key mgmt info\n");
|
|
}
|
|
offset++;
|
|
}
|
|
|
|
/* Sanity Check with FW capability */
|
|
if (is_rsn_ie) {
|
|
if ((offset + RSN_CAP_LEN) <= len) {
|
|
rsn_cap = data[offset] + (data[offset + 1] << 8);
|
|
if ((rsn_cap & ~RSN_CAP_MASK) != 0)
|
|
err = -EINVAL;
|
|
}
|
|
if (err < 0) {
|
|
nvt_dbg(CFG80211, "RSN capability mismatch\n");
|
|
goto exit;
|
|
}
|
|
}
|
|
exit:
|
|
return err;
|
|
}
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
s32 nvt_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
|
|
struct cfg80211_beacon_data *bcon)
|
|
{
|
|
u8 *wps_ie, *p2p_ie, *rsn_ie, *wpa_ie;
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _BSS_CONFIG_INFO bss_config;
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
struct nvt_tlv *ssid_ie;
|
|
u8 vsie_enable = 0;
|
|
u8 *capability_info = 0;
|
|
u8 ie_offset;
|
|
s32 err = 0;
|
|
struct _nvt_cfg80211 *cfg = nvt_if->nvt_adapter->nvt_cfg80211;
|
|
struct _nvt_802_11d_reg *domain_info = &cfg->domain_reg;
|
|
u8 *bcn_buf = NULL;
|
|
u8 *probe_rsp_buf = NULL;
|
|
u8 reg_buf[NVT_MAX_TRIPLET_802_11D * 3 + 3] = {0};
|
|
u8 reg_len = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
//clean ap relative flags
|
|
wconf->ctrl_flag &= ~(BEACON_EXTRA_IE_FLAG|PROBE_RESP_EXTRA_IE_FLAG|
|
|
ASSOC_RESP_EXTRA_IE_FLAG);
|
|
|
|
memset(&bss_config, 0, sizeof(struct _BSS_CONFIG_INFO));
|
|
|
|
/* find the SSID_IE */
|
|
ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
|
|
ssid_ie = nvt_parse_tlvs((u8 *)&bcon->head[ie_offset],
|
|
bcon->head_len - ie_offset, WLAN_EID_SSID);
|
|
|
|
if (!ssid_ie)
|
|
return -EINVAL;
|
|
|
|
bss_config.ssid_le.SSID_len = ssid_ie->len;
|
|
memcpy(bss_config.ssid_le.SSID, ssid_ie->data, ssid_ie->len);
|
|
|
|
// get capability info
|
|
capability_info = (u8 *)ssid_ie;
|
|
capability_info = capability_info - 2;
|
|
|
|
/* find the WPS IE */
|
|
wps_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPS,
|
|
bcon->tail,
|
|
bcon->tail_len);
|
|
if (wps_ie != NULL)
|
|
bss_config.wps_enable = 1;
|
|
else
|
|
bss_config.wps_enable = 0;
|
|
if (wps_ie != NULL)
|
|
nvt_dbg(CFG80211, "[%s]WPS_OK\n", __func__);
|
|
/* find the P2P IE */
|
|
p2p_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_WFA,
|
|
WLAN_OUI_TYPE_WFA_P2P,
|
|
bcon->tail,
|
|
bcon->tail_len);
|
|
if (p2p_ie != NULL)
|
|
bss_config.p2p_enable = 1;
|
|
else
|
|
bss_config.p2p_enable = 0;
|
|
|
|
/* find the RSN_IE */
|
|
rsn_ie = (u8 *)cfg80211_find_ie(WLAN_EID_RSN,
|
|
bcon->tail, bcon->tail_len);
|
|
|
|
/* find the WPA_IE */
|
|
wpa_ie = (u8 *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT,
|
|
WLAN_OUI_TYPE_MICROSOFT_WPA,
|
|
bcon->tail, bcon->tail_len);
|
|
|
|
if ((wpa_ie != NULL || rsn_ie != NULL)) {
|
|
if (wpa_ie != NULL) {
|
|
err = nvt_configure_wpaie((struct nvt_vs_tlv *)wpa_ie,
|
|
false, &bss_config.security_mode,
|
|
&bss_config.auth_mode);
|
|
/* WPA IE */
|
|
bss_config.security_mode |= WID_11I_MODE_WPA;
|
|
nvt_dbg(CFG80211, "WPA_IE OK\n");
|
|
if (err < 0)
|
|
goto exit;
|
|
}
|
|
if (rsn_ie != NULL) {
|
|
err = nvt_configure_wpaie((struct nvt_vs_tlv *)rsn_ie,
|
|
true, &bss_config.security_mode,
|
|
&bss_config.auth_mode);
|
|
/* RSN IE */
|
|
bss_config.security_mode |= WID_11I_MODE_RSN;
|
|
if (err < 0)
|
|
goto exit;
|
|
}
|
|
} else {
|
|
if (capability_info[0] & 0x10) {
|
|
// privacy flag
|
|
err = -EINVAL;
|
|
goto exit;
|
|
} else {
|
|
bss_config.auth_mode = WID_AUTH_OPEN_PSK;
|
|
bss_config.security_mode = 0;
|
|
}
|
|
}
|
|
if (nvt_icfg_lock(nvt_bus) < 0 || nvt_icfg_reset(nvt_bus) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
//ssid
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_SSID,
|
|
(u8 *)&bss_config.ssid_le.SSID, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
//security
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_11I_MODE,
|
|
(u8 *)&bss_config.security_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_AUTH_TYPE,
|
|
(u8 *)&bss_config.auth_mode, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// WPS_enable
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_WPS_REGISTRATION_START,
|
|
(u8 *)&bss_config.wps_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
// P2P_enable
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_P2P_ENABLE,
|
|
(u8 *)&bss_config.p2p_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
} else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
if (nvt_icfg_lock(nvt_bus) < 0 || nvt_icfg_reset(nvt_bus) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
|
|
nvt_dbg(CFG80211, "%s, country_code: %c%c\n", __func__,
|
|
domain_info->country_code[0],
|
|
domain_info->country_code[1]);
|
|
|
|
if (nvt_is_an_alpha2(domain_info->country_code)) {
|
|
nvt_dbg(CFG80211, "construct country element\n");
|
|
reg_len = nvt_set_country_info_elem(reg_buf, domain_info);
|
|
}
|
|
|
|
//beacon ies
|
|
if (((bcon->beacon_ies_len > 0) && (bcon->beacon_ies != NULL)) &&
|
|
(reg_len != 0)) {
|
|
vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "add country info. in extra beacon ies\n");
|
|
bcn_buf = kzalloc(reg_len + bcon->beacon_ies_len, GFP_KERNEL);
|
|
if (bcn_buf == NULL) {
|
|
nvt_dbg(CFG80211, "alloc bcn buf fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(&bcn_buf[0], ®_buf[0], reg_len);
|
|
memcpy(&bcn_buf[reg_len], bcon->beacon_ies,
|
|
bcon->beacon_ies_len);
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)bcn_buf, reg_len + bcon->beacon_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
kfree(bcn_buf);
|
|
return -EINVAL;
|
|
}
|
|
kfree(bcn_buf);
|
|
} else if ((bcon->beacon_ies_len > 0) && (bcon->beacon_ies != NULL)) {
|
|
vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)bcon->beacon_ies, bcon->beacon_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else if (reg_len != 0) {
|
|
nvt_dbg(CFG80211, "only country info. in extra beacon ies\n");
|
|
vsie_enable |= 0x01;
|
|
wconf->ctrl_flag |= BEACON_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_DATA,
|
|
(u8 *)reg_buf, reg_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "no ie in extra beacon ies\n");
|
|
}
|
|
|
|
//probe rsp ies
|
|
// filter p2p IE
|
|
err = nvt_filter_p2p_ie(&bcon->proberesp_ies, &bcon->proberesp_ies_len);
|
|
if (err < 0)
|
|
goto exit;
|
|
// filter wfd IE
|
|
err = nvt_filter_wfd_ie(&bcon->proberesp_ies, &bcon->proberesp_ies_len);
|
|
if (err < 0)
|
|
goto exit;
|
|
|
|
if (((bcon->proberesp_ies_len > 0) && (bcon->proberesp_ies != NULL)) &&
|
|
(reg_len != 0)) {
|
|
vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "add country info. in extra prob_rsp ies\n");
|
|
probe_rsp_buf = kzalloc(reg_len + bcon->proberesp_ies_len,
|
|
GFP_KERNEL);
|
|
if (probe_rsp_buf == NULL) {
|
|
nvt_dbg(CFG80211, "alloc prob_rsp buf fail\n");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(&probe_rsp_buf[0], ®_buf[0], reg_len);
|
|
memcpy(&probe_rsp_buf[reg_len], bcon->proberesp_ies,
|
|
bcon->proberesp_ies_len);
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)probe_rsp_buf,
|
|
reg_len + bcon->proberesp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
kfree(probe_rsp_buf);
|
|
return -EINVAL;
|
|
}
|
|
kfree(probe_rsp_buf);
|
|
} else if ((bcon->proberesp_ies_len > 0) &&
|
|
(bcon->proberesp_ies != NULL)) {
|
|
vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)bcon->proberesp_ies,
|
|
bcon->proberesp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else if (reg_len != 0) {
|
|
vsie_enable |= 0x04;
|
|
wconf->ctrl_flag |= PROBE_RESP_EXTRA_IE_FLAG;
|
|
nvt_dbg(CFG80211, "only country info. in extra prob_rsp ies\n");
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_PROBERSP,
|
|
(u8 *)reg_buf, reg_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
nvt_dbg(CFG80211, "no ie in extra prob_rsp ies\n");
|
|
}
|
|
|
|
//assoc rsp ies
|
|
if ((bcon->assocresp_ies_len > 0) && (bcon->assocresp_ies != NULL)) {
|
|
vsie_enable |= 0x08;
|
|
wconf->ctrl_flag |= ASSOC_RESP_EXTRA_IE_FLAG;
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_TX_ASSOCRSP,
|
|
(u8 *)bcon->assocresp_ies, bcon->assocresp_ies_len) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (nvt_icfg_add(nvt_bus, NVT_ICFG_SET, WID_VSIE_FRAME,
|
|
(u8 *)&vsie_enable, 1) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
}
|
|
exit:
|
|
if (nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) < 0) {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
return -EINVAL;
|
|
} else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
|
|
static s32
|
|
nvt_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
|
|
u8 *mac)
|
|
#else
|
|
static s32
|
|
nvt_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
|
|
struct station_del_parameters *params)
|
|
#endif
|
|
{
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
|
u8 *mac = (u8 *)params->mac;
|
|
#endif
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
struct _nvt_priv *nvt_priv = &(nvt_adapter->nvt_priv);
|
|
struct _sta_sts_in_ap *sta_sts_in_ap;
|
|
#endif
|
|
struct _ba_struct_t *ba_ctxt = NULL;
|
|
u8 i = 0;
|
|
u8 idx;
|
|
s32 err = 0;
|
|
|
|
nvt_dbg(CFG80211, "%s\n", __func__);
|
|
|
|
if ((!mac) || is_broadcast_ether_addr(mac))
|
|
return -EFAULT;
|
|
|
|
if (nvt_icfg_lock(nvt_bus) > -1 &&
|
|
nvt_icfg_reset(nvt_bus) > -1 &&
|
|
nvt_icfg_add(nvt_bus,
|
|
NVT_ICFG_SET, WID_EXT_DISCONNECT, mac, 6) > -1 &&
|
|
nvt_icfg_send_and_get_resp(nvt_bus, NULL, 0) > -1)
|
|
nvt_icfg_unlock(nvt_bus);
|
|
else {
|
|
nvt_icfg_unlock(nvt_bus);
|
|
err = -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_WIFI_TUNING_PHASE_II
|
|
list_for_each_entry(sta_sts_in_ap, &nvt_priv->sta_list_in_ap, list) {
|
|
if (!memcmp(sta_sts_in_ap->mac_addr, mac, ETH_ALEN)) {
|
|
nvt_dbg(CLEARVIEW, "%s: del sta sts from list, %p\n", __func__,
|
|
sta_sts_in_ap);
|
|
list_del(&sta_sts_in_ap->list);
|
|
kfree(sta_sts_in_ap);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Reset BA handlers */
|
|
for (i = 0; i < ASSOC_NUM; i++) {
|
|
idx = i * NUM_TIDS;
|
|
ba_ctxt = &(nvt_adapter->nvt_priv.ba_rx[idx]);
|
|
if (memcmp(ba_ctxt->dst_addr, mac, ETH_ALEN) == 0) {
|
|
nvt_reset_ba_handle(nvt_adapter, i + 1);
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static s32
|
|
nvt_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
|
|
enum nl80211_iftype type, u32 *flags, struct vif_params *params)
|
|
{
|
|
s32 ret = 0;
|
|
struct _nvt_if *nvt_if = netdev_priv(ndev);
|
|
|
|
nvt_dbg(CFG80211, "%s, iftype = %d\n", __func__, type);
|
|
|
|
ndev->type = ARPHRD_ETHER;
|
|
switch (type) {
|
|
case NL80211_IFTYPE_MONITOR:
|
|
nvt_reset_fcmode(nvt_if, type);
|
|
if (nvt_if->mode != NVT_FW_STA) {
|
|
nvt_if->mode = NVT_FW_STA;
|
|
ret = nvt_fw_change_opmode(nvt_if, NVT_FW_STA);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s:fw download STA fail\r\n",
|
|
__func__);
|
|
}
|
|
}
|
|
ndev->type = ARPHRD_IEEE80211_RADIOTAP;
|
|
break;
|
|
case NL80211_IFTYPE_WDS:
|
|
case NL80211_IFTYPE_MESH_POINT:
|
|
nvt_dbg(ERROR,
|
|
"type (%d) : currently we do not support this type\n", type);
|
|
break;
|
|
case NL80211_IFTYPE_ADHOC:
|
|
break;
|
|
case NL80211_IFTYPE_STATION:
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
nvt_reset_fcmode(nvt_if, type);
|
|
if (nvt_if->mode != NVT_FW_STA) {
|
|
nvt_if->mode = NVT_FW_STA;
|
|
ret = nvt_fw_change_opmode(nvt_if, NVT_FW_STA);
|
|
//nvt_init_fw_by_wids(nvt_if->nvt_adapter->nvt_bus);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s:fw download STA fail\r\n",
|
|
__func__);
|
|
}
|
|
}
|
|
break;
|
|
case NL80211_IFTYPE_AP:
|
|
case NL80211_IFTYPE_AP_VLAN:
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
nvt_reset_fcmode(nvt_if, type);
|
|
nvt_if->mode = NVT_FW_AP;
|
|
ret = nvt_fw_change_opmode(nvt_if, NVT_FW_AP);
|
|
if (ret < 0) {
|
|
nvt_dbg(ERROR, "%s:fw download AP fail\r\n", __func__);
|
|
}
|
|
/*get fw info*/
|
|
{
|
|
s32 val_len = 0;
|
|
u8 resp[512] = {0};
|
|
u32 buff_size = 512;
|
|
u8 wid_val_pos = 0;
|
|
struct _nvt_bus *nvt_bus = nvt_if->nvt_adapter->nvt_bus;
|
|
|
|
val_len = nvt_get_wid_value(nvt_bus, WID_FIRMWARE_INFO,
|
|
resp, buff_size, &wid_val_pos);
|
|
|
|
if (resp[wid_val_pos+6] & 0x8) {
|
|
nvt_if->wait_ap_start_event = 1;
|
|
} else {
|
|
nvt_if->wait_ap_start_event = 0;
|
|
}
|
|
}
|
|
|
|
/* Initialize FW defaut configurations */
|
|
//nvt_init_fw_by_wids(nvt_if->nvt_adapter->nvt_bus);
|
|
break;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
nvt_if->wdev.iftype = type;
|
|
|
|
/* get firmware capability */
|
|
nvt_set_firmware_capability(nvt_if->nvt_adapter);
|
|
|
|
/* Initialize FW defaut configurations */
|
|
nvt_init_fw_by_wids(nvt_if->nvt_adapter->nvt_bus, type);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* nvt_resume_result - station mode resume event handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_resume_result(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
u16 wid = data[0]+(data[1] << 8);
|
|
|
|
nvt_dbg(CFG80211, "%s, wid=0x%x\n", __func__, wid);
|
|
nvt_if->sleep_mode = NVT_SLEEP_MODE_DISABLE;
|
|
|
|
if (data[2] != 0) {
|
|
nvt_scan_init(nvt_adapter->nvt_cfg80211);
|
|
cfg80211_disconnected(nvt_if->ndev, 0, NULL, 0, GFP_KERNEL);
|
|
}
|
|
|
|
if (nvt_txmq == 0) {
|
|
netif_start_queue(nvt_if->ndev);
|
|
} else {
|
|
netif_tx_start_all_queues(nvt_if->ndev);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_rekeyoffload_info - station mode rekeyoffload info handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_rekeyoffload_info(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
struct _nvt_wlan_conf *wconf = &nvt_if->nvt_wconf;
|
|
struct _nvt_sec *sec = &nvt_if->nvt_wconf.nvt_sec;
|
|
u16 wid = data[0]+(data[1] << 8);
|
|
|
|
nvt_dbg(CFG80211, "%s, wid=0x%x\n", __func__, wid);
|
|
|
|
if (sec->ext_mode_11i & WID_EXT_11I_WAPI) {
|
|
nvt_dbg(CFG80211, "WAPI REKEY OFFLOAD INFO\n");
|
|
cfg80211_gtk_rekey_notify(nvt_if->ndev,
|
|
wconf->bssid, &data[2], GFP_KERNEL);
|
|
} else if ((sec->mode_11i & WID_11I_TKIP) ||
|
|
(sec->mode_11i & WID_11I_CCMP)) {
|
|
nvt_dbg(CFG80211, "WPA REKEY OFFLOAD INFO\n");
|
|
cfg80211_gtk_rekey_notify(nvt_if->ndev,
|
|
wconf->bssid, &data[2], GFP_KERNEL);
|
|
} else {
|
|
nvt_dbg(CFG80211, "Rekey but not in WPA/WPA2/WAPI mode\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* nvt_setkey_info - station mode setkey info handle
|
|
*
|
|
* @nvt_if: nvt network interface
|
|
* @msg_len: event message length
|
|
* @data: event data
|
|
*
|
|
* Return: return 0 success
|
|
*/
|
|
s32 nvt_setkey_info(struct _nvt_if *nvt_if, u16 msg_len, u8 *data)
|
|
{
|
|
struct _nvt_adapter *nvt_adapter = nvt_if->nvt_adapter;
|
|
u8 sta_index;
|
|
u16 wid = data[0]+(data[1] << 8);
|
|
|
|
nvt_dbg(ERROR, "%s, wid=0x%x\n", __func__, wid);
|
|
|
|
/* handle set ptk key info */
|
|
if (data[2] == 1) {
|
|
for (sta_index = 1; sta_index < 9; sta_index++) {
|
|
nvt_dbg(ERROR, "reset unicast PN\n");
|
|
nvt_reset_unicast_pn(nvt_adapter, sta_index);
|
|
}
|
|
}
|
|
/* handle set gtk key info */
|
|
if (data[3] == 1) {
|
|
if (nvt_adapter->nvt_priv.pn_bcmc == NULL) {
|
|
nvt_dbg(ERROR, "BCMC PN is NULL, allocate it\n");
|
|
nvt_adapter->nvt_priv.pn_bcmc = kzalloc(6, GFP_KERNEL);
|
|
if (nvt_adapter->nvt_priv.pn_bcmc == NULL) {
|
|
nvt_dbg(ERROR, "BC PN allocate fail!!\n");
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
memcpy(nvt_adapter->nvt_priv.pn_bcmc, &data[4], 6);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*compute UDP and IP header's checksum*/
|
|
u16 dhcp_checksum(u8 *buf, u32 nbytes, u32 sum)
|
|
{
|
|
u32 counter;
|
|
counter = 0;
|
|
while (nbytes > 1) {
|
|
sum += htons(*((u16 *)(buf + counter)));
|
|
nbytes -= 2;
|
|
counter += 2;
|
|
}
|
|
|
|
if (nbytes) {
|
|
sum += (buf[counter] << 8);
|
|
}
|
|
while (sum >> 16) {
|
|
sum = (sum & 0xffff) + (sum >> 16);
|
|
}
|
|
return ~sum;
|
|
}
|
|
|
|
void build_dhcp_renew_pat(u8 *buf, u8 *src_addr, u8 *dst_addr,
|
|
u8 *mac_addr)
|
|
{
|
|
u16 buf_idx = 0;
|
|
u8 checksum[2] = {0, 0};
|
|
u16 sum = 0;
|
|
u16 udp_data_size = sizeof(struct dhcp_udp) +
|
|
sizeof(struct dhcp_packet);
|
|
buf_idx += sizeof(struct dhcp_ip);
|
|
build_udp_header(buf + buf_idx, checksum);
|
|
buf_idx += sizeof(struct dhcp_udp);
|
|
build_dhcp_request(buf + buf_idx, mac_addr, src_addr, dst_addr);
|
|
//back 12 byte to fill udp pseudo header
|
|
//since pseudo header for UDP require 12bytes
|
|
buf_idx = sizeof(struct dhcp_ip) - 12;
|
|
//fill udp pseudo header
|
|
memcpy(buf + buf_idx, src_addr, 4);
|
|
buf_idx += 4;
|
|
memcpy(buf + buf_idx, dst_addr, 4);
|
|
buf_idx += 4;
|
|
*(buf + buf_idx) = 0x00;
|
|
*(buf + buf_idx + 1) = 0x11;
|
|
buf_idx += 2;
|
|
*(buf + buf_idx) = (udp_data_size >> 8) & 0xff;
|
|
*(buf + buf_idx + 1) = udp_data_size & 0xff;
|
|
buf_idx = sizeof(struct dhcp_ip) - 12;
|
|
//calculate udp checksum
|
|
sum = dhcp_checksum(buf + buf_idx,
|
|
20 + sizeof(struct dhcp_packet), 0);
|
|
//fill checksum to udp header
|
|
((struct dhcp_udp *)(buf + sizeof(struct dhcp_ip)))->checksum[1]
|
|
= sum & 0xFF;
|
|
((struct dhcp_udp *)(buf + sizeof(struct dhcp_ip)))->checksum[0]
|
|
= (sum >> 8) & 0xFF;
|
|
//build IP header
|
|
build_ip_header(buf, checksum, src_addr, dst_addr);
|
|
//calculate ip checksum
|
|
sum = dhcp_checksum(buf, 20, 0);
|
|
((struct dhcp_ip *)(buf))->ip_checksum[1] = sum & 0xFF;
|
|
((struct dhcp_ip *)(buf))->ip_checksum[0] = (sum >> 8) & 0xFF;
|
|
}
|
|
|
|
void build_ip_header(u8 *buf, u8 *checksum, u8 *src_addr,
|
|
u8 *dst_addr)
|
|
{
|
|
u16 ip_data_size = sizeof(struct dhcp_ip) +
|
|
sizeof(struct dhcp_udp) +
|
|
sizeof(struct dhcp_packet);
|
|
struct dhcp_ip *dhcp_ip_header = (struct dhcp_ip *) buf;
|
|
memset(dhcp_ip_header, 0, sizeof(struct dhcp_ip));
|
|
//fixed version 4 and header len 20byte
|
|
dhcp_ip_header->ip_v_and_hl = 0x45;
|
|
dhcp_ip_header->ip_tos = 0x00;
|
|
//id = 0x01
|
|
dhcp_ip_header->ip_id[0] = 0x00;
|
|
dhcp_ip_header->ip_id[1] = 0x01;
|
|
//fixed ip_len : 278byte
|
|
dhcp_ip_header->ip_len[0] =
|
|
(ip_data_size >> 8) & 0xff;
|
|
dhcp_ip_header->ip_len[1] = ip_data_size & 0xff;
|
|
//offset
|
|
dhcp_ip_header->ip_off[0] = 0x00;
|
|
dhcp_ip_header->ip_off[0] = 0x00;
|
|
//ip time to live
|
|
dhcp_ip_header->ip_ttl = 0x40;
|
|
//fixed udp protocol : 0x11
|
|
dhcp_ip_header->ip_p = 0x11;
|
|
dhcp_ip_header->ip_checksum[0] = checksum[0];
|
|
dhcp_ip_header->ip_checksum[1] = checksum[1];
|
|
memcpy(dhcp_ip_header->ip_src, src_addr, 4);
|
|
memcpy(dhcp_ip_header->ip_dst, dst_addr, 4);
|
|
}
|
|
|
|
void build_udp_header(u8 *buf, u8 *checksum)
|
|
{
|
|
u16 udp_data_size = sizeof(struct dhcp_udp) +
|
|
sizeof(struct dhcp_packet);
|
|
struct dhcp_udp *dhcp_udp_header = (struct dhcp_udp *) buf;
|
|
memset(dhcp_udp_header, 0, sizeof(struct dhcp_udp));
|
|
dhcp_udp_header->src_port[0] = 0x00;
|
|
dhcp_udp_header->src_port[1] = 0x44;
|
|
dhcp_udp_header->dst_port[0] = 0x00;
|
|
dhcp_udp_header->dst_port[1] = 0x43;
|
|
//dhcp request is being hard code : 258byte
|
|
dhcp_udp_header->length[0] =
|
|
(udp_data_size >> 8) & 0xff;
|
|
dhcp_udp_header->length[1] = udp_data_size & 0xff;
|
|
dhcp_udp_header->checksum[0] = checksum[0];
|
|
dhcp_udp_header->checksum[1] = checksum[1];
|
|
}
|
|
|
|
void build_dhcp_request(u8 *buf, u8 *chaddr, u8 *ciaddr, u8 *s_addr)
|
|
{
|
|
struct dhcp_packet *dhcp_pkt_ptr = (struct dhcp_packet *) buf;
|
|
memset(dhcp_pkt_ptr, 0, sizeof(struct dhcp_packet));
|
|
dhcp_pkt_ptr->op = 0x01;
|
|
dhcp_pkt_ptr->htype = 0x01;
|
|
dhcp_pkt_ptr->hlen = 0x06;
|
|
dhcp_pkt_ptr->hops = 0x0;
|
|
dhcp_pkt_ptr->xid[0] = 0x00;
|
|
dhcp_pkt_ptr->xid[1] = 0x00;
|
|
dhcp_pkt_ptr->xid[2] = 0x00;
|
|
dhcp_pkt_ptr->xid[3] = 0x05;
|
|
dhcp_pkt_ptr->secs[0] = 0x0;
|
|
dhcp_pkt_ptr->secs[1] = 0x0;
|
|
//solicit own client IP addr
|
|
memcpy(dhcp_pkt_ptr->ciaddr, ciaddr, 4);
|
|
memcpy(dhcp_pkt_ptr->chaddr, chaddr, 6);
|
|
//fill cookie
|
|
dhcp_pkt_ptr->dhcp_cookie[0] = 0x63;
|
|
dhcp_pkt_ptr->dhcp_cookie[1] = 0x82;
|
|
dhcp_pkt_ptr->dhcp_cookie[2] = 0x53;
|
|
dhcp_pkt_ptr->dhcp_cookie[3] = 0x63;
|
|
//request option
|
|
dhcp_pkt_ptr->dhcp_req_type = 0x35;
|
|
dhcp_pkt_ptr->dhcp_req_len = 0x01;
|
|
dhcp_pkt_ptr->request_code = 0x03;
|
|
//client ID option
|
|
dhcp_pkt_ptr->dhcp_ci_type = 61;
|
|
dhcp_pkt_ptr->dhcp_ci_len = 7;
|
|
dhcp_pkt_ptr->dhcp_ci_hw_type = 0x01;
|
|
memcpy(dhcp_pkt_ptr->dhcp_ci_hw_mac, chaddr, 6);
|
|
//request IP option
|
|
dhcp_pkt_ptr->dhcp_rIP_type = 50;
|
|
dhcp_pkt_ptr->dhcp_rIP_len = 4;
|
|
memcpy(dhcp_pkt_ptr->dhcp_rIP, ciaddr, 4);
|
|
//server ID option
|
|
//dhcp_pkt_ptr->dhcp_server_type = 0x36;
|
|
//dhcp_pkt_ptr->dhcp_s_len = 0x04;
|
|
//memcpy(dhcp_pkt_ptr->server_ip, s_addr, 4);
|
|
dhcp_pkt_ptr->end = 0xff;
|
|
}
|