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;
 | |
| }
 | 
