blob: 2184a684015c6dee2b4b50fa287809e814085cf1 [file] [log] [blame]
/** @file moal_sta_cfg80211.c
*
* @brief This file contains the functions for STA CFG80211.
*
*
* Copyright 2011-2021 NXP
*
* This software file (the File) is distributed by NXP
* under the terms of the GNU General Public License Version 2, June 1991
* (the License). You may use, redistribute and/or modify the File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include "moal_cfg80211.h"
#include "moal_cfg80211_util.h"
#include "moal_sta_cfg80211.h"
#include "moal_eth_ioctl.h"
#ifdef UAP_SUPPORT
#include "moal_uap.h"
#endif
#include <linux/sort.h>
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
extern int fw_region;
#endif
#endif
/* Supported crypto cipher suits to be advertised to cfg80211 */
static const u32 cfg80211_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP,
WLAN_CIPHER_SUITE_SMS4,
WLAN_CIPHER_SUITE_AES_CMAC,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
WLAN_CIPHER_SUITE_BIP_GMAC_128,
WLAN_CIPHER_SUITE_BIP_GMAC_256,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
WLAN_CIPHER_SUITE_GCMP,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
WLAN_CIPHER_SUITE_GCMP_256,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
WLAN_CIPHER_SUITE_CCMP_256,
#endif
};
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
static void
#else
static int
#endif
woal_cfg80211_reg_notifier(struct wiphy *wiphy,
struct regulatory_request *request);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
static int woal_cfg80211_scan(struct wiphy *wiphy,
struct cfg80211_scan_request *request);
#else
static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_scan_request *request);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
static void woal_cfg80211_abort_scan(struct wiphy *wiphy,
struct wireless_dev *wdev);
#endif
static int woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme);
static int woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
t_u16 reason_code);
static int woal_cfg80211_get_station(struct wiphy *wiphy,
struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *mac,
#else
u8 *mac,
#endif
struct station_info *sinfo);
static int woal_cfg80211_dump_station(struct wiphy *wiphy,
struct net_device *dev, int idx,
t_u8 *mac, struct station_info *sinfo);
static int woal_cfg80211_dump_survey(struct wiphy *wiphy,
struct net_device *dev, int idx,
struct survey_info *survey);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int woal_cfg80211_get_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);
#endif
static int woal_cfg80211_set_power_mgmt(struct wiphy *wiphy,
struct net_device *dev, bool enabled,
int timeout);
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
static int woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
struct net_device *dev,
s32 rssi_thold, u32 rssi_hyst);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
static int woal_cfg80211_get_tx_power(struct wiphy *wiphy,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
struct wireless_dev *wdev,
#endif
int *dbm);
static int woal_cfg80211_set_tx_power(struct wiphy *wiphy,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
struct wireless_dev *wdev,
#endif
#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
enum tx_power_setting type,
#else
enum nl80211_tx_power_setting type,
#endif
int dbm);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct wireless_dev *wdev,
#else
struct net_device *dev,
#endif
u64 cookie);
static int
woal_cfg80211_remain_on_channel(struct wiphy *wiphy,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct wireless_dev *wdev,
#else
struct net_device *dev,
#endif
struct ieee80211_channel *chan,
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
enum nl80211_channel_type channel_type,
#endif
unsigned int duration, u64 * cookie);
static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct wireless_dev *wdev,
#else
struct net_device *dev,
#endif
u64 cookie);
#endif /* KERNEL_VERSION */
#ifdef CONFIG_NL80211_TESTMODE
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
static int woal_testmode_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
void *data, int len);
#else
static int woal_testmode_cmd(struct wiphy *wiphy, void *data, int len);
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
int woal_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_sched_scan_request *request);
int woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, u64 reqid
#endif
);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
int woal_cfg80211_resume(struct wiphy *wiphy);
int woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
static void woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
void woal_check_auto_tdls(struct wiphy *wiphy, struct net_device *dev);
int woal_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *peer,
#else
u8 *peer,
#endif
enum nl80211_tdls_operation oper);
int woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *peer,
#else
u8 *peer,
#endif
u8 action_code, u8 dialog_token, u16 status_code,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
u32 peer_capability,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
bool initiator,
#endif
const u8 *extra_ies, size_t extra_ies_len);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
static int woal_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr, u8 oper_class,
struct cfg80211_chan_def *chandef);
void woal_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr);
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
static int woal_cfg80211_change_station(struct wiphy *wiphy,
struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *mac,
#else
u8 *mac,
#endif
struct station_parameters *params);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
int woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int woal_cfg80211_authenticate(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_auth_request *req);
static int woal_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req);
#ifdef UAP_SUPPORT
int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_parameters *params);
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
#ifdef UAP_SUPPORT
static int woal_cfg80211_add_station(struct wiphy *wiphy,
struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *mac,
#else
u8 *mac,
#endif
struct station_parameters *params);
#endif
#endif
static int woal_cfg80211_deauthenticate(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_deauth_request *req);
static int woal_cfg80211_disassociate(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_disassoc_request *req);
/** cfg80211 operations */
static struct cfg80211_ops woal_cfg80211_ops = {
.change_virtual_intf = woal_cfg80211_change_virtual_intf,
.scan = woal_cfg80211_scan,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
.abort_scan = woal_cfg80211_abort_scan,
#endif
.connect = woal_cfg80211_connect,
.disconnect = woal_cfg80211_disconnect,
.deauth = woal_cfg80211_deauthenticate,
.disassoc = woal_cfg80211_disassociate,
.get_station = woal_cfg80211_get_station,
.dump_station = woal_cfg80211_dump_station,
.dump_survey = woal_cfg80211_dump_survey,
.set_wiphy_params = woal_cfg80211_set_wiphy_params,
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0)
.set_channel = woal_cfg80211_set_channel,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
.get_channel = woal_cfg80211_get_channel,
#endif
.add_key = woal_cfg80211_add_key,
.del_key = woal_cfg80211_del_key,
.set_default_key = woal_cfg80211_set_default_key,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
.set_default_mgmt_key = woal_cfg80211_set_default_mgmt_key,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
.set_default_beacon_key = woal_cfg80211_set_default_beacon_key,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
.set_rekey_data = woal_cfg80211_set_rekey_data,
#endif
.set_pmksa = woal_cfg80211_set_pmksa,
.del_pmksa = woal_cfg80211_del_pmksa,
.flush_pmksa = woal_cfg80211_flush_pmksa,
.set_power_mgmt = woal_cfg80211_set_power_mgmt,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
.set_tx_power = woal_cfg80211_set_tx_power,
.get_tx_power = woal_cfg80211_get_tx_power,
#endif
.set_bitrate_mask = woal_cfg80211_set_bitrate_mask,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
.sched_scan_start = woal_cfg80211_sched_scan_start,
.sched_scan_stop = woal_cfg80211_sched_scan_stop,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
.suspend = woal_cfg80211_suspend,
.resume = woal_cfg80211_resume,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
.set_wakeup = woal_cfg80211_set_wakeup,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
.set_antenna = woal_cfg80211_set_antenna,
.get_antenna = woal_cfg80211_get_antenna,
#endif
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
.set_cqm_rssi_config = woal_cfg80211_set_cqm_rssi_config,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
.tdls_oper = woal_cfg80211_tdls_oper,
.tdls_mgmt = woal_cfg80211_tdls_mgmt,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
.tdls_channel_switch =
woal_cfg80211_tdls_channel_switch,.tdls_cancel_channel_switch =
woal_cfg80211_tdls_cancel_channel_switch,
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
.change_station = woal_cfg80211_change_station,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
.update_ft_ies = woal_cfg80211_update_ft_ies,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
.set_qos_map = woal_cfg80211_set_qos_map,
#endif
#ifdef UAP_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
.set_coalesce = woal_cfg80211_set_coalesce,
#endif
.add_virtual_intf =
woal_cfg80211_add_virtual_intf,.del_virtual_intf =
woal_cfg80211_del_virtual_intf,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
.start_ap = woal_cfg80211_add_beacon,.change_beacon =
woal_cfg80211_set_beacon,.stop_ap = woal_cfg80211_del_beacon,
#else
.add_beacon = woal_cfg80211_add_beacon,.set_beacon =
woal_cfg80211_set_beacon,.del_beacon = woal_cfg80211_del_beacon,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
.change_bss = woal_cfg80211_change_bss,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
.add_station = woal_cfg80211_add_station,
#endif
.del_station = woal_cfg80211_del_station,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
.set_txq_params = woal_cfg80211_set_txq_params,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
.set_mac_acl = woal_cfg80211_set_mac_acl,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
.start_radar_detection =
woal_cfg80211_start_radar_detection,.channel_switch =
woal_cfg80211_channel_switch,
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
.update_mgmt_frame_registrations =
woal_cfg80211_mgmt_frame_register,
#else
.mgmt_frame_register = woal_cfg80211_mgmt_frame_register,
#endif
.mgmt_tx = woal_cfg80211_mgmt_tx,
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
.mgmt_tx_cancel_wait =
woal_cfg80211_mgmt_tx_cancel_wait,.remain_on_channel =
woal_cfg80211_remain_on_channel,.cancel_remain_on_channel =
woal_cfg80211_cancel_remain_on_channel,
#endif
#ifdef CONFIG_NL80211_TESTMODE
.testmode_cmd = woal_testmode_cmd,
#endif
};
/** Region code mapping */
typedef struct _region_code_t {
/** Region */
t_u8 region[COUNTRY_CODE_LEN];
} region_code_t;
static const struct ieee80211_regdomain mrvl_regdom = {
.n_reg_rules = 4,
.alpha2 = "99",
.reg_rules = {
/* IEEE 802.11b/g, channels 1..11 */
REG_RULE(2412 - 10, 2472 + 10, 40, 6, 20, 0),
/* If any */
/* IEEE 802.11 channel 14 - Only JP enables
* this and for 802.11b only
*/
REG_RULE(2484 - 10, 2484 + 10, 20, 6, 20, 0),
/* IEEE 802.11a, channel 36..64 */
REG_RULE(5150 - 10, 5350 + 10, 80, 6, 20, 0),
/* IEEE 802.11a, channel 100..165 */
REG_RULE(5470 - 10, 5850 + 10, 80, 6, 20, 0),
}
};
#define AUTH_TX_DEFAULT_WAIT_TIME 2400
/********************************************************
Local Variables
********************************************************/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
// clang-format off
static const struct ieee80211_txrx_stypes
ieee80211_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
.tx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
MBIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
MBIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = MBIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
MBIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
MBIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
MBIT(IEEE80211_STYPE_DISASSOC >> 4) |
MBIT(IEEE80211_STYPE_AUTH >> 4) |
MBIT(IEEE80211_STYPE_DEAUTH >> 4) |
MBIT(IEEE80211_STYPE_ACTION >> 4),
},
[NL80211_IFTYPE_AP_VLAN] = {
.tx = 0x0000,
.rx = 0x0000,
},
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
MBIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
MBIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_P2P_GO] = {
.tx = MBIT(IEEE80211_STYPE_ACTION >> 4) |
MBIT(IEEE80211_STYPE_AUTH >> 4) |
MBIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
MBIT(IEEE80211_STYPE_REASSOC_RESP >> 4) |
MBIT(IEEE80211_STYPE_PROBE_RESP >> 4),
.rx = MBIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
MBIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
MBIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
MBIT(IEEE80211_STYPE_DISASSOC >> 4) |
MBIT(IEEE80211_STYPE_AUTH >> 4) |
MBIT(IEEE80211_STYPE_DEAUTH >> 4) |
MBIT(IEEE80211_STYPE_ACTION >> 4),
},
#endif
#endif
[NL80211_IFTYPE_MESH_POINT] = {
.tx = 0x0000,
.rx = 0x0000,
},
};
// clang-format on
#endif
#if CFG80211_VERSION_CODE > KERNEL_VERSION(3, 0, 0)
/**
* NOTE: types in all the sets must be equals to the
* initial value of wiphy->interface_modes
*/
static const struct ieee80211_iface_limit cfg80211_ap_sta_limits[] = {
{.max = 4,
.types = MBIT(NL80211_IFTYPE_STATION)
#ifdef UAP_CFG80211
| MBIT(NL80211_IFTYPE_AP)
#endif
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
| MBIT(NL80211_IFTYPE_P2P_GO) | MBIT(NL80211_IFTYPE_P2P_CLIENT)
#endif
#endif
}
};
static struct ieee80211_iface_combination cfg80211_iface_comb_ap_sta = {
.limits = cfg80211_ap_sta_limits,
.num_different_channels = 1,
.n_limits = ARRAY_SIZE(cfg80211_ap_sta_limits),
.max_interfaces = 4,
.beacon_int_infra_match = MTRUE,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
.radar_detect_widths =
MBIT(NL80211_CHAN_WIDTH_20_NOHT) | MBIT(NL80211_CHAN_WIDTH_20),
#endif
};
#endif
extern pmoal_handle m_handle[];
#ifdef CONFIG_PM
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
static const struct wiphy_wowlan_support wowlan_support = {
.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT,
.n_patterns = MAX_NUM_FILTERS,
.pattern_min_len = 1,
.pattern_max_len = WOWLAN_MAX_PATTERN_LEN,
.max_pkt_offset = WOWLAN_MAX_OFFSET_LEN,
};
static const struct wiphy_wowlan_support wowlan_support_with_gtk = {
.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT |
WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
WIPHY_WOWLAN_GTK_REKEY_FAILURE,
.n_patterns = MAX_NUM_FILTERS,
.pattern_min_len = 1,
.pattern_max_len = WOWLAN_MAX_PATTERN_LEN,
.max_pkt_offset = WOWLAN_MAX_OFFSET_LEN,
};
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
static const struct wiphy_coalesce_support coalesce_support = {
.n_rules = COALESCE_MAX_RULES,
.max_delay = MAX_COALESCING_DELAY,
.n_patterns = COALESCE_MAX_FILTERS,
.pattern_min_len = 1,
.pattern_max_len = MAX_PATTERN_LEN,
.max_pkt_offset = MAX_OFFSET_LEN,
};
#endif
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
/**
* @brief This function check cfg80211 special region code.
*
* @param region_string Region string
*
* @return MTRUE/MFALSE
*/
t_u8
is_cfg80211_special_region_code(t_u8 *region_string)
{
t_u8 i;
region_code_t cfg80211_special_region_code[] = {
{"00 "}, {"99 "}, {"98 "}, {"97 "}
};
for (i = 0; i < COUNTRY_CODE_LEN && region_string[i]; i++)
region_string[i] = toupper(region_string[i]);
for (i = 0; i < ARRAY_SIZE(cfg80211_special_region_code); i++) {
if (!memcmp(region_string,
cfg80211_special_region_code[i].region,
COUNTRY_CODE_LEN)) {
PRINTM(MIOCTL, "special region code=%s\n",
region_string);
return MTRUE;
}
}
return MFALSE;
}
/**
* @brief Get the encryption mode from cipher
*
* @param cipher Cipher cuite
* @param wpa_enabled WPA enable or disable
*
* @return MLAN_ENCRYPTION_MODE_*
*/
static int
woal_cfg80211_get_encryption_mode(t_u32 cipher, int *wpa_enabled)
{
int encrypt_mode;
ENTER();
*wpa_enabled = 0;
switch (cipher) {
case MW_AUTH_CIPHER_NONE:
encrypt_mode = MLAN_ENCRYPTION_MODE_NONE;
break;
case WLAN_CIPHER_SUITE_WEP40:
encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40;
break;
case WLAN_CIPHER_SUITE_WEP104:
encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104;
break;
case WLAN_CIPHER_SUITE_TKIP:
encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP;
*wpa_enabled = 1;
break;
case WLAN_CIPHER_SUITE_CCMP:
encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP;
*wpa_enabled = 1;
break;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
case WLAN_CIPHER_SUITE_CCMP_256:
encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP_256;
*wpa_enabled = 1;
break;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
case WLAN_CIPHER_SUITE_GCMP:
encrypt_mode = MLAN_ENCRYPTION_MODE_GCMP;
*wpa_enabled = 1;
break;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
case WLAN_CIPHER_SUITE_GCMP_256:
encrypt_mode = MLAN_ENCRYPTION_MODE_GCMP_256;
*wpa_enabled = 1;
break;
#endif
default:
encrypt_mode = -1;
}
LEAVE();
return encrypt_mode;
}
/**
* @brief get associate failure status code
*
* @param priv Pointer to the moal_private driver data struct
*
* @return IEEE status code
*/
static int
woal_get_assoc_status(moal_private *priv)
{
int ret = WLAN_STATUS_UNSPECIFIED_FAILURE;
t_u16 status = (t_u16)(priv->assoc_status & 0xffff);
t_u16 cap = (t_u16)(priv->assoc_status >> 16);
switch (cap) {
case 0xfffd:
case 0xfffe:
ret = status;
break;
case 0xfffc:
ret = WLAN_STATUS_AUTH_TIMEOUT;
break;
default:
break;
}
PRINTM(MCMND, "Assoc fail: status=%d, cap=0x%x, IEEE status=%d\n",
status, cap, ret);
return ret;
}
/**
* @brief Check the pairwise or group cipher for
* WEP enabled or not
*
* @param cipher MLAN Cipher cuite
*
* @return 1 -- enable or 0 -- disable
*/
static int
woal_cfg80211_is_alg_wep(t_u32 cipher)
{
int alg = 0;
ENTER();
if (cipher == MLAN_ENCRYPTION_MODE_WEP40 ||
cipher == MLAN_ENCRYPTION_MODE_WEP104)
alg = 1;
LEAVE();
return alg;
}
/**
* @brief Convert NL80211 interface type to MLAN_BSS_MODE_*
*
* @param iftype Interface type of NL80211
*
* @return Driver bss mode
*/
static t_u32
woal_nl80211_iftype_to_mode(enum nl80211_iftype iftype)
{
switch (iftype) {
case NL80211_IFTYPE_STATION:
return MLAN_BSS_MODE_INFRA;
case NL80211_IFTYPE_UNSPECIFIED:
default:
return MLAN_BSS_MODE_AUTO;
}
}
/**
* @brief Control WPS Session Enable/Disable
*
* @param priv Pointer to the moal_private driver data struct
* @param enable enable/disable flag
*
* @return 0 --success, otherwise fail
*/
static int
woal_wps_cfg(moal_private *priv, int enable)
{
int ret = 0;
mlan_ds_wps_cfg *pwps = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
PRINTM(MINFO, "WOAL_WPS_SESSION\n");
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
pwps = (mlan_ds_wps_cfg *)req->pbuf;
req->req_id = MLAN_IOCTL_WPS_CFG;
req->action = MLAN_ACT_SET;
pwps->sub_command = MLAN_OID_WPS_CFG_SESSION;
if (enable)
pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START;
else
pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END;
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief configure ASSOC IE
*
* @param priv A pointer to moal private structure
* @param ie A pointer to ie data
* @param ie_len The length of ie data
* @param wait_option wait option
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_assoc_ies_cfg(moal_private *priv, t_u8 *ie, int ie_len,
t_u8 wait_option)
{
int bytes_left = ie_len;
t_u8 *pcurrent_ptr = ie;
int total_ie_len;
t_u8 element_len;
int ret = MLAN_STATUS_SUCCESS;
IEEEtypes_ElementId_e element_id;
IEEEtypes_VendorSpecific_t *pvendor_ie;
t_u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 };
t_u8 hs20_oui[] = { 0x50, 0x6f, 0x9a, 0x10 };
while (bytes_left >= 2) {
element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr));
element_len = *((t_u8 *)pcurrent_ptr + 1);
total_ie_len = element_len + sizeof(IEEEtypes_Header_t);
if (bytes_left < total_ie_len) {
PRINTM(MERROR,
"InterpretIE: Error in processing IE, bytes left < IE length\n");
bytes_left = 0;
continue;
}
switch (element_id) {
case RSN_IE:
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR, "Fail to set RSN IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL, "Set RSN IE\n");
break;
case VENDOR_SPECIFIC_221:
pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr;
if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui,
sizeof(pvendor_ie->vend_hdr.oui)) &&
(pvendor_ie->vend_hdr.oui_type == wps_oui[3])) {
PRINTM(MIOCTL, "Enable WPS session\n");
woal_wps_cfg(priv, MTRUE);
}
if (!memcmp(pvendor_ie->vend_hdr.oui, hs20_oui,
sizeof(pvendor_ie->vend_hdr.oui)) &&
(pvendor_ie->vend_hdr.oui_type == hs20_oui[3])) {
PRINTM(MIOCTL,
"Hotspot2.0 is enabled for this bss\n");
if (MLAN_STATUS_SUCCESS !=
woal_set_hotspotcfg(priv, wait_option,
(HOTSPOT_BY_SUPPLICANT |
HOTSPOT_ENABLED))) {
PRINTM(MERROR,
"Fail to enable hotspot 2.0\n");
ret = -EFAULT;
goto done;
}
}
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR,
"Fail to Set VENDOR SPECIFIC IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL,
"Set VENDOR SPECIFIC IE, OUI: %02x:%02x:%02x:%02x\n",
pvendor_ie->vend_hdr.oui[0],
pvendor_ie->vend_hdr.oui[1],
pvendor_ie->vend_hdr.oui[2],
pvendor_ie->vend_hdr.oui_type);
break;
case MOBILITY_DOMAIN:
break;
case FAST_BSS_TRANSITION:
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR, "Fail to set"
"FAST_BSS_TRANSITION IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL, "Set FAST_BSS_TRANSITION IE\n");
break;
case RIC:
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR,
"Fail to set"
"RESOURCE INFORMATION CONTAINER IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL,
"Set RESOURCE INFORMATION CONTAINER IE\n");
break;
case EXT_CAPABILITY:
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR,
"Fail to set Extended Capabilites IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL, "Set Extended Capabilities IE\n");
break;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
case EXTENSION:
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR, "Fail to set Extension IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL, "Set Extension IE\n");
break;
case FRAGMENT:
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR, "Fail to set Fragmented IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL, "Set Fragmented IE\n");
break;
#endif
default:
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET,
pcurrent_ptr, &total_ie_len,
wait_option)) {
PRINTM(MERROR, "Fail to set GEN IE\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL, "Set GEN IE\n");
break;
}
pcurrent_ptr += element_len + 2;
/* Need to account for IE ID and IE Len */
bytes_left -= (element_len + 2);
}
done:
return ret;
}
#ifdef CONFIG_NL80211_TESTMODE
enum moal_tm_attr {
__MOAL_TM_ATTR_INVALID = 0,
MOAL_TM_ATTR_CMD = 1,
MOAL_TM_ATTR_DATA = 2,
/* keep last */
__MOAL_TM_ATTR_AFTER_LAST,
MOAL_TM_ATTR_MAX = __MOAL_TM_ATTR_AFTER_LAST - 1,
};
static const struct nla_policy moal_tm_policy[MOAL_TM_ATTR_MAX + 1] = {
[MOAL_TM_ATTR_CMD] = {.type = NLA_U32},
[MOAL_TM_ATTR_DATA] = {.type = NLA_BINARY,
.len = MRVDRV_SIZE_OF_CMD_BUFFER},
};
enum moal_tm_command {
MOAL_TM_CMD_HOSTCMD = 0,
};
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
static int
woal_testmode_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
void *data, int len)
#else
static int
woal_testmode_cmd(struct wiphy *wiphy, void *data, int len)
#endif
{
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
moal_private *priv =
(moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
mlan_ioctl_req *req = NULL;
mlan_ds_misc_cfg *misc_cfg = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
struct nlattr *tb[MOAL_TM_ATTR_MAX + 1];
struct sk_buff *skb;
int err;
if (!priv)
return -EINVAL;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
err = nla_parse(tb, MOAL_TM_ATTR_MAX, data, len, moal_tm_policy, NULL);
#else
err = nla_parse(tb, MOAL_TM_ATTR_MAX, data, len, moal_tm_policy);
#endif
if (err)
return err;
if (!tb[MOAL_TM_ATTR_CMD])
return -EINVAL;
switch (nla_get_u32(tb[MOAL_TM_ATTR_CMD])) {
case MOAL_TM_CMD_HOSTCMD:
if (!tb[MOAL_TM_ATTR_DATA])
return -EINVAL;
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL)
return -ENOMEM;
misc_cfg = (mlan_ds_misc_cfg *)req->pbuf;
misc_cfg->sub_command = MLAN_OID_MISC_HOST_CMD;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_SET;
misc_cfg->param.hostcmd.len = nla_len(tb[MOAL_TM_ATTR_DATA]);
moal_memcpy_ext(priv->phandle, misc_cfg->param.hostcmd.cmd,
nla_data(tb[MOAL_TM_ATTR_DATA]),
misc_cfg->param.hostcmd.len,
MRVDRV_SIZE_OF_CMD_BUFFER);
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
err = -EFAULT;
goto error;
}
/* process hostcmd response */
skb = cfg80211_testmode_alloc_reply_skb(wiphy,
misc_cfg->param.hostcmd.
len);
if (!skb) {
kfree(req);
return -ENOMEM;
}
err = nla_put(skb, MOAL_TM_ATTR_DATA,
misc_cfg->param.hostcmd.len,
misc_cfg->param.hostcmd.cmd);
if (err) {
kfree(req);
kfree_skb(skb);
return -EMSGSIZE;
}
err = cfg80211_testmode_reply(skb);
kfree(req);
return err;
default:
return -EOPNOTSUPP;
}
error:
if (status != MLAN_STATUS_PENDING)
kfree(req);
return err;
}
#endif
/**
* @brief Send domain info command to FW
*
* @param priv A pointer to moal_private structure
* @param wait_option wait option
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
woal_send_domain_info_cmd_fw(moal_private *priv, t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
enum ieee80211_band band;
struct ieee80211_supported_band *sband = NULL;
struct ieee80211_channel *channel = NULL;
t_u8 no_of_sub_band = 0;
t_u8 no_of_parsed_chan = 0;
t_u8 first_chan = 0, next_chan = 0, max_pwr = 0;
t_u8 i, flag = 0;
mlan_ds_11d_cfg *cfg_11d = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (!priv->wdev || !priv->wdev->wiphy) {
PRINTM(MERROR, "No wdev or wiphy in priv\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
band = priv->phandle->band;
if (!priv->wdev->wiphy->bands[band]) {
PRINTM(MERROR, "11D: setting domain info in FW failed band=%d",
band);
ret = MLAN_STATUS_FAILURE;
goto done;
}
PRINTM(MCMD_D, "Send domain info: country=%c%c band=%d\n",
priv->phandle->country_code[0], priv->phandle->country_code[1],
band);
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
cfg_11d = (mlan_ds_11d_cfg *)req->pbuf;
cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO_EXT;
req->req_id = MLAN_IOCTL_11D_CFG;
req->action = MLAN_ACT_SET;
if (is_cfg80211_special_region_code(priv->phandle->country_code)) {
/* Set country code */
cfg_11d->param.domain_info.country_code[0] = 'W';
cfg_11d->param.domain_info.country_code[1] = 'W';
} else {
/* Set country code */
cfg_11d->param.domain_info.country_code[0] =
priv->phandle->country_code[0];
cfg_11d->param.domain_info.country_code[1] =
priv->phandle->country_code[1];
}
cfg_11d->param.domain_info.country_code[2] = ' ';
cfg_11d->param.domain_info.band = band;
sband = priv->wdev->wiphy->bands[band];
for (i = 0; (i < sband->n_channels) &&
(no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D); i++) {
channel = &sband->channels[i];
if (channel->flags & IEEE80211_CHAN_DISABLED)
continue;
if (!flag) {
flag = 1;
next_chan = first_chan = (t_u32)channel->hw_value;
max_pwr = channel->max_power;
no_of_parsed_chan = 1;
continue;
}
if (channel->hw_value == next_chan + 1 &&
channel->max_power == max_pwr) {
next_chan++;
no_of_parsed_chan++;
} else {
cfg_11d->param.domain_info.sub_band[no_of_sub_band]
.first_chan = first_chan;
cfg_11d->param.domain_info.sub_band[no_of_sub_band]
.no_of_chan = no_of_parsed_chan;
cfg_11d->param.domain_info.sub_band[no_of_sub_band]
.max_tx_pwr = max_pwr;
no_of_sub_band++;
next_chan = first_chan = (t_u32)channel->hw_value;
max_pwr = channel->max_power;
no_of_parsed_chan = 1;
}
}
if (flag && (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D)) {
cfg_11d->param.domain_info.sub_band[no_of_sub_band].first_chan =
first_chan;
cfg_11d->param.domain_info.sub_band[no_of_sub_band].no_of_chan =
no_of_parsed_chan;
cfg_11d->param.domain_info.sub_band[no_of_sub_band].max_tx_pwr =
max_pwr;
no_of_sub_band++;
}
cfg_11d->param.domain_info.no_of_sub_band = no_of_sub_band;
PRINTM(MCMND, "CFG80211: Country=%c%c, band=%d, no_of_sub_band=%d\n",
priv->phandle->country_code[0], priv->phandle->country_code[1],
priv->phandle->band, cfg_11d->param.domain_info.no_of_sub_band);
/* Send domain info command to FW */
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = MLAN_STATUS_FAILURE;
PRINTM(MERROR, "11D: Error setting domain info in FW\n");
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Request the driver to change the channel and
* change domain info according to that channel
*
* @param priv A pointer to moal_private structure
* @param chan A pointer to ieee80211_channel structure
* @param channel_type Channel type of nl80211_channel_type
* @param wait_option wait option
*
* @return 0 -- success, otherwise fail
*/
int
woal_set_rf_channel(moal_private *priv, struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type, t_u8 wait_option)
{
int ret = 0;
t_u32 mode, config_bands = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_radio_cfg *radio_cfg = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (!chan) {
LEAVE();
return -EINVAL;
}
mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype);
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
radio_cfg = (mlan_ds_radio_cfg *)req->pbuf;
radio_cfg->sub_command = MLAN_OID_BAND_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
/* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN
*/
req->action = MLAN_ACT_GET;
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
req->action = MLAN_ACT_SET;
priv->phandle->band = chan->band;
/* Set appropriate bands */
if (chan->band == IEEE80211_BAND_2GHZ)
config_bands = BAND_B | BAND_G | BAND_GN;
else {
config_bands = BAND_AN | BAND_A;
}
if (mode == MLAN_BSS_MODE_IBSS) {
radio_cfg->param.band_cfg.adhoc_start_band = config_bands;
radio_cfg->param.band_cfg.adhoc_channel =
ieee80211_frequency_to_channel(chan->center_freq);
}
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
woal_send_domain_info_cmd_fw(priv, wait_option);
PRINTM(MINFO, "Setting band %d, and mode = %d channel=%d\n",
config_bands, mode,
ieee80211_frequency_to_channel(chan->center_freq));
if (MLAN_STATUS_SUCCESS !=
woal_change_adhoc_chan(priv,
ieee80211_frequency_to_channel(chan->
center_freq),
wait_option)) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Set ewpa mode
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param ssid_bssid A pointer to mlan_ssid_bssid structure
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --
* success, otherwise fail
*/
static mlan_status
woal_set_ewpa_mode(moal_private *priv, t_u8 wait_option,
mlan_ssid_bssid *ssid_bssid)
{
mlan_ioctl_req *req = NULL;
mlan_ds_sec_cfg *sec = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (!priv->phandle->card_info->embedded_supp)
goto error;
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg));
if (req == NULL) {
status = MLAN_STATUS_FAILURE;
goto error;
}
/* Fill request buffer */
sec = (mlan_ds_sec_cfg *)req->pbuf;
sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE;
req->req_id = MLAN_IOCTL_SEC_CFG;
req->action = MLAN_ACT_GET;
/* Try Get All */
memset(&sec->param.passphrase, 0, sizeof(mlan_ds_passphrase));
moal_memcpy_ext(priv->phandle, &sec->param.passphrase.ssid,
&ssid_bssid->ssid, sizeof(sec->param.passphrase.ssid),
sizeof(sec->param.passphrase.ssid));
moal_memcpy_ext(priv->phandle, &sec->param.passphrase.bssid,
&ssid_bssid->bssid, MLAN_MAC_ADDR_LENGTH,
sizeof(sec->param.passphrase.bssid));
sec->param.passphrase.psk_type = MLAN_PSK_QUERY;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS)
goto error;
sec->param.ewpa_enabled = MFALSE;
if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) {
if (sec->param.passphrase.psk.passphrase.passphrase_len > 0)
sec->param.ewpa_enabled = MTRUE;
} else if (sec->param.passphrase.psk_type == MLAN_PSK_PMK)
sec->param.ewpa_enabled = MTRUE;
sec->sub_command = MLAN_OID_SEC_CFG_EWPA_ENABLED;
req->action = MLAN_ACT_SET;
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, wait_option);
error:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return status;
}
/**
* @brief Set encryption mode and enable WPA
*
* @param priv A pointer to moal_private structure
* @param encrypt_mode Encryption mode
* @param wpa_enabled WPA enable or not
* @param wait_option wait option
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_set_auth(moal_private *priv, int encrypt_mode,
int wpa_enabled, t_u8 wait_option)
{
int ret = 0;
ENTER();
if (MLAN_STATUS_SUCCESS !=
woal_set_encrypt_mode(priv, wait_option, encrypt_mode))
ret = -EFAULT;
if (wpa_enabled) {
if (MLAN_STATUS_SUCCESS !=
woal_set_wpa_enable(priv, wait_option, 1))
ret = -EFAULT;
}
LEAVE();
return ret;
}
/**
* @brief Informs the CFG802.11 subsystem of a new BSS connection.
*
* The following information are sent to the CFG802.11 subsystem
* to register the new BSS connection. If we do not register the new BSS,
* a kernel panic will result.
* - MAC address
* - Capabilities
* - Beacon period
* - RSSI value
* - Channel
* - Supported rates IE
* - Extended capabilities IE
* - DS parameter set IE
* - HT Capability IE
* - Vendor Specific IE (221)
* - WPA IE
* - RSN IE
*
* @param priv A pointer to moal_private structure
* @param ssid_bssid A pointer to A pointer to mlan_ssid_bssid structure
* @param wait_option wait_option
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
woal_inform_bss_from_scan_result(moal_private *priv,
mlan_ssid_bssid *ssid_bssid, t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
struct ieee80211_channel *chan;
mlan_scan_resp scan_resp;
BSSDescriptor_t *scan_table;
t_u64 ts = 0;
u16 cap_info = 0;
int i = 0;
struct cfg80211_bss *pub = NULL;
ENTER();
if (!priv->wdev || !priv->wdev->wiphy) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
memset(&scan_resp, 0, sizeof(scan_resp));
if (MLAN_STATUS_SUCCESS !=
woal_get_scan_table(priv, wait_option, &scan_resp)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (scan_resp.num_in_scan_table) {
scan_table = (BSSDescriptor_t *)scan_resp.pscan_table;
for (i = 0; i < (int)scan_resp.num_in_scan_table; i++) {
if (ssid_bssid) {
/* Inform specific BSS only */
if (memcmp(ssid_bssid->ssid.ssid,
scan_table[i].ssid.ssid,
ssid_bssid->ssid.ssid_len) ||
memcmp(ssid_bssid->bssid,
scan_table[i].mac_address, ETH_ALEN))
continue;
}
if (!scan_table[i].freq) {
scan_table[i].freq =
ieee80211_channel_to_frequency((int)
scan_table
[i].
channel
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
,
woal_band_cfg_to_ieee_band
(scan_table
[i].
bss_band)
#endif
);
}
chan = ieee80211_get_channel(priv->wdev->wiphy,
scan_table[i].freq);
if (!chan) {
PRINTM(MCMND,
"Fail to get chan with freq: channel=%d freq=%d\n",
(int)scan_table[i].channel,
(int)scan_table[i].freq);
continue;
}
#if defined(WIFI_DIRECT_SUPPORT)
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
!ssid_bssid) {
if (!strncmp(scan_table[i].ssid.ssid, "DIRECT-",
strlen("DIRECT-"))) {
PRINTM(MCMND,
"wlan: P2P device " MACSTR
" found, channel=%d\n",
MAC2STR(scan_table[i]
.mac_address),
(int)chan->hw_value);
}
}
#endif
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
/** Andorid's Location service is expecting timestamp to
* be local time (in microsecond) since boot; and not
* the TSF found in the beacon. */
ts = ktime_to_us(ktime_get_boottime());
#else
moal_memcpy_ext(priv->phandle, &ts,
scan_table[i].time_stamp, sizeof(ts),
sizeof(ts));
#endif
moal_memcpy_ext(priv->phandle, &cap_info,
&scan_table[i].cap_info,
sizeof(cap_info), sizeof(cap_info));
pub = cfg80211_inform_bss(priv->wdev->wiphy, chan,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
CFG80211_BSS_FTYPE_UNKNOWN,
#endif
scan_table[i].mac_address, ts,
cap_info,
scan_table[i].beacon_period,
scan_table[i].pbeacon_buf +
WLAN_802_11_FIXED_IE_SIZE,
scan_table[i].
beacon_buf_size -
WLAN_802_11_FIXED_IE_SIZE,
-RSSI_DBM_TO_MDM(scan_table
[i].rssi),
GFP_KERNEL);
if (pub) {
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
pub->len_information_elements =
pub->len_beacon_ies;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(priv->wdev->wiphy, pub);
#else
cfg80211_put_bss(pub);
#endif
}
}
}
done:
LEAVE();
return ret;
}
/**
* @brief Informs the CFG802.11 subsystem of a new IBSS connection.
*
* The following information are sent to the CFG802.11 subsystem
* to register the new IBSS connection. If we do not register the
* new IBSS, a kernel panic will result.
* - MAC address
* - Capabilities
* - Beacon period
* - RSSI value
* - Channel
* - Supported rates IE
* - Extended capabilities IE
* - DS parameter set IE
* - HT Capability IE
* - Vendor Specific IE (221)
* - WPA IE
* - RSN IE
*
* @param priv A pointer to moal_private structure
* @param cahn A pointer to ieee80211_channel structure
* @param beacon_interval Beacon interval
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
woal_cfg80211_inform_ibss_bss(moal_private *priv,
struct ieee80211_channel *chan,
t_u16 beacon_interval)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_bss_info bss_info;
mlan_ds_get_signal signal;
t_u8 ie_buf[MLAN_MAX_SSID_LENGTH + sizeof(IEEEtypes_Header_t)];
int ie_len = 0;
struct cfg80211_bss *bss = NULL;
ENTER();
ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
if (ret)
goto done;
memset(ie_buf, 0, sizeof(ie_buf));
ie_buf[0] = WLAN_EID_SSID;
ie_buf[1] = bss_info.ssid.ssid_len;
moal_memcpy_ext(priv->phandle, &ie_buf[sizeof(IEEEtypes_Header_t)],
&bss_info.ssid.ssid, bss_info.ssid.ssid_len,
sizeof(ie_buf) - sizeof(IEEEtypes_Header_t));
ie_len = ie_buf[1] + sizeof(IEEEtypes_Header_t);
/* Get signal information from the firmware */
memset(&signal, 0, sizeof(mlan_ds_get_signal));
if (MLAN_STATUS_SUCCESS !=
woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
PRINTM(MERROR, "Error getting signal information\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
bss = cfg80211_inform_bss(priv->wdev->wiphy, chan,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
CFG80211_BSS_FTYPE_UNKNOWN,
#endif
bss_info.bssid, 0, WLAN_CAPABILITY_IBSS,
beacon_interval, ie_buf, ie_len,
signal.bcn_rssi_avg, GFP_KERNEL);
if (bss)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(priv->wdev->wiphy, bss);
#else
cfg80211_put_bss(bss);
#endif
done:
LEAVE();
return ret;
}
/**
* @brief Process country IE before assoicate
*
* @param priv A pointer to moal_private structure
* @param bss A pointer to cfg80211_bss structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_process_country_ie(moal_private *priv, struct cfg80211_bss *bss)
{
u8 *country_ie, country_ie_len;
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_11d_cfg *cfg_11d = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
rcu_read_lock();
country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
if (!country_ie) {
rcu_read_unlock();
PRINTM(MIOCTL, "No country IE found!\n");
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
LEAVE();
return 0;
}
country_ie_len = country_ie[1];
if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) {
rcu_read_unlock();
PRINTM(MIOCTL, "Wrong Country IE length!\n");
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
LEAVE();
return 0;
}
priv->phandle->country_code[0] = country_ie[2];
priv->phandle->country_code[1] = country_ie[3];
priv->phandle->country_code[2] = ' ';
if (is_cfg80211_special_region_code(priv->phandle->country_code)) {
rcu_read_unlock();
PRINTM(MIOCTL, "Skip special region code in CountryIE");
LEAVE();
return 0;
}
if (MLAN_STATUS_SUCCESS !=
woal_set_region_code(priv, priv->phandle->country_code))
PRINTM(MERROR, "Set country code failed!\n");
/* Allocate an IOCTL request buffer */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg));
if (req == NULL) {
rcu_read_unlock();
PRINTM(MERROR, "Fail to allocate mlan_ds_11d_cfg buffer\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
cfg_11d = (mlan_ds_11d_cfg *)req->pbuf;
cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO_EXT;
req->req_id = MLAN_IOCTL_11D_CFG;
req->action = MLAN_ACT_SET;
/* Set country code */
cfg_11d->param.domain_info.country_code[0] =
priv->phandle->country_code[0];
cfg_11d->param.domain_info.country_code[1] =
priv->phandle->country_code[1];
cfg_11d->param.domain_info.country_code[2] = ' ';
/** IEEE80211_BAND_2GHZ or IEEE80211_BAND_5GHZ */
cfg_11d->param.domain_info.band = priv->phandle->band;
country_ie_len -= COUNTRY_CODE_LEN;
cfg_11d->param.domain_info.no_of_sub_band =
MIN(MRVDRV_MAX_SUBBAND_802_11D,
(country_ie_len /
sizeof(struct ieee80211_country_ie_triplet)));
moal_memcpy_ext(priv->phandle,
(u8 *)cfg_11d->param.domain_info.sub_band,
&country_ie[2] + COUNTRY_CODE_LEN,
cfg_11d->param.domain_info.no_of_sub_band *
sizeof(mlan_ds_subband_set_t),
sizeof(cfg_11d->param.domain_info.sub_band));
PRINTM(MCMND, "11D: Country IE: %c%c band=%d no_of_sub_band=%d\n",
country_ie[2], country_ie[3], priv->phandle->band,
cfg_11d->param.domain_info.no_of_sub_band);
rcu_read_unlock();
/* Send domain info command to FW */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = MLAN_STATUS_FAILURE;
PRINTM(MERROR, "11D: Error setting domain info in FW\n");
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Request scan based on connect parameter
*
* @param priv A pointer to moal_private structure
* @param conn_param A pointer to connect parameters
* @param wait_option wait option
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_connect_scan(moal_private *priv,
struct cfg80211_connect_params *conn_param,
t_u8 wait_option)
{
moal_handle *handle = priv->phandle;
int ret = 0;
wlan_user_scan_cfg scan_req;
enum ieee80211_band band;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
int chan_idx = 0, i;
ENTER();
if (handle->scan_pending_on_block == MTRUE) {
PRINTM(MINFO, "scan already in processing...\n");
LEAVE();
return ret;
}
#ifdef REASSOCIATION
if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) {
PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n");
LEAVE();
return -EBUSY;
}
#endif /* REASSOCIATION */
priv->report_scan_result = MTRUE;
memset(&scan_req, 0x00, sizeof(scan_req));
moal_memcpy_ext(priv->phandle, scan_req.ssid_list[0].ssid,
conn_param->ssid, conn_param->ssid_len,
sizeof(scan_req.ssid_list[0].ssid));
scan_req.ssid_list[0].max_len = 0;
if (conn_param->channel) {
scan_req.chan_list[0].chan_number =
conn_param->channel->hw_value;
scan_req.chan_list[0].radio_type = conn_param->channel->band;
if (conn_param->channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
scan_req.chan_list[0].scan_type =
MLAN_SCAN_TYPE_PASSIVE;
else if (conn_param->channel->flags & IEEE80211_CHAN_RADAR)
scan_req.chan_list[0].scan_type =
MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
else
scan_req.chan_list[0].scan_type = MLAN_SCAN_TYPE_ACTIVE;
scan_req.chan_list[0].scan_time = 0;
} else {
for (band = 0; (band < IEEE80211_NUM_BANDS); band++) {
if (!priv->wdev->wiphy->bands[band])
continue;
sband = priv->wdev->wiphy->bands[band];
for (i = 0; (i < sband->n_channels); i++) {
ch = &sband->channels[i];
if (ch->flags & IEEE80211_CHAN_DISABLED)
continue;
scan_req.chan_list[chan_idx].radio_type = band;
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
scan_req.chan_list[chan_idx].scan_type =
MLAN_SCAN_TYPE_PASSIVE;
else if (ch->flags & IEEE80211_CHAN_RADAR)
scan_req.chan_list[chan_idx].scan_type =
MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
else
scan_req.chan_list[chan_idx].scan_type =
MLAN_SCAN_TYPE_ACTIVE;
scan_req.chan_list[chan_idx].chan_number =
(u32)ch->hw_value;
chan_idx++;
}
}
}
moal_memcpy_ext(priv->phandle, scan_req.random_mac, priv->random_mac,
ETH_ALEN, sizeof(scan_req.random_mac));
ret = woal_request_userscan(priv, wait_option, &scan_req);
#ifdef REASSOCIATION
MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
#endif
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
/**
* @brief Save assoc parameters for roaming
*
* @param priv A pointer to moal_private
* @param req A pointer to cfg80211_assoc_request structure
*/
static void
woal_save_assoc_params(moal_private *priv,
struct cfg80211_assoc_request *req,
mlan_ssid_bssid *ssid_bssid)
{
ENTER();
priv->assoc_bss = req->bss;
if (req->bss->channel) {
priv->sme_current.channel = &priv->conn_chan;
moal_memcpy_ext(priv->phandle, priv->sme_current.channel,
req->bss->channel,
sizeof(struct ieee80211_channel),
sizeof(struct ieee80211_channel));
}
priv->sme_current.bssid = priv->conn_bssid;
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
req->bss->bssid, MLAN_MAC_ADDR_LENGTH,
MLAN_MAC_ADDR_LENGTH);
if (req->ie && req->ie_len) {
priv->sme_current.ie = kzalloc(req->ie_len, GFP_KERNEL);
priv->sme_current.ie_len = req->ie_len;
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ie,
req->ie, req->ie_len, priv->sme_current.ie_len);
}
moal_memcpy_ext(priv->phandle, &priv->sme_current.crypto, &req->crypto,
sizeof(struct cfg80211_crypto_settings),
sizeof(struct cfg80211_crypto_settings));
priv->sme_current.flags = req->flags;
moal_memcpy_ext(priv->phandle, &priv->sme_current.ht_capa,
&req->ht_capa, sizeof(struct ieee80211_ht_cap),
sizeof(struct ieee80211_ht_cap));
moal_memcpy_ext(priv->phandle, &priv->sme_current.ht_capa_mask,
&req->ht_capa_mask, sizeof(struct ieee80211_ht_cap),
sizeof(struct ieee80211_ht_cap));
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
moal_memcpy_ext(priv->phandle, &priv->sme_current.vht_capa,
&req->vht_capa, sizeof(struct ieee80211_vht_cap),
sizeof(struct ieee80211_vht_cap));
moal_memcpy_ext(priv->phandle, &priv->sme_current.vht_capa_mask,
&req->vht_capa_mask, sizeof(struct ieee80211_vht_cap),
sizeof(struct ieee80211_vht_cap));
#endif
if (ssid_bssid && ssid_bssid->ssid.ssid_len) {
priv->sme_current.ssid = priv->conn_ssid;
memset(priv->conn_ssid, 0, MLAN_MAX_SSID_LENGTH);
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ssid,
ssid_bssid->ssid.ssid,
ssid_bssid->ssid.ssid_len,
sizeof(priv->conn_ssid));
priv->conn_ssid_len = ssid_bssid->ssid.ssid_len;
}
LEAVE();
}
/**
* @brief Save auth parameters for roaming
*
* @param priv A pointer to moal_private
* @param req A pointer to struct cfg80211_auth_request
*/
static void
woal_save_auth_params(moal_private *priv, struct cfg80211_auth_request *req)
{
ENTER();
woal_clear_conn_params(priv);
priv->sme_current.auth_type = req->auth_type;
priv->sme_current.key_idx = req->key_idx;
priv->sme_current.key_len = req->key_len;
if (req->key && req->key_len && (req->key_len <= MAX_WEP_KEY_SIZE)) {
priv->sme_current.key = priv->conn_wep_key;
moal_memcpy_ext(priv->phandle, (t_u8 *)priv->sme_current.key,
req->key, req->key_len,
sizeof(priv->conn_wep_key));
}
LEAVE();
}
/**
* @brief Request scan based on auth_request parameter
*
* @param priv A pointer to moal_private structure
* @param req A pointer to cfg80211_auth_request
* @param wait_option wait option
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_auth_scan(moal_private *priv,
struct cfg80211_auth_request *req, t_u8 wait_option)
{
moal_handle *handle = priv->phandle;
int ret = 0;
wlan_user_scan_cfg scan_req;
enum ieee80211_band band;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
int chan_idx = 0, i;
const u8 *ssid;
ENTER();
if (handle->scan_pending_on_block == MTRUE) {
PRINTM(MINFO, "scan already in processing...\n");
LEAVE();
return ret;
}
#ifdef REASSOCIATION
if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) {
PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n");
LEAVE();
return -EBUSY;
}
#endif /* REASSOCIATION */
priv->report_scan_result = MTRUE;
memset(&scan_req, 0x00, sizeof(scan_req));
rcu_read_lock();
ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
if (ssid) {
moal_memcpy_ext(priv->phandle, scan_req.ssid_list[0].ssid,
ssid + 2, ssid[1],
sizeof(scan_req.ssid_list[0].ssid));
scan_req.ssid_list[0].max_len = 0;
}
rcu_read_unlock();
moal_memcpy_ext(priv->phandle, scan_req.specific_bssid, req->bss->bssid,
ETH_ALEN, ETH_ALEN);
if (req->bss->channel) {
scan_req.chan_list[0].chan_number = req->bss->channel->hw_value;
scan_req.chan_list[0].radio_type = req->bss->channel->band;
if (req->bss->channel->flags & IEEE80211_CHAN_PASSIVE_SCAN)
scan_req.chan_list[0].scan_type =
MLAN_SCAN_TYPE_PASSIVE;
else if (req->bss->channel->flags & IEEE80211_CHAN_RADAR)
scan_req.chan_list[0].scan_type =
MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
else
scan_req.chan_list[0].scan_type = MLAN_SCAN_TYPE_ACTIVE;
scan_req.chan_list[0].scan_time = 0;
} else {
for (band = 0; (band < IEEE80211_NUM_BANDS); band++) {
if (!priv->wdev->wiphy->bands[band])
continue;
sband = priv->wdev->wiphy->bands[band];
for (i = 0; (i < sband->n_channels); i++) {
ch = &sband->channels[i];
if (ch->flags & IEEE80211_CHAN_DISABLED)
continue;
scan_req.chan_list[chan_idx].radio_type = band;
if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN)
scan_req.chan_list[chan_idx].scan_type =
MLAN_SCAN_TYPE_PASSIVE;
else if (ch->flags & IEEE80211_CHAN_RADAR)
scan_req.chan_list[chan_idx].scan_type =
MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
else
scan_req.chan_list[chan_idx].scan_type =
MLAN_SCAN_TYPE_ACTIVE;
scan_req.chan_list[chan_idx].chan_number =
(u32)ch->hw_value;
chan_idx++;
}
}
}
moal_memcpy_ext(priv->phandle, scan_req.random_mac, priv->random_mac,
ETH_ALEN, sizeof(scan_req.random_mac));
ret = woal_request_userscan(priv, wait_option, &scan_req);
#ifdef REASSOCIATION
MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
#endif
LEAVE();
return ret;
}
/**
* @brief This function is authentication handler when host MLME
* enable.
* In this case driver will prepare and send Auth Req.
*
* @param wiphy A pointer to wiphy.
*
* @param dev A pointer to net_device
*
* @param req A pointer to cfg80211_auth_request
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_authenticate(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_auth_request *req)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
IEEE80211_MGMT *mgmt = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
pmlan_buffer pmbuf = NULL;
t_u32 pkt_type, tx_control;
t_u16 packet_len = 0, auth_alg;
t_u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int ret = 0;
t_u8 trans = 1, status_code = 0;
t_u8 *varptr = NULL;
mlan_ssid_bssid *ssid_bssid;
moal_handle *handle = priv->phandle;
int i;
ENTER();
priv->cfg_disconnect = MFALSE;
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
PRINTM(MERROR, "Role is AP\n");
LEAVE();
return -EFAULT;
}
#endif
if (priv->wdev->iftype != NL80211_IFTYPE_STATION
#ifdef WIFI_DIRECT_SUPPORT
&& priv->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT
#endif /* WIFI_DIRECT_SUPPORT */
) {
PRINTM(MERROR,
"Received infra auth request when interface not in infra mode\n");
LEAVE();
return -EINVAL;
}
/** cancel pending scan */
woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
ssid_bssid = kzalloc(sizeof(mlan_ssid_bssid), GFP_ATOMIC);
if (!ssid_bssid) {
PRINTM(MERROR, "Fail to allocate ssid_bssid buffer\n");
LEAVE();
return -ENOMEM;
}
moal_memcpy_ext(priv->phandle, ssid_bssid->bssid, req->bss->bssid,
ETH_ALEN, sizeof(ssid_bssid->bssid));
/* Not allowed to connect to the same AP which is already connected
with other interface */
for (i = 0; i < handle->priv_num; i++) {
if (handle->priv[i] != priv &&
MTRUE == woal_is_connected(handle->priv[i], ssid_bssid)) {
PRINTM(MMSG,
"wlan: already connected with other interface, bssid "
MACSTR "\n",
MAC2STR(handle->priv[i]->cfg_bssid));
kfree(ssid_bssid);
LEAVE();
return -EINVAL;
}
}
if (MLAN_STATUS_SUCCESS != woal_find_bssid(priv, req->bss->bssid)) {
woal_cfg80211_auth_scan(priv, req, MOAL_IOCTL_WAIT);
if (MLAN_STATUS_SUCCESS !=
woal_find_bssid(priv, req->bss->bssid)) {
PRINTM(MMSG, "bssid not find in scan list\n");
kfree(ssid_bssid);
LEAVE();
return -EFAULT;
}
}
kfree(ssid_bssid);
if ((priv->auth_alg != WLAN_AUTH_SAE) &&
(priv->auth_flag & HOST_MLME_AUTH_PENDING)) {
PRINTM(MERROR, "pending auth on going\n");
LEAVE();
return -EBUSY;
}
#ifdef WIFI_DIRECT_SUPPORT
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
(priv->wdev->iftype == NL80211_IFTYPE_STATION ||
priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
/* if bsstype == wifi direct, and iftype == station or p2p
* client, that means wpa_supplicant wants to enable wifi direct
* functionality, so we should init p2p client.
*
* Note that due to kernel iftype check, ICS wpa_supplicant
* could not updaet iftype to init p2p client, so we have to
* done it here.
* */
if (MLAN_STATUS_SUCCESS != woal_cfg80211_init_p2p_client(priv)) {
PRINTM(MERROR,
"Init p2p client for wpa_supplicant failed.\n");
ret = -EFAULT;
LEAVE();
return ret;
}
}
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
/* WAR for P2P connection with vendor TV */
woal_sched_timeout(200);
}
#endif
/*enable auth register frame */
if (priv->auth_flag == 0) {
woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MTRUE);
woal_mgmt_frame_register(priv, IEEE80211_STYPE_DEAUTH, MTRUE);
woal_mgmt_frame_register(priv, IEEE80211_STYPE_DISASSOC, MTRUE);
}
#define HEADER_SIZE 8
// frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4
#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6)
// 6 = auth_alg + auth_transaction +auth_status
#define AUTH_BODY_LEN 6
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
packet_len = (t_u16)req->ie_len + req->auth_data_len + MGMT_HEADER_LEN +
AUTH_BODY_LEN;
#else
packet_len = (t_u16)req->ie_len + req->sae_data_len + MGMT_HEADER_LEN +
AUTH_BODY_LEN;
#endif
pmbuf = woal_alloc_mlan_buffer(priv->phandle,
MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE +
packet_len + sizeof(packet_len));
if (!pmbuf) {
PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
ret = -ENOMEM;
goto done;
}
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0,
KEY_INDEX_CLEAR_ALL, NULL, 1,
MOAL_IOCTL_WAIT)) {
/* Disable keys and clear all previous security settings */
PRINTM(MERROR, "Fail to clear previous keys\n");
ret = -EFAULT;
goto done;
}
switch (req->auth_type) {
case NL80211_AUTHTYPE_OPEN_SYSTEM:
auth_alg = WLAN_AUTH_OPEN;
break;
case NL80211_AUTHTYPE_SHARED_KEY:
auth_alg = WLAN_AUTH_SHARED_KEY;
break;
case NL80211_AUTHTYPE_FT:
auth_alg = WLAN_AUTH_FT;
break;
case NL80211_AUTHTYPE_NETWORK_EAP:
auth_alg = WLAN_AUTH_LEAP;
break;
case NL80211_AUTHTYPE_SAE:
auth_alg = WLAN_AUTH_SAE;
break;
default:
PRINTM(MERROR, "Unsupported auth type=%d\n", req->auth_type);
ret = -EOPNOTSUPP;
break;
}
if (ret)
goto done;
if (MLAN_STATUS_SUCCESS !=
woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_alg)) {
PRINTM(MERROR, "Fail to set auth mode\n");
ret = -EFAULT;
goto done;
}
if (req->key &&
((auth_alg == WLAN_AUTH_OPEN) ||
(auth_alg == WLAN_AUTH_SHARED_KEY))) {
PRINTM(MMSG, "Setting wep encryption with key len %d\n",
req->key_len);
/* Set the WEP key */
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_set_wep_keys(priv, req->key, req->key_len,
req->key_idx, MOAL_IOCTL_WAIT)) {
PRINTM(MERROR, "Fail to set wep key idx %d\n", req->key_idx);
ret = -EFAULT;
goto done;
}
/* Enable the WEP key by key index */
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_set_wep_keys(priv, NULL, 0, req->key_idx,
MOAL_IOCTL_WAIT)) {
PRINTM(MERROR, "Fail to enable wep key idx %d\n", req->key_idx);
ret = -EFAULT;
goto done;
}
}
if (priv->auth_flag == 0) {
if (woal_cfg80211_remain_on_channel_cfg
(priv, MOAL_IOCTL_WAIT, MFALSE, (t_u8 *)&status,
req->bss->channel, 0, AUTH_TX_DEFAULT_WAIT_TIME)) {
PRINTM(MERROR, "Fail to configure remain on channel\n");
ret = -EFAULT;
goto done;
}
if (status == MLAN_STATUS_SUCCESS) {
priv->phandle->remain_on_channel = MTRUE;
moal_memcpy_ext(priv->phandle, &(priv->phandle->chan),
req->bss->channel,
sizeof(struct ieee80211_channel),
sizeof(priv->phandle->chan));
} else {
PRINTM(MERROR,
"HostMlme %s: Set remain on Channel: with status=%d\n",
dev->name, status);
}
}
pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
tx_control = 0;
/* Add pkt_type and tx_control */
moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset,
&pkt_type, sizeof(pkt_type), sizeof(pkt_type));
moal_memcpy_ext(priv->phandle,
pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type),
&tx_control, sizeof(tx_control), sizeof(tx_control));
mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
HEADER_SIZE + sizeof(packet_len));
memset(mgmt, 0, MGMT_HEADER_LEN);
/**Authentication Frame: Frame Control*/
mgmt->frame_control =
woal_cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
/**Authentication Frame: Destination Address*/
moal_memcpy_ext(priv->phandle, mgmt->da, req->bss->bssid, ETH_ALEN,
sizeof(mgmt->da));
/**Authentication Frame: Source Address*/
moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN,
sizeof(mgmt->sa));
/**Authentication Frame: BSSID*/
moal_memcpy_ext(priv->phandle, mgmt->bssid, req->bss->bssid, ETH_ALEN,
sizeof(mgmt->bssid));
moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN,
sizeof(mgmt->addr4));
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
if (req->auth_data_len >= 4) {
if (req->auth_type == NL80211_AUTHTYPE_SAE) {
__le16 *pos = (__le16 *) req->auth_data;
trans = le16_to_cpu(pos[0]);
status_code = le16_to_cpu(pos[1]);
}
moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable),
req->auth_data + 4, req->auth_data_len - 4,
req->auth_data_len - 4);
varptr = (t_u8 *)&mgmt->u.auth.variable +
(req->auth_data_len - 4);
packet_len -= 4;
}
#else
if (req->sae_data_len >= 4) {
if (req->auth_type == NL80211_AUTHTYPE_SAE) {
__le16 *pos = (__le16 *) req->sae_data;
trans = le16_to_cpu(pos[0]);
status_code = le16_to_cpu(pos[1]);
}
moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable),
req->sae_data + 4, req->sae_data_len - 4,
req->sae_data_len - 4);
varptr = (t_u8 *)&mgmt->u.auth.variable +
(req->sae_data_len - 4);
packet_len -= 4;
}
#endif
/*Add packet len */
moal_memcpy_ext(priv->phandle,
pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
&packet_len, sizeof(packet_len), sizeof(packet_len));
/**Authentication Frame: Authentication Alg*/
mgmt->u.auth.auth_alg = woal_cpu_to_le16(auth_alg);
mgmt->u.auth.auth_transaction = trans;
/**Authentication Frame: Status code*/
mgmt->u.auth.status_code = status_code;
if (req->ie && req->ie_len) {
if (!varptr) {
varptr = (t_u8 *)&mgmt->u.auth.variable;
}
moal_memcpy_ext(priv->phandle, (t_u8 *)varptr, req->ie,
req->ie_len, req->ie_len);
}
pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len);
pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
pmbuf->bss_index = priv->bss_index;
pmbuf->priority = 7;
priv->host_mlme = MTRUE;
priv->auth_flag = HOST_MLME_AUTH_PENDING;
priv->auth_alg = woal_cpu_to_le16(auth_alg);
PRINTM(MMSG, "wlan: HostMlme %s send auth to bssid " MACSTR "\n",
dev->name, MAC2STR(req->bss->bssid));
DBG_HEXDUMP(MDAT_D, "Auth:", pmbuf->pbuf + pmbuf->data_offset,
pmbuf->data_len);
if (priv->bss_type == MLAN_BSS_TYPE_STA)
woal_save_auth_params(priv, req);
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
switch (status) {
case MLAN_STATUS_PENDING:
atomic_inc(&priv->phandle->tx_pending);
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
break;
case MLAN_STATUS_SUCCESS:
woal_free_mlan_buffer(priv->phandle, pmbuf);
break;
case MLAN_STATUS_FAILURE:
default:
woal_free_mlan_buffer(priv->phandle, pmbuf);
priv->host_mlme = MFALSE;
priv->auth_flag = 0;
priv->auth_alg = 0xFFFF;
PRINTM(MERROR, "Fail to send packet status=%d\n", status);
ret = -EFAULT;
break;
}
done:
if (ret) {
woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE);
if (priv->phandle->remain_on_channel) {
woal_cfg80211_remain_on_channel_cfg(priv,
MOAL_IOCTL_WAIT,
MTRUE,
(t_u8 *)&status,
NULL, 0, 0);
priv->phandle->remain_on_channel = MFALSE;
}
}
LEAVE();
return ret;
}
/**
* @brief This workqueue function handles association response in host mlme
* case
*
* @param work A pointer to work_struct
*
* @return N/A
*/
void
woal_host_mlme_work_queue(struct work_struct *work)
{
moal_handle *handle = container_of(work, moal_handle, host_mlme_work);
moal_private *priv = (moal_private *)handle->host_mlme_priv;
mlan_status status = MLAN_STATUS_SUCCESS;
if (priv) {
if (priv->auth_flag & HOST_MLME_AUTH_DONE) {
priv->auth_flag = 0;
woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH,
MFALSE);
if (priv->phandle->remain_on_channel) {
woal_cfg80211_remain_on_channel_cfg(priv,
MOAL_IOCTL_WAIT,
MTRUE,
(t_u8 *)
&status,
NULL, 0, 0);
priv->phandle->remain_on_channel = MFALSE;
}
PRINTM(MCMND, "wlan: HostMlme %s auth success\n",
priv->netdev->name);
}
}
}
/**
* @brief This workqueue function handles association response in event queue
* case
*
* @param priv pointer to moal_private
* @param assoc_rsp pointer to mlan_ds_misc_assoc_rsp
*
* @return N/A
*/
void
woal_host_mlme_process_assoc_resp(moal_private *priv,
mlan_ds_misc_assoc_rsp *assoc_rsp)
{
struct cfg80211_bss *bss = NULL;
unsigned long flags;
if (priv) {
if (priv->auth_flag & HOST_MLME_ASSOC_DONE) {
priv->auth_flag = 0;
bss = priv->assoc_bss;
if (!bss) {
PRINTM(MERROR,
"HostMlme %s:assoc_bss is null\n",
priv->netdev->name);
return;
}
if (assoc_rsp->assoc_resp_len) {
PRINTM(MCMND,
"HostMlme: %s assoc_resp_len=%d, frame_control=0x%x\n",
priv->netdev->name,
assoc_rsp->assoc_resp_len,
((struct ieee80211_mgmt *)
assoc_rsp->assoc_resp_buf)
->frame_control);
if (ieee80211_is_assoc_resp(((struct
ieee80211_mgmt *)
assoc_rsp->
assoc_resp_buf)
->frame_control) ||
ieee80211_is_reassoc_resp(((struct
ieee80211_mgmt
*)
assoc_rsp->
assoc_resp_buf)
->frame_control)) {
spin_lock_irqsave(&priv->connect_lock,
flags);
if (le16_to_cpu(((struct ieee80211_mgmt
*)assoc_rsp->
assoc_resp_buf)
->u.assoc_resp.
status_code) !=
WLAN_STATUS_SUCCESS) {
memset(priv->cfg_bssid, 0,
ETH_ALEN);
if (priv->bss_type ==
MLAN_BSS_TYPE_STA)
woal_clear_conn_params
(priv);
} else {
priv->cfg_disconnect = MFALSE;
}
spin_unlock_irqrestore(&priv->
connect_lock,
flags);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
cfg80211_rx_assoc_resp(priv->netdev,
bss,
assoc_rsp->
assoc_resp_buf,
assoc_rsp->
assoc_resp_len,
-1, NULL, 0);
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
cfg80211_rx_assoc_resp(priv->netdev,
bss,
assoc_rsp->
assoc_resp_buf,
assoc_rsp->
assoc_resp_len,
-1);
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
cfg80211_rx_assoc_resp(priv->netdev,
bss,
assoc_rsp->
assoc_resp_buf,
assoc_rsp->
assoc_resp_len);
#else
cfg80211_send_rx_assoc(priv->netdev,
bss,
assoc_rsp->
assoc_resp_buf,
assoc_rsp->
assoc_resp_len);
#endif
#endif
#endif
}
}
}
}
}
/**
* @brief Handle assoc response event
*
* @param priv A pointer moal_private structure
* @param pchan_info A pointer to mlan_ds_misc_assoc_rsp structure
*
* @return N/A
*/
static void
woal_assoc_resp_event(moal_private *priv, mlan_ds_misc_assoc_rsp *passoc_rsp)
{
struct woal_event *evt;
unsigned long flags;
moal_handle *handle = priv->phandle;
evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC);
if (evt) {
evt->priv = priv;
evt->type = WOAL_EVENT_ASSOC_RESP;
moal_memcpy_ext(priv->phandle, &evt->assoc_resp, passoc_rsp,
sizeof(mlan_ds_misc_assoc_rsp),
sizeof(mlan_ds_misc_assoc_rsp));
INIT_LIST_HEAD(&evt->link);
spin_lock_irqsave(&handle->evt_lock, flags);
list_add_tail(&evt->link, &handle->evt_queue);
spin_unlock_irqrestore(&handle->evt_lock, flags);
queue_work(handle->evt_workqueue, &handle->evt_work);
}
}
/**
* @brief This function is association handler when host MLME
* enable.
* In this case driver will prepare and send Assoc Req.
*
* @param wiphy A pointer to wiphy.
*
* @param dev A pointer to net_device
*
* @param req A pointer to cfg80211_assoc_request
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
int ret = 0;
mlan_ssid_bssid ssid_bssid;
unsigned long flags;
const u8 *ssid_ie;
int wpa_enabled = 0, group_enc_mode = 0, pairwise_enc_mode = 0;
mlan_bss_info bss_info;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (priv->auth_alg == WLAN_AUTH_SAE) {
priv->auth_flag = HOST_MLME_AUTH_DONE;
woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE);
PRINTM(MINFO, "wlan: HostMlme %s auth exchange successful\n",
priv->netdev->name);
if (priv->phandle->remain_on_channel) {
if (woal_cfg80211_remain_on_channel_cfg
(priv, MOAL_IOCTL_WAIT, MTRUE, (t_u8 *)&status,
NULL, 0, 0)) {
PRINTM(MERROR,
"Failed to cancel remain on channel\n");
ret = -EFAULT;
goto done;
}
priv->phandle->remain_on_channel = MFALSE;
}
}
if (priv->auth_flag && !(priv->auth_flag & HOST_MLME_AUTH_DONE)) {
LEAVE();
return -EBUSY;
}
/** cancel pending scan */
woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
priv->cfg_connect = MTRUE;
priv->assoc_status = 0;
priv->auth_alg = 0xFFFF;
memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid));
rcu_read_lock();
ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
moal_memcpy_ext(priv->phandle, ssid_bssid.bssid, req->bss->bssid,
ETH_ALEN, sizeof(ssid_bssid.bssid));
if (!ssid_ie) {
rcu_read_unlock();
ret = -EINVAL;
goto done;
}
moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, ssid_ie + 2,
ssid_ie[1], sizeof(ssid_bssid.ssid.ssid));
ssid_bssid.ssid.ssid_len = ssid_ie[1];
rcu_read_unlock();
if (ssid_bssid.ssid.ssid_len > MW_ESSID_MAX_SIZE) {
PRINTM(MERROR, "Invalid SSID - aborting\n");
ret = -EINVAL;
goto done;
}
if (!ssid_bssid.ssid.ssid_len || ssid_bssid.ssid.ssid[0] < 0x20) {
PRINTM(MERROR, "Invalid SSID - aborting\n");
ret = -EINVAL;
goto done;
}
#ifdef STA_WEXT
if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) {
switch (req->crypto.wpa_versions) {
case NL80211_WPA_VERSION_2:
priv->wpa_version = IW_AUTH_WPA_VERSION_WPA2;
break;
case NL80211_WPA_VERSION_1:
priv->wpa_version = IW_AUTH_WPA_VERSION_WPA;
break;
default:
priv->wpa_version = 0;
break;
}
if (req->crypto.n_akm_suites) {
switch (req->crypto.akm_suites[0]) {
case WLAN_AKM_SUITE_PSK:
priv->key_mgmt = IW_AUTH_KEY_MGMT_PSK;
break;
case WLAN_AKM_SUITE_8021X:
priv->key_mgmt = IW_AUTH_KEY_MGMT_802_1X;
break;
default:
priv->key_mgmt = 0;
break;
}
}
}
#endif
if (req->ie && req->ie_len) { /* Set the IE */
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_assoc_ies_cfg(priv, (t_u8 *)req->ie,
req->ie_len, MOAL_IOCTL_WAIT)) {
ret = -EFAULT;
goto done;
}
}
if (req->crypto.n_ciphers_pairwise) {
pairwise_enc_mode =
woal_cfg80211_get_encryption_mode(req->crypto.
ciphers_pairwise[0],
&wpa_enabled);
ret = woal_cfg80211_set_auth(priv, pairwise_enc_mode,
wpa_enabled, MOAL_IOCTL_WAIT);
if (ret)
goto done;
}
if (req->crypto.cipher_group) {
group_enc_mode =
woal_cfg80211_get_encryption_mode(req->crypto.
cipher_group,
&wpa_enabled);
ret = woal_cfg80211_set_auth(priv, group_enc_mode, wpa_enabled,
MOAL_IOCTL_WAIT);
if (ret)
goto done;
}
ssid_bssid.host_mlme = priv->host_mlme;
if (req->bss->channel) {
ssid_bssid.channel_flags = req->bss->channel->flags;
ssid_bssid.channel_flags |= CHAN_FLAGS_MAX;
PRINTM(MCMND, "channel flags=0x%x\n", req->bss->channel->flags);
}
if (req->prev_bssid) {
moal_memcpy_ext(priv->phandle, ssid_bssid.prev_bssid,
req->prev_bssid, ETH_ALEN,
sizeof(ssid_bssid.prev_bssid));
}
PRINTM(MCMND, "wlan: HostMlme %s send assoicate to bssid " MACSTR "\n",
priv->netdev->name, MAC2STR(req->bss->bssid));
if (MLAN_STATUS_SUCCESS !=
woal_bss_start(priv, MOAL_IOCTL_WAIT_TIMEOUT, &ssid_bssid)) {
PRINTM(MERROR, "HostMlme %s: bss_start Fails\n",
priv->netdev->name);
priv->host_mlme = MFALSE;
priv->auth_flag = 0;
ret = -EFAULT;
}
done:
if (!ret) {
priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD;
if (priv->bss_type == MLAN_BSS_TYPE_STA
#ifdef WIFI_DIRECT_SUPPORT
|| priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT
#endif
)
woal_save_assoc_params(priv, req, &ssid_bssid);
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
priv->channel = bss_info.bss_chan;
}
spin_lock_irqsave(&priv->connect_lock, flags);
priv->cfg_connect = MFALSE;
if (!ret && priv->media_connected) {
PRINTM(MMSG,
"wlan: HostMlme %s Connected to bssid " MACSTR
" successfully\n",
priv->netdev->name, MAC2STR(priv->cfg_bssid));
spin_unlock_irqrestore(&priv->connect_lock, flags);
} else {
PRINTM(MERROR,
"wlan: HostMlme %s Failed to connect to bssid " MACSTR
"\n", priv->netdev->name, MAC2STR(req->bss->bssid));
if (ssid_bssid.assoc_rsp.assoc_resp_len &&
ssid_bssid.assoc_rsp.assoc_resp_len >
sizeof(IEEEtypes_MgmtHdr_t)) {
// save the connection param when send assoc_resp to
// kernel
woal_save_assoc_params(priv, req, &ssid_bssid);
ret = 0;
} else {
ssid_bssid.assoc_rsp.assoc_resp_len = 0;
ret = -EFAULT;
memset(priv->cfg_bssid, 0, ETH_ALEN);
if (priv->bss_type == MLAN_BSS_TYPE_STA)
woal_clear_conn_params(priv);
}
priv->host_mlme = MFALSE;
priv->auth_flag = 0;
spin_unlock_irqrestore(&priv->connect_lock, flags);
}
/*Association Response should also be send when ret is non-zero.
We also need to return success when we have association response
available */
if (ssid_bssid.assoc_rsp.assoc_resp_len) {
priv->auth_flag |= HOST_MLME_ASSOC_DONE;
woal_assoc_resp_event(priv, &ssid_bssid.assoc_rsp);
}
LEAVE();
return ret;
}
#endif
/**
* @brief Request the driver for (re)association
*
* @param priv A pointer to moal_private structure
* @param sme A pointer to connect parameters
* @param wait_option wait option
* @param assoc_resp A pointer to assoc_rsp structure;
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_assoc(moal_private *priv, void *sme, t_u8 wait_option,
mlan_ds_misc_assoc_rsp *assoc_rsp)
{
struct cfg80211_ibss_params *ibss_param = NULL;
struct cfg80211_connect_params *conn_param = NULL;
mlan_802_11_ssid req_ssid;
mlan_ssid_bssid ssid_bssid;
mlan_ioctl_req *req = NULL;
int ret = 0;
t_u32 auth_type = 0, mode;
int wpa_enabled = 0;
int group_enc_mode = 0, pairwise_enc_mode = 0;
int alg_is_wep = 0;
t_u8 *ssid, ssid_len = 0, *bssid;
t_u8 *ie = NULL;
int ie_len = 0;
struct ieee80211_channel *channel = NULL;
t_u16 beacon_interval = 0;
bool privacy;
struct cfg80211_bss *pub = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (!sme) {
LEAVE();
return -EFAULT;
}
mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype);
if (mode == MLAN_BSS_MODE_IBSS) {
ibss_param = (struct cfg80211_ibss_params *)sme;
ssid = (t_u8 *)ibss_param->ssid;
ssid_len = ibss_param->ssid_len;
bssid = (t_u8 *)ibss_param->bssid;
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
channel = ibss_param->channel;
#else
channel = ibss_param->chandef.chan;
#endif
if (channel)
priv->phandle->band = channel->band;
if (ibss_param->ie_len)
ie = (t_u8 *)ibss_param->ie;
ie_len = ibss_param->ie_len;
beacon_interval = ibss_param->beacon_interval;
privacy = ibss_param->privacy;
} else {
conn_param = (struct cfg80211_connect_params *)sme;
ssid = (t_u8 *)conn_param->ssid;
ssid_len = conn_param->ssid_len;
bssid = (t_u8 *)conn_param->bssid;
channel = conn_param->channel;
if (channel)
priv->phandle->band = channel->band;
if (conn_param->ie_len)
ie = (t_u8 *)conn_param->ie;
ie_len = conn_param->ie_len;
privacy = conn_param->privacy;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
pub = cfg80211_get_bss(priv->wdev->wiphy, channel,
bssid, ssid, ssid_len,
IEEE80211_BSS_TYPE_ESS,
IEEE80211_PRIVACY_ANY);
#else
pub = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid,
ssid_len, WLAN_CAPABILITY_ESS,
WLAN_CAPABILITY_ESS);
#endif
if (pub) {
if ((!priv->phandle->params.reg_alpha2 ||
strncmp(priv->phandle->params.reg_alpha2,
"99", strlen("99")))
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
&&
(!moal_extflg_isset(priv->phandle,
EXT_COUNTRY_IE_IGNORE))
#endif
)
woal_process_country_ie(priv, pub);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(priv->wdev->wiphy, pub);
#else
cfg80211_put_bss(pub);
#endif
} else
woal_send_domain_info_cmd_fw(priv, wait_option);
#ifdef STA_WEXT
if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) {
switch (conn_param->crypto.wpa_versions) {
case NL80211_WPA_VERSION_2:
priv->wpa_version = IW_AUTH_WPA_VERSION_WPA2;
break;
case NL80211_WPA_VERSION_1:
priv->wpa_version = IW_AUTH_WPA_VERSION_WPA;
break;
default:
priv->wpa_version = 0;
break;
}
if (conn_param->crypto.n_akm_suites) {
switch (conn_param->crypto.akm_suites[0]) {
case WLAN_AKM_SUITE_PSK:
priv->key_mgmt = IW_AUTH_KEY_MGMT_PSK;
break;
case WLAN_AKM_SUITE_8021X:
priv->key_mgmt =
IW_AUTH_KEY_MGMT_802_1X;
break;
default:
priv->key_mgmt = 0;
break;
}
}
}
#endif
}
memset(&req_ssid, 0, sizeof(mlan_802_11_ssid));
memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid));
req_ssid.ssid_len = ssid_len;
if (ssid_len > MW_ESSID_MAX_SIZE) {
PRINTM(MERROR, "Invalid SSID - aborting\n");
ret = -EINVAL;
goto done;
}
moal_memcpy_ext(priv->phandle, req_ssid.ssid, ssid, ssid_len,
sizeof(req_ssid.ssid));
if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) {
PRINTM(MERROR, "Invalid SSID - aborting\n");
ret = -EINVAL;
goto done;
}
if (priv->phandle->card_info->embedded_supp)
if (MLAN_STATUS_SUCCESS !=
woal_set_ewpa_mode(priv, wait_option, &ssid_bssid)) {
ret = -EFAULT;
goto done;
}
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0,
KEY_INDEX_CLEAR_ALL, NULL, 1, wait_option)) {
/* Disable keys and clear all previous security settings */
ret = -EFAULT;
goto done;
}
#ifdef STA_CFG80211
if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) {
/** Check if current roaming support OKC offload roaming */
if (conn_param && conn_param->crypto.n_akm_suites &&
conn_param->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X) {
if (priv->okc_roaming_ie && priv->okc_ie_len) {
ie = priv->okc_roaming_ie;
ie_len = priv->okc_ie_len;
}
}
}
#endif
if ((priv->ft_pre_connect ||
(conn_param && conn_param->auth_type == NL80211_AUTHTYPE_FT)) &&
priv->ft_ie_len) {
ie = priv->ft_ie;
ie_len = priv->ft_ie_len;
priv->ft_ie_len = 0;
}
if (ie && ie_len) { /* Set the IE */
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_assoc_ies_cfg(priv, ie, ie_len,
wait_option)) {
ret = -EFAULT;
goto done;
}
}
if (conn_param && mode != MLAN_BSS_MODE_IBSS) {
/* These parameters are only for managed mode */
if (conn_param->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM)
auth_type = MLAN_AUTH_MODE_OPEN;
else if (conn_param->auth_type == NL80211_AUTHTYPE_SHARED_KEY)
auth_type = MLAN_AUTH_MODE_SHARED;
else if (conn_param->auth_type == NL80211_AUTHTYPE_NETWORK_EAP)
auth_type = MLAN_AUTH_MODE_NETWORKEAP;
else if (conn_param->auth_type == NL80211_AUTHTYPE_FT)
auth_type = MLAN_AUTH_MODE_FT;
else
auth_type = MLAN_AUTH_MODE_AUTO;
if (priv->ft_pre_connect)
auth_type = MLAN_AUTH_MODE_FT;
if (MLAN_STATUS_SUCCESS !=
woal_set_auth_mode(priv, wait_option, auth_type)) {
ret = -EFAULT;
goto done;
}
if (conn_param->crypto.n_ciphers_pairwise) {
pairwise_enc_mode =
woal_cfg80211_get_encryption_mode(conn_param->
crypto.
ciphers_pairwise
[0],
&wpa_enabled);
ret = woal_cfg80211_set_auth(priv, pairwise_enc_mode,
wpa_enabled, wait_option);
if (ret)
goto done;
}
if (conn_param->crypto.cipher_group) {
group_enc_mode =
woal_cfg80211_get_encryption_mode(conn_param->
crypto.
cipher_group,
&wpa_enabled);
ret = woal_cfg80211_set_auth(priv, group_enc_mode,
wpa_enabled, wait_option);
if (ret)
goto done;
}
if (conn_param->key) {
alg_is_wep =
woal_cfg80211_is_alg_wep(pairwise_enc_mode) |
woal_cfg80211_is_alg_wep(group_enc_mode);
if (alg_is_wep) {
PRINTM(MINFO,
"Setting wep encryption with key len %d\n",
conn_param->key_len);
/* Set the WEP key */
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_set_wep_keys(priv,
conn_param->key,
conn_param->
key_len,
conn_param->
key_idx,
wait_option)) {
ret = -EFAULT;
goto done;
}
/* Enable the WEP key by key index */
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_set_wep_keys(priv, NULL, 0,
conn_param->
key_idx,
wait_option)) {
ret = -EFAULT;
goto done;
}
}
}
}
if (mode == MLAN_BSS_MODE_IBSS) {
mlan_ds_bss *bss = NULL;
/* Change beacon interval */
if ((beacon_interval < MLAN_MIN_BEACON_INTERVAL) ||
(beacon_interval > MLAN_MAX_BEACON_INTERVAL)) {
ret = -EINVAL;
goto done;
}
kfree(req);
req = NULL;
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
bss = (mlan_ds_bss *)req->pbuf;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL;
bss->param.bcn_interval = beacon_interval;
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
/* "privacy" is set only for ad-hoc mode */
if (privacy) {
/*
* Keep MLAN_ENCRYPTION_MODE_WEP40 for now so that
* the firmware can find a matching network from the
* scan. cfg80211 does not give us the encryption
* mode at this stage so just setting it to wep here
*/
if (MLAN_STATUS_SUCCESS !=
woal_set_auth_mode(priv, wait_option,
MLAN_AUTH_MODE_OPEN)) {
ret = -EFAULT;
goto done;
}
wpa_enabled = 0;
ret = woal_cfg80211_set_auth(priv,
MLAN_ENCRYPTION_MODE_WEP104,
wpa_enabled, wait_option);
if (ret)
goto done;
}
}
moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, &req_ssid,
sizeof(mlan_802_11_ssid), sizeof(ssid_bssid.ssid));
if (bssid)
moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, bssid,
ETH_ALEN, sizeof(ssid_bssid.bssid));
if (MLAN_STATUS_SUCCESS !=
woal_find_essid(priv, &ssid_bssid, wait_option)) {
/* Do specific SSID scanning */
if (mode != MLAN_BSS_MODE_IBSS)
ret = woal_cfg80211_connect_scan(priv, conn_param,
wait_option);
else
ret = woal_request_scan(priv, wait_option, &req_ssid);
if (ret) {
ret = -EFAULT;
goto done;
}
}
/* Disconnect before try to associate */
if (mode == MLAN_BSS_MODE_IBSS)
woal_disconnect(priv, wait_option, NULL,
DEF_DEAUTH_REASON_CODE);
if (mode != MLAN_BSS_MODE_IBSS) {
if (MLAN_STATUS_SUCCESS !=
woal_find_best_network(priv, wait_option, &ssid_bssid)) {
ret = -EFAULT;
goto done;
}
/* Inform the BSS information to kernel, otherwise
* kernel will give a panic after successful assoc */
if (MLAN_STATUS_SUCCESS !=
woal_inform_bss_from_scan_result(priv, &ssid_bssid,
wait_option)) {
ret = -EFAULT;
goto done;
}
} else if (MLAN_STATUS_SUCCESS !=
woal_find_best_network(priv, wait_option, &ssid_bssid))
/* Adhoc start, Check the channel command */
woal_11h_channel_check_ioctl(priv, wait_option);
PRINTM(MINFO, "Trying to associate to %s and bssid " MACSTR "\n",
(char *)req_ssid.ssid, MAC2STR(ssid_bssid.bssid));
/* Zero SSID implies use BSSID to connect */
if (bssid)
memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid));
else /* Connect to BSS by ESSID */
memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH);
if (channel) {
ssid_bssid.channel_flags = channel->flags;
ssid_bssid.channel_flags |= CHAN_FLAGS_MAX;
PRINTM(MCMND, "channel flags=0x%x\n", channel->flags);
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
if (conn_param && conn_param->prev_bssid) {
moal_memcpy_ext(priv->phandle, ssid_bssid.prev_bssid,
conn_param->prev_bssid, ETH_ALEN,
sizeof(ssid_bssid.prev_bssid));
}
#endif
if (MLAN_STATUS_SUCCESS !=
woal_bss_start(priv, MOAL_IOCTL_WAIT_TIMEOUT, &ssid_bssid)) {
ret = -EFAULT;
goto done;
}
/* Inform the IBSS information to kernel, otherwise
* kernel will give a panic after successful assoc */
if (mode == MLAN_BSS_MODE_IBSS) {
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_inform_ibss_bss(priv, channel,
beacon_interval)) {
ret = -EFAULT;
goto done;
}
} else if (assoc_rsp) {
moal_memcpy_ext(priv->phandle, assoc_rsp, &ssid_bssid.assoc_rsp,
sizeof(mlan_ds_misc_assoc_rsp),
sizeof(mlan_ds_misc_assoc_rsp));
PRINTM(MCMND, "assoc_rsp ie len=%d\n",
assoc_rsp->assoc_resp_len);
}
done:
if (ret) {
/* clear the encryption mode */
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_set_auth(priv, MLAN_ENCRYPTION_MODE_NONE,
MFALSE, wait_option)) {
PRINTM(MERROR, "Could not clear encryption \n");
ret = -EFAULT;
}
/* clear IE */
ie_len = 0;
if (MLAN_STATUS_SUCCESS !=
woal_set_get_gen_ie(priv, MLAN_ACT_SET, NULL, &ie_len,
wait_option)) {
PRINTM(MERROR, "Could not clear RSN IE\n");
ret = -EFAULT;
}
}
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief Request the driver to fill the tx/rx rate info
*
* @param priv A pointer to moal_private structure
* @param sinfo A pointer to station_info structure
*
* @return 0 -- success, otherwise fail
*/
static void
woal_cfg80211_fill_rate_info(moal_private *priv, struct station_info *sinfo)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ioctl_req *req = NULL;
mlan_ds_rate *rate = NULL;
t_u16 Rates[12] = { 0x02, 0x04, 0x0B, 0x16, 0x0C, 0x12,
0x18, 0x24, 0x30, 0x48, 0x60, 0x6c
};
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
rate = (mlan_ds_rate *)req->pbuf;
rate->sub_command = MLAN_OID_GET_DATA_RATE;
req->req_id = MLAN_IOCTL_RATE;
req->action = MLAN_ACT_GET;
ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
if (rate->param.data_rate.tx_rate_format != MLAN_RATE_FORMAT_LG) {
if (rate->param.data_rate.tx_rate_format == MLAN_RATE_FORMAT_HT) {
sinfo->txrate.flags = RATE_INFO_FLAGS_MCS;
if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
sinfo->txrate.bw = RATE_INFO_BW_40;
#else
sinfo->txrate.flags |=
RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
else
sinfo->txrate.bw = RATE_INFO_BW_20;
#endif
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
else if (rate->param.data_rate.tx_rate_format ==
MLAN_RATE_FORMAT_VHT) {
sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS;
sinfo->txrate.nss = rate->param.data_rate.tx_nss + 1;
if (rate->param.data_rate.tx_ht_bw == MLAN_VHT_BW80)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
sinfo->txrate.bw = RATE_INFO_BW_80;
#else
sinfo->txrate.flags |=
RATE_INFO_FLAGS_80_MHZ_WIDTH;
#endif
else if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
sinfo->txrate.bw = RATE_INFO_BW_40;
#else
sinfo->txrate.flags |=
RATE_INFO_FLAGS_40_MHZ_WIDTH;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
else
sinfo->txrate.bw = RATE_INFO_BW_20;
#endif
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 18)
else if (rate->param.data_rate.tx_rate_format ==
MLAN_RATE_FORMAT_HE) {
sinfo->txrate.flags = RATE_INFO_FLAGS_HE_MCS;
sinfo->txrate.nss = rate->param.data_rate.tx_nss + 1;
sinfo->txrate.mcs = rate->param.data_rate.tx_mcs_index;
sinfo->txrate.he_gi = rate->param.data_rate.tx_ht_gi;
if (rate->param.data_rate.tx_ht_bw == MLAN_VHT_BW80)
sinfo->txrate.bw = RATE_INFO_BW_80;
else if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40)
sinfo->txrate.bw = RATE_INFO_BW_40;
else
sinfo->txrate.bw = RATE_INFO_BW_20;
}
#endif
if (rate->param.data_rate.tx_ht_gi == MLAN_HT_SGI)
sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
sinfo->txrate.mcs = rate->param.data_rate.tx_mcs_index;
} else {
/* Bit rate is in 500 kb/s units. Convert it to 100kb/s units */
sinfo->txrate.legacy =
Rates[rate->param.data_rate.tx_data_rate] * 5;
}
//Fill Rx rate
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
if (rate->param.data_rate.rx_rate_format != MLAN_RATE_FORMAT_LG) {
if (rate->param.data_rate.rx_rate_format == MLAN_RATE_FORMAT_HT) {
sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS;
if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40)
sinfo->rxrate.bw = RATE_INFO_BW_40;
else
sinfo->rxrate.bw = RATE_INFO_BW_20;
} else if (rate->param.data_rate.rx_rate_format ==
MLAN_RATE_FORMAT_VHT) {
sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS;
sinfo->rxrate.nss = rate->param.data_rate.rx_nss + 1;
if (rate->param.data_rate.rx_ht_bw == MLAN_VHT_BW80)
sinfo->rxrate.bw = RATE_INFO_BW_80;
else if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40)
sinfo->rxrate.bw = RATE_INFO_BW_40;
else
sinfo->rxrate.bw = RATE_INFO_BW_20;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 18)
else if (rate->param.data_rate.rx_rate_format ==
MLAN_RATE_FORMAT_HE) {
sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS;
sinfo->rxrate.nss = rate->param.data_rate.rx_nss + 1;
sinfo->rxrate.mcs = rate->param.data_rate.rx_mcs_index;
sinfo->rxrate.he_gi = rate->param.data_rate.rx_ht_gi;
if (rate->param.data_rate.rx_ht_bw == MLAN_VHT_BW80)
sinfo->rxrate.bw = RATE_INFO_BW_80;
else if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40)
sinfo->rxrate.bw = RATE_INFO_BW_40;
else
sinfo->rxrate.bw = RATE_INFO_BW_20;
}
#endif
if (rate->param.data_rate.rx_ht_gi == MLAN_HT_SGI)
sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
sinfo->rxrate.mcs = rate->param.data_rate.rx_mcs_index;
} else {
/* Bit rate is in 500 kb/s units. Convert it to 100kb/s units */
sinfo->rxrate.legacy =
Rates[rate->param.data_rate.rx_data_rate] * 5;
}
#endif
done:
if (ret != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return;
}
/**
* @brief Request the driver to dump the station information
*
* @param priv A pointer to moal_private structure
* @param sinfo A pointer to station_info structure
*
* @return 0 -- success, otherwise fail
*/
static mlan_status
woal_cfg80211_dump_station_info(moal_private *priv, struct station_info *sinfo)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
mlan_ds_get_signal signal;
mlan_ds_get_stats stats;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
mlan_bss_info bss_info;
t_u8 dtim_period = 0;
#endif
ENTER();
if (priv->phandle->scan_pending_on_block) {
if (priv->sinfo)
moal_memcpy_ext(priv->phandle, sinfo, priv->sinfo,
sizeof(struct station_info),
sizeof(struct station_info));
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
sinfo->filled = MBIT(NL80211_STA_INFO_RX_BYTES) |
MBIT(NL80211_STA_INFO_TX_BYTES) |
MBIT(NL80211_STA_INFO_RX_PACKETS) |
MBIT(NL80211_STA_INFO_TX_PACKETS) |
MBIT(NL80211_STA_INFO_SIGNAL) |
MBIT(NL80211_STA_INFO_TX_BITRATE) |
MBIT(NL80211_STA_INFO_RX_BITRATE);
#else
sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES |
STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS |
STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
sinfo->filled |= MBIT(NL80211_STA_INFO_TX_FAILED);
#else
sinfo->filled |= STATION_INFO_TX_FAILED;
#endif
#endif
/* Get signal information from the firmware */
memset(&signal, 0, sizeof(mlan_ds_get_signal));
if (MLAN_STATUS_SUCCESS !=
woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
PRINTM(MERROR, "Error getting signal information\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
/* Get stats information from the firmware */
memset(&stats, 0, sizeof(mlan_ds_get_stats));
if (MLAN_STATUS_SUCCESS !=
woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) {
PRINTM(MERROR, "Error getting stats information\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
sinfo->rx_bytes = priv->stats.rx_bytes;
sinfo->tx_bytes = priv->stats.tx_bytes;
sinfo->rx_packets = priv->stats.rx_packets;
sinfo->tx_packets = priv->stats.tx_packets;
sinfo->signal = signal.bcn_rssi_avg;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
sinfo->tx_failed = stats.failed;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
/* Update BSS information */
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
sinfo->filled |= MBIT(NL80211_STA_INFO_BSS_PARAM);
#else
sinfo->filled |= STATION_INFO_BSS_PARAM;
#endif
sinfo->bss_param.flags = 0;
ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
if (ret)
goto done;
if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_PREAMBLE)
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_SLOT_TIME)
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
sinfo->bss_param.beacon_interval = bss_info.beacon_interval;
/* Get DTIM period */
ret = woal_set_get_dtim_period(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT,
&dtim_period);
if (ret) {
PRINTM(MERROR, "Get DTIM period failed\n");
goto done;
}
sinfo->bss_param.dtim_period = dtim_period;
#endif
woal_cfg80211_fill_rate_info(priv, sinfo);
if (priv->sinfo)
moal_memcpy_ext(priv->phandle, priv->sinfo, sinfo,
sizeof(struct station_info),
sizeof(struct station_info));
done:
LEAVE();
return ret;
}
/********************************************************
Global Functions
********************************************************/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
/**
* @brief Set all radar channel's dfs_state
*
* @param wiphy A pointer to wiphy structure
*
* @return N/A
*/
void
woal_update_radar_chans_dfs_state(struct wiphy *wiphy)
{
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
enum ieee80211_band band;
struct ieee80211_supported_band *sband;
int i;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
sband = wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
if (sband->channels[i].flags & IEEE80211_CHAN_RADAR) {
if (moal_extflg_isset(handle, EXT_DFS_OFFLOAD))
sband->channels[i].dfs_state =
NL80211_DFS_AVAILABLE;
else
sband->channels[i].dfs_state =
NL80211_DFS_USABLE;
}
}
}
PRINTM(MCMND, "Set radar dfs_state: dfs_offload=%d\n",
moal_extflg_isset(handle, EXT_DFS_OFFLOAD));
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
/**
* @brief This function will be used by sort, to compare LHS & RHS
*
* @param lhs LHS value
* @param rhs RHS value
* @return 0
*/
static int
compare(const void *lhs, const void *rhs)
{
const chan_freq_power_t *lhs_cfp = (const chan_freq_power_t *)(lhs);
const chan_freq_power_t *rhs_cfp = (const chan_freq_power_t *)(rhs);
if (lhs_cfp->channel < rhs_cfp->channel)
return -1;
if (lhs_cfp->channel > rhs_cfp->channel)
return 1;
return 0;
}
/**
* @brief This function get channel reg_rule flags
*
* @param buf Buffer containing channel region config
* @param num_chan Length of buffer
* @param regd ieee80211_regdomain to be updated
*
* @return N/A
*/
t_u32
woal_get_chan_rule_flags(mlan_ds_custom_reg_domain * custom_reg, t_u8 channel)
{
t_u16 num_chan = 0;
t_u32 flags = 0;
int idx;
t_u16 chflags;
num_chan = custom_reg->num_bg_chan;
num_chan += custom_reg->num_a_chan;
for (idx = 0; idx < num_chan; idx++) {
chflags = custom_reg->cfp_tbl[idx].dynamic.flags;
if (chflags & NXP_CHANNEL_DISABLED)
continue;
if (custom_reg->cfp_tbl[idx].channel == channel) {
if (chflags & NXP_CHANNEL_PASSIVE)
flags |= NL80211_RRF_NO_IR;
if (chflags & NXP_CHANNEL_DFS)
flags |= NL80211_RRF_DFS;
if (chflags & NXP_CHANNEL_NO_OFDM)
flags |= NL80211_RRF_NO_OFDM;
break;
}
}
return flags;
}
/**
* @brief This function update the beaconng flags of channel
*
* @param custom_reg pointer to mlan_ds_custom_reg_domain
*
* @return pointer to ieee80211_regdomain
*/
static void
woal_reg_apply_beaconing_flags(struct wiphy *wiphy,
mlan_ds_custom_reg_domain * custom_reg)
{
struct ieee80211_supported_band *sband;
struct ieee80211_channel *ch;
int band, i;
t_u32 rule_flags = 0;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
sband = wiphy->bands[band];
if (!sband)
continue;
for (i = 0; i < sband->n_channels; i++) {
ch = &sband->channels[i];
if (ch->flags &
(IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR))
continue;
rule_flags =
woal_get_chan_rule_flags(custom_reg,
ch->hw_value);
if (!(rule_flags & NL80211_RRF_NO_IR))
ch->flags &= ~IEEE80211_CHAN_NO_IR;
}
}
}
/**
* @brief This function create the custom regdomain
*
* @param custom_reg pointer to mlan_ds_custom_reg_domain
*
* @return pointer to ieee80211_regdomain
*/
static struct ieee80211_regdomain *
create_custom_regdomain(mlan_ds_custom_reg_domain * custom_reg)
{
struct ieee80211_reg_rule *rule;
bool new_rule;
int idx, freq, prev_freq = 0;
t_u8 chan;
t_u16 num_chan = 0;
t_u32 bw, prev_bw = 0;
t_u16 chflags, prev_chflags = 0, valid_rules = 0;
struct ieee80211_regdomain *regd = NULL;
int regd_size;
const struct ieee80211_freq_range *freq_range = NULL;
num_chan = custom_reg->num_bg_chan;
num_chan += custom_reg->num_a_chan;
sort(&custom_reg->cfp_tbl[custom_reg->num_bg_chan],
custom_reg->num_a_chan, sizeof(chan_freq_power_t), &compare, NULL);
regd_size = sizeof(struct ieee80211_regdomain) +
num_chan * sizeof(struct ieee80211_reg_rule);
regd = kzalloc(regd_size, GFP_KERNEL);
if (!regd) {
return NULL;
}
for (idx = 0; idx < num_chan; idx++) {
enum ieee80211_band band;
chan = custom_reg->cfp_tbl[idx].channel;
if (!chan) {
if (regd)
kfree(regd);
return NULL;
}
chflags = custom_reg->cfp_tbl[idx].dynamic.flags;
band = (chan <= 14) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
freq = ieee80211_channel_to_frequency(chan, band);
PRINTM(MINFO, "chan=%d freq=%d chan_flag=0x%x\n", chan, freq,
chflags);
new_rule = false;
if (chflags & NXP_CHANNEL_DISABLED)
continue;
if (band == IEEE80211_BAND_5GHZ) {
if (!(chflags & NXP_CHANNEL_NOHT80))
bw = MHZ_TO_KHZ(80);
else if (!(chflags & NXP_CHANNEL_NOHT40))
bw = MHZ_TO_KHZ(40);
else
bw = MHZ_TO_KHZ(20);
} else {
if (!(chflags & NXP_CHANNEL_NOHT40))
bw = MHZ_TO_KHZ(40);
else
bw = MHZ_TO_KHZ(20);
}
if (idx == 0 || prev_chflags != chflags || prev_bw != bw ||
freq - prev_freq > 20) {
valid_rules++;
new_rule = true;
}
rule = &regd->reg_rules[valid_rules - 1];
rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10);
prev_chflags = chflags;
prev_freq = freq;
prev_bw = bw;
if (!new_rule)
continue;
rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10);
rule->power_rule.max_eirp = DBM_TO_MBM(19);
rule->flags = 0;
if (chflags & NXP_CHANNEL_PASSIVE)
rule->flags |= NL80211_RRF_NO_IR;
if (chflags & NXP_CHANNEL_DFS)
rule->flags |= NL80211_RRF_DFS;
if (chflags & NXP_CHANNEL_NO_OFDM)
rule->flags |= NL80211_RRF_NO_OFDM;
rule->freq_range.max_bandwidth_khz = bw;
}
regd->n_reg_rules = valid_rules;
if (custom_reg->region.country_code[0] == 'W' &&
custom_reg->region.country_code[1] == 'W') {
regd->alpha2[0] = '0';
regd->alpha2[1] = '0';
} else {
/* set alpha2 from FW. */
regd->alpha2[0] = custom_reg->region.country_code[0];
regd->alpha2[1] = custom_reg->region.country_code[1];
}
switch (custom_reg->region.dfs_region) {
case 1:
regd->dfs_region = NL80211_DFS_FCC;
break;
case 2:
regd->dfs_region = NL80211_DFS_ETSI;
break;
case 3:
regd->dfs_region = NL80211_DFS_JP;
break;
default:
regd->dfs_region = NL80211_DFS_UNSET;
break;
}
PRINTM(MCMND, "create_custom_regdomain: %c%c rules=%d dfs_region=%d\n",
regd->alpha2[0], regd->alpha2[1], valid_rules, regd->dfs_region);
for (idx = 0; idx < (int)regd->n_reg_rules; idx++) {
rule = &regd->reg_rules[idx];
freq_range = &rule->freq_range;
PRINTM(MCMND,
"flags=0x%x star_freq=%d end_freq=%d freq_diff=%d max_bandwidth=%d\n",
rule->flags, freq_range->start_freq_khz,
freq_range->end_freq_khz,
freq_range->end_freq_khz - freq_range->start_freq_khz,
freq_range->max_bandwidth_khz);
}
if (!regd->n_reg_rules) {
kfree(regd);
regd = NULL;
}
return regd;
}
/**
* @brief create custom channel regulatory config
*
* @param priv A pointer to moal_private structure
*
* @return 0-success, otherwise failure
*/
static int
woal_update_custom_regdomain(moal_private *priv, struct wiphy *wiphy)
{
mlan_ds_misc_cfg *misc = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
t_u8 country_code[COUNTRY_CODE_LEN];
int ret = 0;
struct ieee80211_regdomain *regd = NULL;
ENTER();
if (!priv || !wiphy) {
LEAVE();
return -EFAULT;
}
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (req == NULL) {
ret = -EFAULT;
goto done;
}
misc = (mlan_ds_misc_cfg *)req->pbuf;
misc->sub_command = MLAN_OID_MISC_GET_CHAN_REGION_CFG;
req->req_id = MLAN_IOCTL_MISC_CFG;
req->action = MLAN_ACT_GET;
memset(&misc->param.custom_reg_domain, 0,
sizeof(misc->param.custom_reg_domain));
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
memset(country_code, 0, sizeof(country_code));
if (MTRUE ==
is_cfg80211_special_region_code(priv->phandle->country_code)) {
country_code[0] = 'W';
country_code[1] = 'W';
} else {
country_code[0] = priv->phandle->country_code[0];
country_code[1] = priv->phandle->country_code[1];
}
if (misc->param.custom_reg_domain.region.country_code[0] == '\0'
|| misc->param.custom_reg_domain.region.country_code[1] == '\0') {
PRINTM(MCMND, "FW country code not valid\n");
ret = -EFAULT;
goto done;
}
if (misc->param.custom_reg_domain.region.country_code[0] !=
country_code[0] ||
misc->param.custom_reg_domain.region.country_code[1] !=
country_code[1]) {
PRINTM(MCMND, "FW country code %c%c not match %c%c\n",
misc->param.custom_reg_domain.region.country_code[0],
misc->param.custom_reg_domain.region.country_code[1],
country_code[0], country_code[1]);
}
regd = create_custom_regdomain(&misc->param.custom_reg_domain);
if (regd) {
PRINTM(MMSG, "call regulatory_set_wiphy_regd %c%c",
misc->param.custom_reg_domain.region.country_code[0],
misc->param.custom_reg_domain.region.country_code[1]);
wiphy->regulatory_flags &=
~(REGULATORY_STRICT_REG | REGULATORY_CUSTOM_REG);
wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
rtnl_lock();
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
ret = regulatory_set_wiphy_regd_sync(wiphy, regd);
#else
ret = regulatory_set_wiphy_regd_sync_rtnl(wiphy, regd);
#endif
rtnl_unlock();
kfree(regd);
if (!ret)
woal_reg_apply_beaconing_flags(wiphy,
&misc->param.
custom_reg_domain);
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief This workqueue handles create customer regulatory
* case
*
* @param work A pointer to work_struct
*
* @return N/A
*/
void
woal_regulatory_work_queue(struct work_struct *work)
{
moal_handle *handle = container_of(work, moal_handle, regulatory_work);
struct wiphy *wiphy = handle->wiphy;
moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
enum ieee80211_band band;
if (priv && wiphy) {
woal_update_custom_regdomain(priv, wiphy);
band = priv->phandle->band;
priv->phandle->band = IEEE80211_BAND_2GHZ;
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
priv->phandle->band = IEEE80211_BAND_5GHZ;
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
priv->phandle->band = band;
}
}
#endif
/**
* @brief Request the driver to change regulatory domain
*
* @param wiphy A pointer to wiphy structure
* @param request A pointer to regulatory_request structure
*
* @return 0
*/
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
static void
#else
static int
#endif
woal_cfg80211_reg_notifier(struct wiphy *wiphy,
struct regulatory_request *request)
{
moal_private *priv = NULL;
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
t_u8 region[COUNTRY_CODE_LEN];
enum ieee80211_band band;
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
int ret = 0;
#endif
t_u8 load_power_table = MFALSE;
mlan_fw_info fw_info;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
char *reg_alpha2 = NULL;
#endif
ENTER();
priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
if (!priv) {
PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__);
LEAVE();
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
return -EINVAL;
#else
return;
#endif
}
PRINTM(MCMND,
"cfg80211 regulatory domain callback "
"%c%c initiator=%d\n",
request->alpha2[0], request->alpha2[1], request->initiator);
memset(&fw_info, 0, sizeof(mlan_fw_info));
woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
if (fw_info.force_reg) {
PRINTM(MINFO,
"Regulatory domain is enforced in the on-chip OTP\n");
LEAVE();
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
return -EINVAL;
#else
return;
#endif
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (moal_extflg_isset(handle, EXT_DFS_OFFLOAD))
woal_update_radar_chans_dfs_state(wiphy);
#endif
memset(region, 0, sizeof(region));
moal_memcpy_ext(priv->phandle, region, request->alpha2,
sizeof(request->alpha2), sizeof(region));
region[2] = ' ';
if ((handle->country_code[0] != request->alpha2[0]) ||
(handle->country_code[1] != request->alpha2[1])) {
if (handle->params.cntry_txpwr &&
!handle->params.txpwrlimit_cfg) {
t_u8 country_code[COUNTRY_CODE_LEN];
handle->country_code[0] = request->alpha2[0];
handle->country_code[1] = request->alpha2[1];
handle->country_code[2] = ' ';
memset(country_code, 0, sizeof(country_code));
if (MTRUE == is_cfg80211_special_region_code(region)) {
country_code[0] = 'W';
country_code[1] = 'W';
} else {
country_code[0] = request->alpha2[0];
country_code[1] = request->alpha2[1];
}
if (MLAN_STATUS_SUCCESS !=
woal_request_country_power_table(priv,
country_code)) {
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
return -EFAULT;
#else
return;
#endif
}
load_power_table = MTRUE;
}
}
if (MTRUE != is_cfg80211_special_region_code(region)) {
if (!handle->params.cntry_txpwr) {
handle->country_code[0] = request->alpha2[0];
handle->country_code[1] = request->alpha2[1];
handle->country_code[2] = ' ';
}
if (MLAN_STATUS_SUCCESS !=
woal_set_region_code(priv, handle->country_code))
PRINTM(MERROR, "Set country code failed!\n");
}
switch (request->initiator) {
case NL80211_REGDOM_SET_BY_DRIVER:
PRINTM(MCMND, "Regulatory domain BY_DRIVER\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
reg_alpha2 = priv->phandle->params.reg_alpha2;
if ((handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE)
&& !handle->params.txpwrlimit_cfg
&& load_power_table
&& reg_alpha2 && woal_is_valid_alpha2(reg_alpha2))
queue_work(handle->evt_workqueue,
&handle->regulatory_work);
#endif
break;
case NL80211_REGDOM_SET_BY_CORE:
PRINTM(MCMND, "Regulatory domain BY_CORE\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
if (handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE
&& load_power_table && !handle->params.txpwrlimit_cfg)
queue_work(handle->evt_workqueue,
&handle->regulatory_work);
#endif
break;
case NL80211_REGDOM_SET_BY_USER:
PRINTM(MCMND, "Regulatory domain BY_USER\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
if (handle->params.cntry_txpwr == CNTRY_RGPOWER_MODE
&& load_power_table && !handle->params.txpwrlimit_cfg)
queue_work(handle->evt_workqueue,
&handle->regulatory_work);
#endif
break;
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
PRINTM(MCMND, "Regulatory domain BY_COUNTRY_IE\n");
break;
}
if (priv->wdev && priv->wdev->wiphy &&
(request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
band = priv->phandle->band;
priv->phandle->band = IEEE80211_BAND_2GHZ;
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
priv->phandle->band = IEEE80211_BAND_5GHZ;
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
priv->phandle->band = band;
}
LEAVE();
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
return ret;
#endif
}
#ifdef UAP_CFG80211
/**
* @brief Swithces BSS role of interface
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT)
* @param bss_role bss role
*
* @return 0 --success, otherwise fail
*/
static mlan_status
woal_role_switch(moal_private *priv, t_u8 wait_option, t_u8 bss_role)
{
int ret = 0;
mlan_ds_bss *bss = NULL;
mlan_ioctl_req *req = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_BSS_ROLE;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
bss->param.bss_role = bss_role;
status = woal_request_ioctl(priv, req, wait_option);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief set/get bandcfg
*
* @param priv A pointer to moal_private structure
* @param action get or set action
* @param band_cfg A pointer to mlan_ds_band_cfg structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_setget_bandcfg(moal_private *priv, t_u8 action, mlan_ds_band_cfg *band_cfg)
{
int ret = 0;
mlan_ioctl_req *req = NULL;
mlan_ds_radio_cfg *radio_cfg = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = -ENOMEM;
goto error;
}
radio_cfg = (mlan_ds_radio_cfg *)req->pbuf;
radio_cfg->sub_command = MLAN_OID_BAND_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
req->action = action;
if (req->action == MLAN_ACT_SET)
moal_memcpy_ext(priv->phandle, &radio_cfg->param.band_cfg,
band_cfg, sizeof(mlan_ds_band_cfg),
sizeof(radio_cfg->param.band_cfg));
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto error;
}
moal_memcpy_ext(priv->phandle, band_cfg, &radio_cfg->param.band_cfg,
sizeof(mlan_ds_band_cfg), sizeof(mlan_ds_band_cfg));
error:
if (status != MLAN_STATUS_PENDING)
kfree(req);
LEAVE();
return ret;
}
/**
* @brief request scan
*
* @param priv A pointer to moal_private structure
* @param scan_cfg A pointer to wlan_user_scan_cfg structure
*
* @return MLAN_STATUS_SUCCESS -- success, otherwise fail
*/
static mlan_status
woal_uap_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
moal_handle *handle = priv->phandle;
moal_private *tmp_priv;
u8 role;
mlan_ds_band_cfg org_bandcfg;
mlan_ds_band_cfg bandcfg;
u8 band_change = MFALSE;
ENTER();
if (priv->bss_index > 0)
tmp_priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
else
tmp_priv = priv;
if (!tmp_priv) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
role = GET_BSS_ROLE(tmp_priv);
if (role == MLAN_BSS_ROLE_UAP)
woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_STA);
if (tmp_priv != priv) {
woal_setget_bandcfg(priv, MLAN_ACT_GET, &bandcfg);
woal_setget_bandcfg(tmp_priv, MLAN_ACT_GET, &org_bandcfg);
if (bandcfg.config_bands != org_bandcfg.config_bands) {
woal_setget_bandcfg(tmp_priv, MLAN_ACT_SET, &bandcfg);
band_change = MTRUE;
}
}
#ifdef REASSOCIATION
if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) {
PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n");
goto done;
}
#endif /* REASSOCIATION */
tmp_priv->report_scan_result = MTRUE;
ret = woal_request_userscan(tmp_priv, MOAL_IOCTL_WAIT, scan_cfg);
woal_sched_timeout(5);
#ifdef REASSOCIATION
MOAL_REL_SEMAPHORE(&handle->reassoc_sem);
#endif
done:
if (role == MLAN_BSS_ROLE_UAP)
woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_UAP);
if (band_change)
woal_setget_bandcfg(tmp_priv, MLAN_ACT_SET, &org_bandcfg);
LEAVE();
return ret;
}
#endif
static int
woal_find_wps_ie_in_probereq(const t_u8 *ie, int len)
{
int left_len = len;
const t_u8 *pos = ie;
t_u8 ie_id, ie_len;
IEEEtypes_VendorSpecific_t *pvendor_ie = NULL;
const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 };
while (left_len >= 2) {
ie_id = *pos;
ie_len = *(pos + 1);
if ((ie_len + 2) > left_len)
break;
if (ie_id == VENDOR_SPECIFIC_221) {
pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos;
if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui,
sizeof(pvendor_ie->vend_hdr.oui)) &&
pvendor_ie->vend_hdr.oui_type == wps_oui[3])
return MTRUE;
}
pos += (ie_len + 2);
left_len -= (ie_len + 2);
}
return MFALSE;
}
/** scan result expired value */
#define SCAN_RESULT_EXPIRTED 1
/**
* @brief check if the scan result expired
*
* @param priv A pointer to moal_private
*
*
* @return MTRUE/MFALSE;
*/
static t_u8
woal_is_uap_scan_result_expired(moal_private *priv)
{
mlan_scan_resp scan_resp;
wifi_timeval t;
ENTER();
if (MLAN_STATUS_SUCCESS !=
woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
LEAVE();
return MTRUE;
}
if (!scan_resp.num_in_scan_table) {
LEAVE();
return MTRUE;
}
woal_get_monotonic_time(&t);
if (t.time_sec > (scan_resp.age_in_secs + SCAN_RESULT_EXPIRTED)) {
LEAVE();
return MTRUE;
}
LEAVE();
return MFALSE;
}
/**
* @brief check if the scan result expired
*
* @param priv A pointer to moal_private
*
*
* @return MTRUE/MFALSE;
*/
static t_u8
woal_is_scan_result_expired(moal_private *priv)
{
mlan_scan_resp scan_resp;
wifi_timeval t;
ENTER();
// Don't block ACS scan
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
LEAVE();
return MTRUE;
}
//Don't block scan when non any interface active
if (!woal_is_any_interface_active(priv->phandle)) {
LEAVE();
return MTRUE;
}
#if defined(WIFI_DIRECT_SUPPORT)
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
//Do not skip p2p interface connect scan
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
LEAVE();
return MTRUE;
}
#endif
#endif
if (!priv->media_connected) {
LEAVE();
return MTRUE;
}
if (MLAN_STATUS_SUCCESS !=
woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
LEAVE();
return MTRUE;
}
if (!scan_resp.num_in_scan_table) {
LEAVE();
return MTRUE;
}
woal_get_monotonic_time(&t);
if (t.time_sec > (scan_resp.age_in_secs + SCAN_RESULT_EXPIRTED)) {
LEAVE();
return MTRUE;
}
LEAVE();
return MFALSE;
}
/**
* @brief check if the scan result ageout
*
* @param priv A pointer to moal_private
*
*
* @return MTRUE/MFALSE;
*/
t_u8
wlan_check_scan_table_ageout(moal_private *priv)
{
mlan_scan_resp scan_resp;
wifi_timeval t;
ENTER();
if (MLAN_STATUS_SUCCESS !=
woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
LEAVE();
return MFALSE;
}
woal_get_monotonic_time(&t);
#define CFG80211_SCAN_RESULT_AGEOUT 10
if (t.time_sec > (scan_resp.age_in_secs + CFG80211_SCAN_RESULT_AGEOUT)) {
LEAVE();
return MFALSE;
}
PRINTM(MCMND, "Scan: Keep Previous result\n");
LEAVE();
return MTRUE;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
/**
* @brief Request the driver to do a scan. Always returning
* zero meaning that the scan request is given to driver,
* and will be valid until passed to cfg80211_scan_done().
* To inform scan results, call cfg80211_inform_bss().
*
* @param wiphy A pointer to wiphy structure
* @param request A pointer to cfg80211_scan_request structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
#else
/**
* @brief Request the driver to do a scan. Always returning
* zero meaning that the scan request is given to driver,
* and will be valid until passed to cfg80211_scan_done().
* To inform scan results, call cfg80211_inform_bss().
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param request A pointer to cfg80211_scan_request structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_scan_request *request)
#endif
{
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct net_device *dev = request->wdev->netdev;
#endif
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
wlan_user_scan_cfg *scan_req = NULL;
mlan_bss_info bss_info;
mlan_scan_cfg scan_cfg;
struct ieee80211_channel *chan;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
t_u8 buf[ETH_ALEN];
#endif
int ret = 0, i;
unsigned long flags;
ENTER();
PRINTM(MINFO, "Received scan request on %s\n", dev->name);
#if defined(WIFI_LATENCY_MODE_SUPPORT)
if (MTRUE == priv->scan_suppressed) {
PRINTM(MINFO, "Suppress scan request per config\n");
ret = -EAGAIN;
goto done;
}
#endif // defined(WIFI_LATENCY_MODE_SUPPORT)
if (priv->phandle->scan_pending_on_block == MTRUE) {
PRINTM(MCMND, "scan already in processing...\n");
LEAVE();
return -EAGAIN;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
if (priv->last_event & EVENT_BG_SCAN_REPORT) {
PRINTM(MCMND, "block scan while pending BGSCAN result\n");
priv->last_event = 0;
LEAVE();
return -EAGAIN;
}
#endif
#if defined(STA_CFG80211) || defined(UAP_CFG80211)
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
if (priv->phandle->is_go_timer_set &&
priv->wdev->iftype != NL80211_IFTYPE_P2P_GO) {
PRINTM(MCMND, "block scan in go timer....\n");
LEAVE();
return -EAGAIN;
}
#endif
#endif
#endif
cancel_delayed_work_sync(&priv->phandle->scan_timeout_work);
priv->phandle->fake_scan_complete = priv->fake_scan_complete;
if (priv->fake_scan_complete || !woal_is_scan_result_expired(priv)) {
priv->phandle->fake_scan_complete = MTRUE;
PRINTM(MEVENT,
"fake scan complete flag is on\n");
priv->phandle->scan_request = request;
queue_delayed_work(priv->phandle->evt_workqueue,
&priv->phandle->scan_timeout_work, msecs_to_jiffies(1000));
return MLAN_STATUS_SUCCESS;
}
memset(&bss_info, 0, sizeof(bss_info));
if (MLAN_STATUS_SUCCESS ==
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
if (bss_info.scan_block) {
PRINTM(MEVENT, "Block scan in mlan module\n");
return -EAGAIN;
}
}
if (priv->phandle->scan_request &&
priv->phandle->scan_request != request) {
PRINTM(MCMND,
"different scan_request is coming before previous one is finished on %s...\n",
dev->name);
LEAVE();
return -EBUSY;
}
spin_lock_irqsave(&priv->phandle->scan_req_lock, flags);
priv->phandle->scan_request = request;
spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags);
if (is_zero_timeval(priv->phandle->scan_time_start)) {
woal_get_monotonic_time(&priv->phandle->scan_time_start);
PRINTM(MINFO, "%s : start_timeval=%d:%d \n", __func__,
priv->phandle->scan_time_start.time_sec,
priv->phandle->scan_time_start.time_usec);
}
scan_req = kmalloc(sizeof(wlan_user_scan_cfg), GFP_KERNEL);
memset(scan_req, 0x00, sizeof(wlan_user_scan_cfg));
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
if (!is_broadcast_ether_addr(request->bssid)) {
moal_memcpy_ext(priv->phandle, scan_req->specific_bssid,
request->bssid, ETH_ALEN,
sizeof(scan_req->specific_bssid));
PRINTM(MIOCTL, "scan: bssid=" MACSTR "\n",
MAC2STR(scan_req->specific_bssid));
}
#endif
memset(&scan_cfg, 0, sizeof(mlan_scan_cfg));
woal_get_scan_config(priv, &scan_cfg);
#ifdef WIFI_DIRECT_SUPPORT
if (priv->phandle->miracast_mode)
scan_req->scan_chan_gap = priv->phandle->scan_chan_gap;
else {
#endif
if (scan_cfg.scan_chan_gap)
scan_req->scan_chan_gap = scan_cfg.scan_chan_gap;
else if (woal_is_any_interface_active(priv->phandle))
scan_req->scan_chan_gap = priv->phandle->scan_chan_gap;
else
scan_req->scan_chan_gap = 0;
#ifdef WIFI_DIRECT_SUPPORT
}
#endif
/** indicate FW, gap is optional */
if (scan_req->scan_chan_gap && priv->phandle->pref_mac)
scan_req->scan_chan_gap |= GAP_FLAG_OPTIONAL;
if (priv->phandle->scan_request->n_channels <= 38) {
if (scan_cfg.ext_scan == 3)
scan_req->ext_scan_type = EXT_SCAN_ENHANCE;
}
for (i = 0; i < priv->phandle->scan_request->n_ssids; i++) {
moal_memcpy_ext(priv->phandle, scan_req->ssid_list[i].ssid,
priv->phandle->scan_request->ssids[i].ssid,
priv->phandle->scan_request->ssids[i].ssid_len,
sizeof(scan_req->ssid_list[i].ssid));
if (priv->phandle->scan_request->ssids[i].ssid_len)
scan_req->ssid_list[i].max_len = 0;
else
scan_req->ssid_list[i].max_len = 0xff;
PRINTM(MIOCTL, "scan: ssid=%s\n", scan_req->ssid_list[i].ssid);
}
#if defined(WIFI_DIRECT_SUPPORT)
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
priv->phandle->scan_request->n_ssids) {
if (!memcmp(scan_req->ssid_list[0].ssid, "DIRECT-", 7))
scan_req->ssid_list[0].max_len = 0xfe;
}
#endif
#endif
for (i = 0; i < (int)MIN(WLAN_USER_SCAN_CHAN_MAX,
priv->phandle->scan_request->n_channels);
i++) {
chan = priv->phandle->scan_request->channels[i];
scan_req->chan_list[i].chan_number = chan->hw_value;
scan_req->chan_list[i].radio_type = chan->band;
if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) ||
!priv->phandle->scan_request->n_ssids)
scan_req->chan_list[i].scan_type =
MLAN_SCAN_TYPE_PASSIVE;
else if (chan->flags & IEEE80211_CHAN_RADAR)
scan_req->chan_list[i].scan_type =
MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE;
else
scan_req->chan_list[i].scan_type =
MLAN_SCAN_TYPE_ACTIVE;
PRINTM(MCMD_D, "cfg80211_scan: chan=%d chan->flag=0x%x\n",
chan->hw_value, chan->flags);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
scan_req->chan_list[i].scan_time =
priv->phandle->scan_request->duration;
#else
scan_req->chan_list[i].scan_time = 0;
#endif
#if defined(WIFI_DIRECT_SUPPORT)
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
priv->phandle->scan_request->n_ssids) {
if (!memcmp(scan_req->ssid_list[0].ssid, "DIRECT-", 7))
scan_req->chan_list[i].scan_time =
MIN_SPECIFIC_SCAN_CHAN_TIME;
}
#endif
#endif
#ifdef WIFI_DIRECT_SUPPORT
if (priv->phandle->miracast_mode)
scan_req->chan_list[i].scan_time =
priv->phandle->miracast_scan_time;
else if (woal_is_any_interface_active(priv->phandle)) {
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
scan_req->chan_list[i].scan_time =
INIT_PASSIVE_SCAN_CHAN_TIME;
else
scan_req->chan_list[i].scan_time =
MIN_SPECIFIC_SCAN_CHAN_TIME;
}
#endif
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
if (!woal_is_uap_scan_result_expired(priv))
scan_req->chan_list[i].scan_time =
MIN_SPECIFIC_SCAN_CHAN_TIME;
else
scan_req->chan_list[i].scan_time =
PASSIVE_SCAN_CHAN_TIME;
}
#endif
}
if (priv->phandle->scan_request->ie &&
priv->phandle->scan_request->ie_len) {
if (woal_find_wps_ie_in_probereq((t_u8 *)priv->phandle->
scan_request->ie,
priv->phandle->scan_request->
ie_len)) {
PRINTM(MIOCTL,
"Notify firmware only keep probe response\n");
scan_req->proberesp_only = MTRUE;
}
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0,
(t_u8 *)priv->phandle->
scan_request->ie,
priv->phandle->scan_request->
ie_len, MGMT_MASK_PROBE_REQ,
MOAL_IOCTL_WAIT)) {
PRINTM(MERROR, "Fail to set scan request IE\n");
ret = -EFAULT;
goto done;
}
} else {
/** Clear SCAN IE in Firmware */
if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK)
woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0,
NULL, 0, NULL, 0,
MGMT_MASK_PROBE_REQ,
MOAL_IOCTL_WAIT);
}
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
/** use sync scan for uap */
ret = woal_uap_scan(priv, scan_req);
if (!ret) {
kfree(scan_req);
LEAVE();
return ret;
} else {
PRINTM(MERROR, "Uap SCAN failure\n");
goto done;
}
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
PRINTM(MIOCTL, "NL80211_SCAN_FLAG_RANDOM_ADDR is set\n");
get_random_bytes(buf, ETH_ALEN);
for (i = 0; i < ETH_ALEN; i++) {
buf[i] &= ~request->mac_addr_mask[i];
buf[i] |= request->mac_addr[i] &
request->mac_addr_mask[i];
}
moal_memcpy_ext(priv->phandle, scan_req->random_mac, buf,
ETH_ALEN, sizeof(scan_req->random_mac));
} else
#endif
moal_memcpy_ext(priv->phandle, scan_req->random_mac,
priv->random_mac, ETH_ALEN,
sizeof(scan_req->random_mac));
PRINTM(MCMND, "wlan:random_mac " MACSTR "\n",
MAC2STR(scan_req->random_mac));
scan_req->keep_previous_scan = wlan_check_scan_table_ageout(priv);
if (MLAN_STATUS_SUCCESS != woal_do_scan(priv, scan_req)) {
PRINTM(MERROR, "woal_do_scan fails!\n");
ret = -EAGAIN;
goto done;
}
done:
if (ret) {
spin_lock_irqsave(&priv->phandle->scan_req_lock, flags);
woal_cfg80211_scan_done(request, MTRUE);
priv->phandle->scan_request = NULL;
priv->phandle->scan_priv = NULL;
spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags);
} else{
PRINTM(MMSG, "wlan: %s START SCAN\n", dev->name);
queue_delayed_work(priv->phandle->evt_workqueue,
&priv->phandle->scan_timeout_work,
msecs_to_jiffies(priv->phandle->scan_timeout));
}
kfree(scan_req);
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
static void
woal_cfg80211_abort_scan(struct wiphy *wiphy, struct wireless_dev *wdev)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
ENTER();
PRINTM(MMSG, "wlan: ABORT SCAN start\n");
woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
LEAVE();
return;
}
#endif
/**
* @brief construct and send ft action request
*
* @param priv A pointer to moal_private structure
* @param ie A pointer to ft ie
* @param le Value of ie len
* @param bssid A pointer to target ap bssid
* @
* @return 0 -- success, otherwise fail
*/
static int
woal_send_ft_action_requst(moal_private *priv, t_u8 *ie, t_u8 len,
t_u8 *bssid, t_u8 *target_ap)
{
IEEE80211_MGMT *mgmt = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
pmlan_buffer pmbuf = NULL;
t_u32 pkt_type;
t_u32 tx_control;
t_u16 packet_len = 0;
t_u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int ret = 0;
ENTER();
/* pkt_type + tx_control */
#define HEADER_SIZE 8
/* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4 */
#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6)
/* 14 = category + action + sta addr + target ap */
#define FT_REQUEST_LEN 14
packet_len = (t_u16)len + MGMT_HEADER_LEN + FT_REQUEST_LEN;
pmbuf = woal_alloc_mlan_buffer(priv->phandle,
MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE +
packet_len + sizeof(packet_len));
if (!pmbuf) {
PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
ret = -ENOMEM;
goto done;
}
pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
tx_control = 0;
/* Add pkt_type and tx_control */
moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset,
&pkt_type, sizeof(pkt_type), sizeof(pkt_type));
moal_memcpy_ext(priv->phandle,
pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type),
&tx_control, sizeof(tx_control), sizeof(tx_control));
/*Add packet len */
moal_memcpy_ext(priv->phandle,
pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
&packet_len, sizeof(packet_len), sizeof(packet_len));
mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
HEADER_SIZE + sizeof(packet_len));
memset(mgmt, 0, MGMT_HEADER_LEN);
mgmt->frame_control =
woal_cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
moal_memcpy_ext(priv->phandle, mgmt->da, bssid, ETH_ALEN,
sizeof(mgmt->da));
moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN,
sizeof(mgmt->sa));
moal_memcpy_ext(priv->phandle, mgmt->bssid, bssid, ETH_ALEN,
sizeof(mgmt->bssid));
moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN,
sizeof(mgmt->addr4));
mgmt->u.ft_req.category = 0x06; /**ft action code 0x6*/
mgmt->u.ft_req.action = 0x1; /**ft action request*/
moal_memcpy_ext(priv->phandle, mgmt->u.ft_req.sta_addr,
priv->current_addr, ETH_ALEN,
sizeof(mgmt->u.ft_req.sta_addr));
moal_memcpy_ext(priv->phandle, mgmt->u.ft_req.target_ap_addr, target_ap,
ETH_ALEN, sizeof(mgmt->u.ft_req.target_ap_addr));
if (ie && len)
moal_memcpy_ext(priv->phandle,
(t_u8 *)(&mgmt->u.ft_req.variable), ie, len,
len);
pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len);
pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
pmbuf->bss_index = priv->bss_index;
pmbuf->priority = 7;
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
switch (status) {
case MLAN_STATUS_PENDING:
atomic_inc(&priv->phandle->tx_pending);
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
break;
case MLAN_STATUS_SUCCESS:
woal_free_mlan_buffer(priv->phandle, pmbuf);
break;
case MLAN_STATUS_FAILURE:
default:
woal_free_mlan_buffer(priv->phandle, pmbuf);
ret = -EFAULT;
break;
}
done:
LEAVE();
return ret;
}
/**
* @brief construct and send ft auth request
*
* @param priv A pointer to moal_private structure
* @param ie A pointer to ft ie
* @param le Value of ie len
* @param bssid A pointer to target ap bssid
* @
* @return 0 -- success, otherwise fail
*/
static int
woal_send_ft_auth_requst(moal_private *priv, t_u8 *ie, t_u8 len, t_u8 *bssid)
{
IEEE80211_MGMT *mgmt = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
pmlan_buffer pmbuf = NULL;
t_u32 pkt_type;
t_u32 tx_control;
t_u16 packet_len = 0;
t_u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
int ret = 0;
ENTER();
/* pkt_type + tx_control */
#define HEADER_SIZE 8
/* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4 */
#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6)
/* 6 = auth_alg + auth_transaction +auth_status */
#define AUTH_BODY_LEN 6
packet_len = (t_u16)len + MGMT_HEADER_LEN + AUTH_BODY_LEN;
pmbuf = woal_alloc_mlan_buffer(priv->phandle,
MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE +
packet_len + sizeof(packet_len));
if (!pmbuf) {
PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
ret = -ENOMEM;
goto done;
}
pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
tx_control = 0;
/* Add pkt_type and tx_control */
moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset,
&pkt_type, sizeof(pkt_type), sizeof(pkt_type));
moal_memcpy_ext(priv->phandle,
pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type),
&tx_control, sizeof(tx_control), sizeof(tx_control));
/*Add packet len */
moal_memcpy_ext(priv->phandle,
pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
&packet_len, sizeof(packet_len), sizeof(packet_len));
mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset +
HEADER_SIZE + sizeof(packet_len));
memset(mgmt, 0, MGMT_HEADER_LEN);
mgmt->frame_control =
woal_cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH);
moal_memcpy_ext(priv->phandle, mgmt->da, bssid, ETH_ALEN,
sizeof(mgmt->da));
moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN,
sizeof(mgmt->sa));
moal_memcpy_ext(priv->phandle, mgmt->bssid, bssid, ETH_ALEN,
sizeof(mgmt->bssid));
moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN,
sizeof(mgmt->addr4));
mgmt->u.auth.auth_alg = woal_cpu_to_le16(WLAN_AUTH_FT);
mgmt->u.auth.auth_transaction = woal_cpu_to_le16(1);
mgmt->u.auth.status_code = woal_cpu_to_le16(0);
if (ie && len)
moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable),
ie, len, len);
pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len);
pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
pmbuf->bss_index = priv->bss_index;
pmbuf->priority = 7;
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
switch (status) {
case MLAN_STATUS_PENDING:
atomic_inc(&priv->phandle->tx_pending);
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
break;
case MLAN_STATUS_SUCCESS:
woal_free_mlan_buffer(priv->phandle, pmbuf);
break;
case MLAN_STATUS_FAILURE:
default:
woal_free_mlan_buffer(priv->phandle, pmbuf);
ret = -EFAULT;
break;
}
done:
LEAVE();
return ret;
}
/**
* @brief connect the AP through ft over air.
*
* @param priv A pointer to moal_private structure
* @param bssid A pointer to bssid
* @param chan struct ieee80211_channel
*
* @return 0 -- success, otherwise fail
*/
static int
woal_connect_ft_over_air(moal_private *priv, t_u8 *bssid,
struct ieee80211_channel *chan)
{
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
t_u8 status = 0;
#endif
t_u8 wait_option = MOAL_IOCTL_WAIT;
int ret = 0;
long timeout = 0;
ENTER();
if (!bssid) {
PRINTM(MERROR,
"Invalid bssid, unable to connect AP to through FT\n");
LEAVE();
return -EFAULT;
}
/*enable auth register frame */
woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MTRUE);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE, &status,
chan, 0, AUTH_TX_DEFAULT_WAIT_TIME);
#endif
/*construct auth request and send out */
woal_send_ft_auth_requst(priv, priv->ft_ie, priv->ft_ie_len, bssid);
PRINTM(MMSG, "wlan: send out FT auth,wait for auth response\n");
/*wait until received auth response */
priv->ft_wait_condition = MFALSE;
timeout = wait_event_timeout(priv->ft_wait_q, priv->ft_wait_condition,
1 * HZ);
if (!timeout) {
/*connet fail */
if (!priv->ft_roaming_triggered_by_driver) {
woal_inform_bss_from_scan_result(priv, NULL,
wait_option);
cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
NULL, 0, NULL, 0,
WLAN_STATUS_SUCCESS,
GFP_KERNEL);
}
priv->ft_roaming_triggered_by_driver = MFALSE;
PRINTM(MMSG, "wlan: keep connected to bssid " MACSTR "\n",
MAC2STR(priv->cfg_bssid));
} else {
PRINTM(MMSG, "wlan: FT auth received \n");
moal_memcpy_ext(priv->phandle, priv->target_ap_bssid, bssid,
ETH_ALEN, sizeof(priv->target_ap_bssid));
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE, &status,
NULL, 0, 0);
#endif
woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE);
LEAVE();
return ret;
}
/**
* @brief connect the AP through ft over DS.
*
* @param priv A pointer to moal_private structure
* @param bssid A pointer to bssid
* @param chan struct ieee80211_channel
*
* @return 0 -- success, otherwise fail
*/
static int
woal_connect_ft_over_ds(moal_private *priv, t_u8 *bssid,
struct ieee80211_channel *pchan)
{
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
t_u8 status = 0;
#endif
t_u8 wait_option = MOAL_IOCTL_WAIT;
int ret = 0;
long timeout = 0;
ENTER();
if (priv->media_connected) {
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE,
&status, pchan, 0, 1200);
#endif
/*construct ft action request and send out */
woal_send_ft_action_requst(priv, priv->ft_ie, priv->ft_ie_len,
(t_u8 *)priv->cfg_bssid, bssid);
PRINTM(MMSG,
"wlan: send out FT request,wait for FT response\n");
/*wait until received auth response */
priv->ft_wait_condition = MFALSE;
timeout = wait_event_timeout(priv->ft_wait_q,
priv->ft_wait_condition, 1 * HZ);
if (!timeout) {
/*go over air, as current AP may be unreachable */
PRINTM(MMSG, "wlan: go over air\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
woal_cfg80211_remain_on_channel_cfg(priv, wait_option,
MTRUE, &status,
NULL, 0, 0);
#endif
woal_connect_ft_over_air(priv, bssid, pchan);
LEAVE();
return ret;
} else {
PRINTM(MMSG, "wlan: received FT response\n");
moal_memcpy_ext(priv->phandle, priv->target_ap_bssid,
bssid, ETH_ALEN,
sizeof(priv->target_ap_bssid));
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE,
&status, NULL, 0, 0);
#endif
}
LEAVE();
return ret;
}
/**
* @brief start FT Roaming.
*
* @param priv A pointer to moal_private structure
* @param ssid_bssid A pointer to mlan_ssid_bssid structure
*
*
* @return 0 -- success, otherwise fail
*/
static int
woal_start_ft_roaming(moal_private *priv, mlan_ssid_bssid *ssid_bssid)
{
struct ieee80211_channel chan;
int ret = 0;
ENTER();
PRINTM(MEVENT, "Try to start FT roaming......\n");
chan.band = (ssid_bssid->channel < 36) ? IEEE80211_BAND_2GHZ :
IEEE80211_BAND_5GHZ;
chan.center_freq = ieee80211_channel_to_frequency(ssid_bssid->channel
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
, chan.band
#endif
);
if (!(priv->last_event & EVENT_PRE_BCN_LOST) &&
(ssid_bssid->ft_cap & MBIT(0))) {
woal_connect_ft_over_ds(priv, (t_u8 *)&ssid_bssid->bssid,
&chan);
} else {
/*if pre beacon lost, it need to send auth request instead ft
* action request when ft over ds */
woal_connect_ft_over_air(priv, (t_u8 *)&ssid_bssid->bssid,
&chan);
}
LEAVE();
return ret;
}
/**
* @brief Request the driver to connect to the ESS with
* the specified parameters from kernel
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param sme A pointer to cfg80211_connect_params structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
int ret = 0;
mlan_bss_info bss_info;
unsigned long flags;
mlan_ds_misc_assoc_rsp *assoc_rsp = NULL;
IEEEtypes_AssocRsp_t *passoc_rsp = NULL;
mlan_ssid_bssid ssid_bssid;
moal_handle *handle = priv->phandle;
int i;
ENTER();
PRINTM(MINFO, "Received association request on %s\n", dev->name);
priv->cfg_disconnect = MFALSE;
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
LEAVE();
return 0;
}
#endif
if (priv->wdev->iftype != NL80211_IFTYPE_STATION
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
&& priv->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT
#endif /* KERNEL_VERSION */
#endif /* WIFI_DIRECT_SUPPORT */
) {
PRINTM(MERROR,
"Received infra assoc request when station not in infra mode\n");
LEAVE();
return -EINVAL;
}
memset(&ssid_bssid, 0, sizeof(ssid_bssid));
moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid.ssid, sme->ssid,
sme->ssid_len, sizeof(ssid_bssid.ssid.ssid));
ssid_bssid.ssid.ssid_len = sme->ssid_len;
if (sme->bssid)
moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, sme->bssid,
ETH_ALEN, sizeof(ssid_bssid.bssid));
/* Not allowed to connect to the same AP which is already connected
with other interface */
for (i = 0; i < handle->priv_num; i++) {
if (handle->priv[i] != priv &&
MTRUE == woal_is_connected(handle->priv[i], &ssid_bssid)) {
PRINTM(MMSG,
"wlan: already connected with other interface, bssid "
MACSTR "\n",
MAC2STR(handle->priv[i]->cfg_bssid));
LEAVE();
return -EINVAL;
}
}
/** cancel pending scan */
woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT &&
(priv->wdev->iftype == NL80211_IFTYPE_STATION ||
priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
/* if bsstype == wifi direct, and iftype == station or p2p
* client, that means wpa_supplicant wants to enable wifi direct
* functionality, so we should init p2p client.
*
* Note that due to kernel iftype check, ICS wpa_supplicant
* could not updaet iftype to init p2p client, so we have to
* done it here.
* */
if (MLAN_STATUS_SUCCESS != woal_cfg80211_init_p2p_client(priv)) {
PRINTM(MERROR,
"Init p2p client for wpa_supplicant failed.\n");
ret = -EFAULT;
LEAVE();
return ret;
}
}
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
/* WAR for P2P connection with vendor TV */
woal_sched_timeout(200);
}
#endif
#endif
/*11r roaming triggered by supplicant */
if (priv->media_connected && priv->ft_ie_len &&
!(priv->ft_cap & MBIT(0))) {
/** get current bss info */
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
/** get target bss info */
if (MLAN_STATUS_SUCCESS !=
woal_find_essid(priv, &ssid_bssid, MOAL_IOCTL_WAIT)) {
ret = woal_cfg80211_connect_scan(priv, sme,
MOAL_IOCTL_WAIT);
if (!ret) {
if (MLAN_STATUS_SUCCESS !=
woal_find_best_network(priv,
MOAL_IOCTL_WAIT,
&ssid_bssid)) {
PRINTM(MERROR,
"can't find targe AP \n");
// LEAVE();
// return -EFAULT;
}
}
}
if (bss_info.mdid == ssid_bssid.ft_md &&
bss_info.ft_cap == ssid_bssid.ft_cap) {
ret = woal_start_ft_roaming(priv, &ssid_bssid);
LEAVE();
return 0;
}
}
priv->cfg_connect = MTRUE;
if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE)
woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE);
priv->assoc_status = 0;
assoc_rsp = kzalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_ATOMIC);
if (!assoc_rsp) {
PRINTM(MERROR, "Failed to allocate memory for assoc_rsp\n");
ret = -ENOMEM;
LEAVE();
return ret;
}
ret = woal_cfg80211_assoc(priv, (void *)sme, MOAL_IOCTL_WAIT,
assoc_rsp);
if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE)
woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE);
if (!ret) {
passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp->assoc_resp_buf;
priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD;
if (priv->bss_type == MLAN_BSS_TYPE_STA)
woal_save_conn_params(priv, sme);
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
priv->channel = bss_info.bss_chan;
if (!ssid_bssid.ft_md) {
priv->ft_ie_len = 0;
priv->ft_pre_connect = MFALSE;
priv->ft_md = 0;
priv->ft_cap = 0;
}
}
spin_lock_irqsave(&priv->connect_lock, flags);
priv->cfg_connect = MFALSE;
if (!ret && priv->media_connected) {
PRINTM(MMSG,
"wlan: Connected to bssid " MACSTR " successfully\n",
MAC2STR(priv->cfg_bssid));
spin_unlock_irqrestore(&priv->connect_lock, flags);
cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0,
passoc_rsp->ie_buffer,
assoc_rsp->assoc_resp_len -
ASSOC_RESP_FIXED_SIZE,
WLAN_STATUS_SUCCESS, GFP_KERNEL);
} else {
PRINTM(MINFO, "wlan: Failed to connect to bssid " MACSTR "\n",
MAC2STR(priv->cfg_bssid));
memset(priv->cfg_bssid, 0, ETH_ALEN);
spin_unlock_irqrestore(&priv->connect_lock, flags);
cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0,
NULL, 0, woal_get_assoc_status(priv),
GFP_KERNEL);
}
kfree(assoc_rsp);
assoc_rsp = NULL;
LEAVE();
return 0;
}
/**
* @brief This function will print diconnect reason code according
* to IEEE 802.11 spec
*
* @param reason_code reason code for the deauth/disaccoc
* received from firmware
* @return N/A
*/
static void
woal_print_disconnect_reason(t_u16 reason_code)
{
ENTER();
switch (reason_code) {
case MLAN_REASON_UNSPECIFIED:
PRINTM(MMSG, "wlan: REASON: Unspecified reason\n");
break;
case MLAN_REASON_PREV_AUTH_NOT_VALID:
PRINTM(MMSG,
"wlan: REASON: Previous authentication no longer valid\n");
break;
case MLAN_REASON_DEAUTH_LEAVING:
PRINTM(MMSG,
"wlan: REASON: (Deauth) Sending STA is leaving (or has left) IBSS or ESS\n");
break;
case MLAN_REASON_DISASSOC_DUE_TO_INACTIVITY:
PRINTM(MMSG,
"wlan: REASON: Disassociated due to inactivity \n");
break;
case MLAN_REASON_DISASSOC_AP_BUSY:
PRINTM(MMSG,
"wlan: REASON: (Disassociated) AP unable to handle all connected STAs\n");
break;
case MLAN_REASON_CLASS2_FRAME_FROM_NOAUTH_STA:
PRINTM(MMSG,
"wlan: REASON: Class 2 frame was received from nonauthenticated STA\n");
break;
case MLAN_REASON_CLASS3_FRAME_FROM_NOASSOC_STA:
PRINTM(MMSG,
"wlan: REASON: Class 3 frame was received from nonassociated STA\n");
break;
case MLAN_REASON_DISASSOC_STA_HAS_LEFT:
PRINTM(MMSG,
"wlan: REASON: (Disassocated) Sending STA is leaving (or has left) BSS\n");
break;
case MLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH:
PRINTM(MMSG,
"wlan: REASON: STA requesting (re)assoc is not authenticated with responding STA\n");
break;
default:
break;
}
LEAVE();
return;
}
/**
* @brief Request the driver to disconnect
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param reason_code Reason code
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
t_u16 reason_code)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
PRINTM(MMSG,
"wlan: Received disassociation request on %s, reason: %u\n",
dev->name, reason_code);
woal_print_disconnect_reason(reason_code);
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
LEAVE();
return 0;
}
#endif
if (priv->phandle->driver_status) {
PRINTM(MERROR,
"Block woal_cfg80211_disconnect in abnormal driver state\n");
LEAVE();
return -EFAULT;
}
if (priv->media_connected == MFALSE) {
PRINTM(MMSG, " Already disconnected\n");
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
if (priv->wdev->current_bss &&
(priv->wdev->iftype == NL80211_IFTYPE_STATION ||
priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) {
priv->cfg_disconnect = MTRUE;
cfg80211_disconnected(priv->netdev, 0, NULL, 0,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
true,
#endif
GFP_KERNEL);
}
#endif
LEAVE();
return 0;
}
if (priv->cfg_disconnect) {
PRINTM(MERROR, "Disassociation already in progress\n");
LEAVE();
return 0;
}
/** cancel pending scan */
woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
priv->cfg_disconnect = MTRUE;
if (woal_disconnect(priv, MOAL_IOCTL_WAIT_TIMEOUT, priv->cfg_bssid,
reason_code) != MLAN_STATUS_SUCCESS) {
priv->cfg_disconnect = MFALSE;
LEAVE();
return -EFAULT;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
if (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)
cfg80211_disconnected(priv->netdev, 0, NULL, 0,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
true,
#endif
GFP_KERNEL);
#endif
memset(priv->cfg_bssid, 0, ETH_ALEN);
if (priv->bss_type == MLAN_BSS_TYPE_STA)
woal_clear_conn_params(priv);
priv->channel = 0;
LEAVE();
return 0;
}
/**
* @brief This function is deauthentication handler when host MLME
* enable.
* In this case driver will prepare and send Deauth Req.
*
* @param wiphy A pointer to wiphy.
*
* @param dev A pointer to net_device
*
* @param req A pointer to cfg80211_deauth_request
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_deauthenticate(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_deauth_request *req)
{
int ret = 0;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
if (priv->host_mlme) {
priv->host_mlme = MFALSE;
priv->auth_flag = 0;
priv->auth_alg = 0xFFFF;
/*send deauth packet to notify disconnection to wpa_supplicant */
woal_deauth_event(priv, req->reason_code);
}
#endif
ret = woal_cfg80211_disconnect(wiphy, dev, req->reason_code);
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
if (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)
cfg80211_disconnected(priv->netdev, 0, NULL, 0, GFP_KERNEL);
#endif
return ret;
}
/**
* @brief This function is disassociation handler when host MLME
* enable.
* In this case driver will prepare and send Disassoc frame.
*
* @param wiphy A pointer to wiphy.
*
* @param dev A pointer to net_device
*
* @param req A pointer to cfg80211_disassoc_request
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_disassociate(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_disassoc_request *req)
{
int ret = 0;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
if (priv->host_mlme) {
priv->host_mlme = MFALSE;
priv->auth_flag = 0;
priv->auth_alg = 0xFFFF;
/*send deauth packet to notify disconnection to wpa_supplicant */
woal_deauth_event(priv, req->reason_code);
}
#endif
ret = woal_cfg80211_disconnect(wiphy, dev, req->reason_code);
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
if (priv->wdev->iftype == NL80211_IFTYPE_STATION ||
priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)
cfg80211_disconnected(priv->netdev, 0, NULL, 0, GFP_KERNEL);
#endif
return ret;
}
/**
* @brief Request the driver to get the station information
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param mac MAC address of the station
* @param sinfo A pointer to station_info structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *mac,
#else
u8 *mac,
#endif
struct station_info *sinfo)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
LEAVE();
return woal_uap_cfg80211_get_station(wiphy, dev, mac, sinfo);
}
#endif
if (priv->media_connected == MFALSE) {
PRINTM(MINFO, "cfg80211: Media not connected!\n");
LEAVE();
return -ENOENT;
}
if (MLAN_STATUS_SUCCESS != woal_cfg80211_dump_station_info(priv, sinfo)) {
PRINTM(MERROR, "cfg80211: Failed to get station info\n");
ret = -EFAULT;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
woal_check_auto_tdls(wiphy, dev);
#endif
LEAVE();
return ret;
}
/**
* @brief Request the driver to dump the station information
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param idx Station index
* @param mac MAC address of the station
* @param sinfo A pointer to station_info structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_dump_station(struct wiphy *wiphy,
struct net_device *dev, int idx,
t_u8 *mac, struct station_info *sinfo)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
LEAVE();
return woal_uap_cfg80211_dump_station(wiphy, dev, idx, mac,
sinfo);
}
#endif
if (!priv->media_connected || idx != 0) {
PRINTM(MINFO,
"cfg80211: Media not connected or not for this station!\n");
LEAVE();
return -ENOENT;
}
moal_memcpy_ext(priv->phandle, mac, priv->cfg_bssid, ETH_ALEN,
ETH_ALEN);
if (MLAN_STATUS_SUCCESS != woal_cfg80211_dump_station_info(priv, sinfo)) {
PRINTM(MERROR, "cfg80211: Failed to get station info\n");
ret = -EFAULT;
}
LEAVE();
return ret;
}
/**
* @brief Convert driver band configuration to IEEE band type
*
* @param band Driver band configuration
*
* @return IEEE band type
*/
static t_u8
woal_bandcfg_to_ieee_band(Band_Config_t bandcfg)
{
t_u8 ret_radio_type = 0;
ENTER();
switch (bandcfg.chanBand) {
case BAND_5GHZ:
ret_radio_type = IEEE80211_BAND_5GHZ;
break;
case BAND_2GHZ:
default:
ret_radio_type = IEEE80211_BAND_2GHZ;
break;
}
LEAVE();
return ret_radio_type;
}
/**
* @brief Request the driver to dump survey info
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param idx Station index
* @param survey A pointer to survey_info structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_dump_survey(struct wiphy *wiphy,
struct net_device *dev, int idx,
struct survey_info *survey)
{
int ret = -ENOENT;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
enum ieee80211_band band;
ChanStatistics_t *pchan_stats = NULL;
mlan_scan_resp scan_resp;
ENTER();
PRINTM(MIOCTL, "dump_survey idx=%d\n", idx);
memset(&scan_resp, 0, sizeof(scan_resp));
if (MLAN_STATUS_SUCCESS !=
woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) {
ret = -EFAULT;
goto done;
}
pchan_stats = (ChanStatistics_t *)scan_resp.pchan_stats;
if (idx > (int)scan_resp.num_in_chan_stats || idx < 0) {
ret = -EFAULT;
goto done;
}
if (idx == (int)scan_resp.num_in_chan_stats ||
!pchan_stats[idx].cca_scan_duration)
goto done;
ret = 0;
memset(survey, 0, sizeof(*survey));
band = woal_bandcfg_to_ieee_band(pchan_stats[idx].bandcfg);
survey->channel =
ieee80211_get_channel(wiphy,
ieee80211_channel_to_frequency(pchan_stats
[idx].
chan_num
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
, band
#endif
));
survey->filled = SURVEY_INFO_NOISE_DBM;
survey->noise = pchan_stats[idx].noise;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
survey->filled |= SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY;
survey->time = pchan_stats[idx].cca_scan_duration;
survey->time_busy = pchan_stats[idx].cca_busy_duration;
#else
survey->filled |=
SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY;
survey->channel_time = pchan_stats[idx].cca_scan_duration;
survey->channel_time_busy = pchan_stats[idx].cca_busy_duration;
#endif
#endif
done:
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
static int
woal_cfg80211_get_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev);
chan_band_info channel;
memset(&channel, 0x00, sizeof(channel));
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
if (priv->bss_started == MTRUE) {
if (MLAN_STATUS_SUCCESS !=
woal_set_get_ap_channel(priv, MLAN_ACT_GET,
MOAL_IOCTL_WAIT,
&channel)) {
PRINTM(MERROR, "Fail to get ap channel \n");
return -EFAULT;
}
} else {
PRINTM(MIOCTL, "get_channel when AP is not started\n");
return -EFAULT;
}
} else
#endif
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
if (priv->media_connected == MTRUE) {
if (MLAN_STATUS_SUCCESS !=
woal_get_sta_channel(priv, MOAL_IOCTL_WAIT,
&channel)) {
PRINTM(MERROR, "Fail to get sta channel \n");
return -EFAULT;
}
} else {
PRINTM(MIOCTL,
"get_channel when STA is not connected\n");
return -EFAULT;
}
} else {
PRINTM(MERROR, "BssRole not support %d.\n", GET_BSS_ROLE(priv));
return -EFAULT;
}
if (MLAN_STATUS_FAILURE == woal_chandef_create(priv, chandef, &channel))
return -EFAULT;
else
return 0;
}
#endif
/**
* @brief Request the driver to change the IEEE power save
* mdoe
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param enabled Enable or disable
* @param timeout Timeout value
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_set_power_mgmt(struct wiphy *wiphy,
struct net_device *dev, bool enabled, int timeout)
{
int ret = 0, disabled;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
if (moal_extflg_isset(priv->phandle, EXT_HW_TEST) ||
(priv->phandle->params.ps_mode == MLAN_INIT_PARA_DISABLED)) {
PRINTM(MIOCTL, "block set power hw_test=%d ps_mode=%d\n",
moal_extflg_isset(priv->phandle, EXT_HW_TEST),
priv->phandle->params.ps_mode);
LEAVE();
return -EFAULT;
}
if (priv->phandle->driver_status) {
PRINTM(MERROR,
"Block woal_cfg80211_set_power_mgmt in abnormal driver state\n");
LEAVE();
return -EFAULT;
}
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) {
PRINTM(MIOCTL, "skip set power for p2p interface\n");
LEAVE();
return ret;
}
#endif
#endif
if (enabled)
disabled = 0;
else
disabled = 1;
if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, MLAN_ACT_SET,
&disabled, timeout,
MOAL_IOCTL_WAIT)) {
ret = -EOPNOTSUPP;
}
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
/**
* @brief Request the driver to get the transmit power info
*
* @param wiphy A pointer to wiphy structure
* @param type TX power adjustment type
* @param dbm TX power in dbm
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_get_tx_power(struct wiphy *wiphy,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
struct wireless_dev *wdev,
#endif
int *dbm)
{
int ret = 0;
moal_private *priv = NULL;
mlan_power_cfg_t power_cfg;
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
ENTER();
if (!handle) {
PRINTM(MFATAL, "Unable to get handle\n");
LEAVE();
return -EFAULT;
}
priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
if (!priv) {
PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__);
LEAVE();
return -EFAULT;
}
if (MLAN_STATUS_SUCCESS !=
woal_set_get_tx_power(priv, MLAN_ACT_GET, &power_cfg)) {
LEAVE();
return -EFAULT;
}
*dbm = power_cfg.power_level;
LEAVE();
return ret;
}
/**
* @brief Request the driver to change the transmit power
*
* @param wiphy A pointer to wiphy structure
* @param type TX power adjustment type
* @param dbm TX power in dbm
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_set_tx_power(struct wiphy *wiphy,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
struct wireless_dev *wdev,
#endif
#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
enum tx_power_setting type,
#else
enum nl80211_tx_power_setting type,
#endif
int dbm)
{
int ret = 0;
moal_private *priv = NULL;
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
mlan_power_cfg_t power_cfg;
ENTER();
priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
if (!priv) {
PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__);
LEAVE();
return -EFAULT;
}
if (type) {
power_cfg.is_power_auto = 0;
power_cfg.power_level = dbm;
} else
power_cfg.is_power_auto = 1;
if (MLAN_STATUS_SUCCESS !=
woal_set_get_tx_power(priv, MLAN_ACT_SET, &power_cfg))
ret = -EFAULT;
LEAVE();
return ret;
}
#endif
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
/**
* CFG802.11 operation handler for connection quality monitoring.
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param rssi_thold rssi threshold
* @param rssi_hyst rssi hysteresis
*/
static int
woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy,
struct net_device *dev,
s32 rssi_thold, u32 rssi_hyst)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
priv->cqm_rssi_thold = rssi_thold;
priv->cqm_rssi_high_thold = rssi_thold;
priv->cqm_rssi_hyst = rssi_hyst;
PRINTM(MIOCTL, "rssi_thold=%d rssi_hyst=%d\n", (int)rssi_thold,
(int)rssi_hyst);
woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT);
LEAVE();
return 0;
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
/**
* @brief remain on channel config
*
* @param priv A pointer to moal_private structure
* @param wait_option Wait option
* @param cancel cancel remain on channel flag
* @param status A pointer to status, success, in process or reject
* @param chan A pointer to ieee80211_channel structure
* @param channel_type channel_type,
* @param duration Duration wait to receive frame
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_remain_on_channel_cfg(moal_private *priv, t_u8 wait_option,
t_u8 remove, t_u8 *status,
struct ieee80211_channel *chan,
enum mlan_channel_type channel_type,
t_u32 duration)
{
mlan_ds_remain_chan chan_cfg;
int ret = 0;
ENTER();
if (!status || (!chan && !remove)) {
PRINTM(MERROR,
"Invalid parameter status=%p, chan=%p, remove=%d\n",
status, chan, remove);
LEAVE();
return -EFAULT;
}
memset(&chan_cfg, 0, sizeof(mlan_ds_remain_chan));
if (remove) {
chan_cfg.remove = MTRUE;
} else {
#ifdef WIFI_DIRECT_SUPPORT
if (priv->phandle->is_go_timer_set) {
PRINTM(MINFO,
"block remain on channel while go timer is on\n");
LEAVE();
return -EBUSY;
}
#endif
if (chan->band == IEEE80211_BAND_2GHZ)
chan_cfg.bandcfg.chanBand = BAND_2GHZ;
else if (chan->band == IEEE80211_BAND_5GHZ)
chan_cfg.bandcfg.chanBand = BAND_5GHZ;
switch (channel_type) {
case CHAN_HT40MINUS:
chan_cfg.bandcfg.chan2Offset = SEC_CHAN_BELOW;
chan_cfg.bandcfg.chanWidth = CHAN_BW_40MHZ;
break;
case CHAN_HT40PLUS:
chan_cfg.bandcfg.chan2Offset = SEC_CHAN_ABOVE;
chan_cfg.bandcfg.chanWidth = CHAN_BW_40MHZ;
break;
case CHAN_VHT80:
chan_cfg.bandcfg.chanWidth = CHAN_BW_80MHZ;
break;
case CHAN_NO_HT:
case CHAN_HT20:
default:
break;
}
chan_cfg.channel =
ieee80211_frequency_to_channel(chan->center_freq);
chan_cfg.remain_period = duration;
PRINTM(MCMND,
"Remain on Channel: chan=%d, offset=%d width=%d\n",
chan_cfg.channel, chan_cfg.bandcfg.chan2Offset,
chan_cfg.bandcfg.chanWidth);
}
if (MLAN_STATUS_SUCCESS ==
woal_set_remain_channel_ioctl(priv, wait_option, &chan_cfg))
*status = chan_cfg.status;
else
ret = -EFAULT;
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
/**
* @brief tx mgmt frame
*
* @param wiphy A pointer to wiphy structure
* @param wdev A pointer to wireless_dev structure
* @param cookie A pointer to frame cookie
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct wireless_dev *wdev, u64 cookie)
#else
/**
* @brief tx mgmt frame
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param cookie A pointer to frame cookie
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
struct net_device *dev, u64 cookie)
#endif
{
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct net_device *dev = wdev->netdev;
#endif
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
int ret = 0;
t_u8 status = 1;
moal_private *remain_priv = NULL;
ENTER();
if (priv->phandle->remain_on_channel) {
remain_priv =
priv->phandle->priv[priv->phandle->remain_bss_index];
if (!remain_priv) {
PRINTM(MERROR,
"mgmt_tx_cancel_wait: Wrong remain_bss_index=%d\n",
priv->phandle->remain_bss_index);
ret = -EFAULT;
goto done;
}
if (woal_cfg80211_remain_on_channel_cfg(remain_priv,
MOAL_IOCTL_WAIT, MTRUE,
&status, NULL, 0, 0)) {
PRINTM(MERROR,
"mgmt_tx_cancel_wait: Fail to cancel remain on channel\n");
ret = -EFAULT;
goto done;
}
if (priv->phandle->cookie) {
cfg80211_remain_on_channel_expired(
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
remain_priv->
netdev,
#else
remain_priv->
wdev,
#endif
priv->
phandle->
cookie,
&priv->
phandle->chan,
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
priv->
phandle->
channel_type,
#endif
GFP_ATOMIC);
priv->phandle->cookie = 0;
}
priv->phandle->remain_on_channel = MFALSE;
}
done:
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
/**
* @brief Make chip remain on channel
*
* @param wiphy A pointer to wiphy structure
* @param wdev A pointer to wireless_dev structure
* @param chan A pointer to ieee80211_channel structure
* @param channel_type Channel type
* @param duration Duration for timer
* @param cookie A pointer to timer cookie
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan,
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
enum nl80211_channel_type channel_type,
#endif
unsigned int duration, u64 * cookie)
#else
/**
* @brief Make chip remain on channel
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param chan A pointer to ieee80211_channel structure
* @param channel_type Channel type
* @param duration Duration for timer
* @param cookie A pointer to timer cookie
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type,
unsigned int duration, u64 * cookie)
#endif
{
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct net_device *dev = wdev->netdev;
#endif
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
int ret = 0;
t_u8 status = 1;
moal_private *remain_priv = NULL;
ENTER();
if (!chan || !cookie) {
PRINTM(MERROR, "Invalid parameter for remain on channel\n");
ret = -EFAULT;
goto done;
}
/** cancel previous remain on channel */
if (priv->phandle->remain_on_channel &&
((priv->phandle->chan.center_freq != chan->center_freq))) {
remain_priv =
priv->phandle->priv[priv->phandle->remain_bss_index];
if (!remain_priv) {
PRINTM(MERROR,
"remain_on_channel: Wrong remain_bss_index=%d\n",
priv->phandle->remain_bss_index);
ret = -EFAULT;
goto done;
}
if (woal_cfg80211_remain_on_channel_cfg(remain_priv,
MOAL_IOCTL_WAIT, MTRUE,
&status, NULL, 0, 0)) {
PRINTM(MERROR,
"remain_on_channel: Fail to cancel remain on channel\n");
ret = -EFAULT;
goto done;
}
priv->phandle->cookie = 0;
priv->phandle->remain_on_channel = MFALSE;
}
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE,
&status, chan,
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
channel_type,
#else
0,
#endif
(t_u32)duration)) {
ret = -EFAULT;
goto done;
}
if (status) {
PRINTM(MMSG,
"%s: Set remain on Channel: channel=%d with status=%d\n",
dev->name,
ieee80211_frequency_to_channel(chan->center_freq),
status);
if (!priv->phandle->remain_on_channel) {
priv->phandle->is_remain_timer_set = MTRUE;
woal_mod_timer(&priv->phandle->remain_timer, duration);
}
}
/* remain on channel operation success */
/* we need update the value cookie */
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
*cookie = (u64) random32() | 1;
#else
*cookie = (u64) prandom_u32() | 1;
#endif
priv->phandle->remain_on_channel = MTRUE;
priv->phandle->remain_bss_index = priv->bss_index;
priv->phandle->cookie = *cookie;
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
priv->phandle->channel_type = channel_type;
#endif
moal_memcpy_ext(priv->phandle, &priv->phandle->chan, chan,
sizeof(struct ieee80211_channel),
sizeof(priv->phandle->chan));
if (status == 0)
PRINTM(MIOCTL,
"%s: Set remain on Channel: channel=%d cookie = %#llx\n",
dev->name,
ieee80211_frequency_to_channel(chan->center_freq),
priv->phandle->cookie);
cfg80211_ready_on_channel(
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
dev,
#else
priv->wdev,
#endif
*cookie, chan,
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
channel_type,
#endif
duration, GFP_KERNEL);
done:
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
/**
* @brief Cancel remain on channel
*
* @param wiphy A pointer to wiphy structure
* @param wdev A pointer to wireless_dev structure
* @param cookie A pointer to timer cookie
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev, u64 cookie)
#else
/**
* @brief Cancel remain on channel
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param cookie A pointer to timer cookie
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
struct net_device *dev, u64 cookie)
#endif
{
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct net_device *dev = wdev->netdev;
#endif
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
moal_private *remain_priv = NULL;
int ret = 0;
t_u8 status = 1;
ENTER();
PRINTM(MIOCTL, "Cancel remain on Channel: cookie = %#llx\n", cookie);
remain_priv = priv->phandle->priv[priv->phandle->remain_bss_index];
if (!remain_priv) {
PRINTM(MERROR,
"cancel_remain_on_channel: Wrong remain_bss_index=%d\n",
priv->phandle->remain_bss_index);
ret = -EFAULT;
goto done;
}
if (woal_cfg80211_remain_on_channel_cfg(remain_priv, MOAL_IOCTL_WAIT,
MTRUE, &status, NULL, 0, 0)) {
PRINTM(MERROR,
"cancel_remain_on_channel: Fail to cancel remain on channel\n");
ret = -EFAULT;
goto done;
}
priv->phandle->remain_on_channel = MFALSE;
if (priv->phandle->cookie)
priv->phandle->cookie = 0;
done:
LEAVE();
return ret;
}
#endif /* KERNEL_VERSION */
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
/**
* @brief start sched scan
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param request A pointer to struct cfg80211_sched_scan_request
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_sched_scan_request *request)
{
struct ieee80211_channel *chan = NULL;
int i = 0;
int ret = 0;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
struct cfg80211_ssid *ssid = NULL;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
t_u8 buf[ETH_ALEN];
#endif
ENTER();
#ifdef UAP_CFG80211
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
LEAVE();
return -EFAULT;
}
#endif
memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg));
if (!request) {
PRINTM(MERROR, "Invalid sched_scan req parameter\n");
LEAVE();
return -EINVAL;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
PRINTM(MIOCTL,
"%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n",
priv->netdev->name, request->n_ssids, request->n_match_sets,
request->n_channels, request->scan_plans[0].interval,
(int)request->ie_len);
#else
PRINTM(MIOCTL,
"%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n",
priv->netdev->name, request->n_ssids, request->n_match_sets,
request->n_channels, request->interval, (int)request->ie_len);
#endif
/** We have pending scan, start bgscan later */
if (priv->phandle->scan_pending_on_block)
priv->scan_cfg.start_later = MTRUE;
for (i = 0; i < request->n_match_sets; i++) {
ssid = &request->match_sets[i].ssid;
strncpy(priv->scan_cfg.ssid_list[i].ssid, ssid->ssid,
ssid->ssid_len);
priv->scan_cfg.ssid_list[i].max_len = 0;
PRINTM(MIOCTL, "sched scan: ssid=%s\n", ssid->ssid);
}
/** Add broadcast scan, when n_match_sets = 0 */
if (!request->n_match_sets)
priv->scan_cfg.ssid_list[0].max_len = 0xff;
for (i = 0; i < (int)MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels);
i++) {
chan = request->channels[i];
priv->scan_cfg.chan_list[i].chan_number = chan->hw_value;
priv->scan_cfg.chan_list[i].radio_type = chan->band;
if (chan->flags &
(IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_RADAR))
priv->scan_cfg.chan_list[i].scan_type =
MLAN_SCAN_TYPE_PASSIVE;
else
priv->scan_cfg.chan_list[i].scan_type =
MLAN_SCAN_TYPE_ACTIVE;
priv->scan_cfg.chan_list[i].scan_time = 0;
#ifdef WIFI_DIRECT_SUPPORT
if (priv->phandle->miracast_mode)
priv->scan_cfg.chan_list[i].scan_time =
priv->phandle->miracast_scan_time;
#endif
}
priv->scan_cfg.chan_per_scan =
MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels);
/** set scan request IES */
if (request->ie && request->ie_len) {
if (MLAN_STATUS_SUCCESS !=
woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0,
(t_u8 *)request->ie,
request->ie_len,
MGMT_MASK_PROBE_REQ,
MOAL_IOCTL_WAIT)) {
PRINTM(MERROR, "Fail to set sched scan IE\n");
ret = -EFAULT;
goto done;
}
} else {
/** Clear SCAN IE in Firmware */
if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK)
woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0,
NULL, 0, NULL, 0,
MGMT_MASK_PROBE_REQ,
MOAL_IOCTL_WAIT);
}
/* Interval between scan cycles in milliseconds,supplicant set to 10
* second */
/* We want to use 30 second for per scan cycle */
priv->scan_cfg.scan_interval = MIN_BGSCAN_INTERVAL;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
if (request->scan_plans[0].interval * 1000 > MIN_BGSCAN_INTERVAL)
priv->scan_cfg.scan_interval =
request->scan_plans[0].interval * 1000;
if (request->n_scan_plans >= 2) {
priv->scan_cfg.config_ees = MTRUE;
priv->scan_cfg.ees_mode =
MBIT(EES_MODE_HIGH) | MBIT(EES_MODE_MID);
priv->scan_cfg.high_period =
request->scan_plans[0].interval * 1000;
priv->scan_cfg.high_period_count =
request->scan_plans[0].iterations;
priv->scan_cfg.mid_period = request->scan_plans[1].interval;
if (request->scan_plans[1].iterations == 0)
priv->scan_cfg.mid_period_count = DEF_REPEAT_COUNT;
else
priv->scan_cfg.mid_period_count =
request->scan_plans[1].iterations;
if (request->n_scan_plans == 3) {
priv->scan_cfg.ees_mode |= MBIT(EES_MODE_LOW);
priv->scan_cfg.low_period =
request->scan_plans[2].interval;
priv->scan_cfg.low_period_count = DEF_REPEAT_COUNT;
}
}
#else
if (request->interval > MIN_BGSCAN_INTERVAL)
priv->scan_cfg.scan_interval = request->interval;
#endif
priv->scan_cfg.repeat_count = DEF_REPEAT_COUNT;
priv->scan_cfg.report_condition =
BG_SCAN_SSID_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE;
priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA;
priv->scan_cfg.action = BG_SCAN_ACT_SET;
priv->scan_cfg.enable = MTRUE;
#ifdef WIFI_DIRECT_SUPPORT
if (priv->phandle->miracast_mode)
priv->scan_cfg.scan_chan_gap = priv->phandle->scan_chan_gap;
else
priv->scan_cfg.scan_chan_gap = 0;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
PRINTM(MIOCTL, "NL80211_SCAN_FLAG_RANDOM_ADDR is set\n");
get_random_bytes(buf, ETH_ALEN);
for (i = 0; i < ETH_ALEN; i++) {
buf[i] &= ~request->mac_addr_mask[i];
buf[i] |= request->mac_addr[i] &
request->mac_addr_mask[i];
}
moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac, buf,
ETH_ALEN, sizeof(priv->scan_cfg.random_mac));
} else
#endif
moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac,
priv->random_mac, ETH_ALEN,
sizeof(priv->scan_cfg.random_mac));
PRINTM(MCMND, "wlan:random_mac " MACSTR "\n",
MAC2STR(priv->scan_cfg.random_mac));
if (MLAN_STATUS_SUCCESS ==
woal_request_bgscan(priv, MOAL_IOCTL_WAIT, &priv->scan_cfg)) {
PRINTM(MMSG, "wlan: sched scan start\n");
priv->sched_scanning = MTRUE;
priv->bg_scan_start = MTRUE;
priv->bg_scan_reported = MFALSE;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
priv->bg_scan_reqid = request->reqid;
#endif
} else
ret = -EFAULT;
done:
LEAVE();
return ret;
}
/**
* @brief stop sched scan
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
, u64 reqid
#endif
)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
PRINTM(MMSG, "wlan: sched scan stop\n");
priv->sched_scanning = MFALSE;
woal_stop_bg_scan(priv, MOAL_NO_WAIT);
priv->bg_scan_start = MFALSE;
priv->bg_scan_reported = MFALSE;
LEAVE();
return 0;
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
/**
* @brief cfg80211_resume handler
*
* @param wiphy A pointer to wiphy structure
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_resume(struct wiphy *wiphy)
{
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM)
struct cfg80211_wowlan_wakeup wakeup_report;
#endif
mlan_ds_hs_wakeup_reason wakeup_reason;
int i;
PRINTM(MCMND, "<--- Enter woal_cfg80211_resume --->\n");
if (!priv) {
PRINTM(MERROR, "woal_cfg80211_resume: priv is NULL\n");
goto done;
}
for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
if (handle->priv[i] &&
(GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) {
if (handle->priv[i]->last_event & EVENT_BG_SCAN_REPORT) {
if (handle->priv[i]->sched_scanning) {
woal_inform_bss_from_scan_result
(handle->priv[i], NULL,
MOAL_IOCTL_WAIT);
woal_report_sched_scan_result(handle->
priv[i]);
handle->priv[i]->last_event = 0;
PRINTM(MCMND,
"Report sched scan result in cfg80211 resume\n");
}
if (!moal_extflg_isset(handle, EXT_HW_TEST) &&
handle->priv[i]->roaming_enabled) {
handle->priv[i]->roaming_required =
MTRUE;
#ifdef ANDROID_KERNEL
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
__pm_wakeup_event(&handle->ws,
ROAMING_WAKE_LOCK_TIMEOUT);
#else
wake_lock_timeout(&handle->wake_lock,
msecs_to_jiffies
(ROAMING_WAKE_LOCK_TIMEOUT));
#endif
#endif
wake_up_interruptible(&handle->
reassoc_thread.
wait_q);
}
}
}
}
woal_get_wakeup_reason(priv, &wakeup_reason);
#ifdef STA_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext))
woal_wake_reason_logger(priv, wakeup_reason);
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM)
memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup));
wakeup_report.pattern_idx = -1;
switch (wakeup_reason.hs_wakeup_reason) {
case NO_HSWAKEUP_REASON:
break;
case BCAST_DATA_MATCHED:
break;
case MCAST_DATA_MATCHED:
break;
case UCAST_DATA_MATCHED:
break;
case MASKTABLE_EVENT_MATCHED:
break;
case NON_MASKABLE_EVENT_MATCHED:
break;
case NON_MASKABLE_CONDITION_MATCHED:
if (wiphy->wowlan_config && wiphy->wowlan_config->disconnect)
wakeup_report.disconnect = true;
break;
case MAGIC_PATTERN_MATCHED:
if (wiphy->wowlan_config && wiphy->wowlan_config->magic_pkt)
wakeup_report.magic_pkt = true;
if (wiphy->wowlan_config && wiphy->wowlan_config->n_patterns)
wakeup_report.pattern_idx = 1;
break;
case CONTROL_FRAME_MATCHED:
break;
case MANAGEMENT_FRAME_MATCHED:
break;
case GTK_REKEY_FAILURE:
if (wiphy->wowlan_config &&
wiphy->wowlan_config->gtk_rekey_failure)
wakeup_report.gtk_rekey_failure = true;
break;
default:
break;
}
if ((wakeup_reason.hs_wakeup_reason > 0) &&
(wakeup_reason.hs_wakeup_reason <= 10)) {
cfg80211_report_wowlan_wakeup(priv->wdev, &wakeup_report,
GFP_KERNEL);
}
#endif
done:
handle->cfg80211_suspend = MFALSE;
PRINTM(MCMND, "<--- Leave woal_cfg80211_resume --->\n");
return 0;
}
/**
* @brief is_wowlan_pattern_supported
*
* @param priv A pointer to moal_private
* @param pat A pointer to wowlan pattern
* @param byte_seq A pointer to byte_seq
*
* @return 1 -- support, 0 -- not support
*/
static t_bool
is_wowlan_pattern_supported(moal_private *priv,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
struct cfg80211_pkt_pattern *pat,
#else
struct cfg80211_wowlan_trig_pkt_pattern
*pat,
#endif
s8 * byte_seq)
{
int j, k, valid_byte_cnt = 0;
t_bool dont_care_byte = MFALSE;
for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) {
for (k = 0; k < 8; k++) {
if (pat->mask[j] & 1 << k) {
moal_memcpy_ext(priv->phandle,
byte_seq + valid_byte_cnt,
&pat->pattern[j * 8 + k], 1, 1);
valid_byte_cnt++;
if (dont_care_byte)
return MFALSE;
} else {
if (valid_byte_cnt)
dont_care_byte = MTRUE;
}
if (valid_byte_cnt > MAX_NUM_BYTE_SEQ)
return MFALSE;
}
}
byte_seq[MAX_NUM_BYTE_SEQ] = valid_byte_cnt;
return MTRUE;
}
/**
* @brief cfg80211_suspend handler
*
* @param wiphy A pointer to wiphy structure
* @param wow A pointer to cfg80211_wowlan
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow)
{
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
int i;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
mlan_ds_misc_mef_flt_cfg mef_cfg;
mef_entry_t *mef_entry = NULL;
int filt_num = 0;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
t_bool first_pat = MTRUE;
#endif
t_u8 byte_seq[MAX_NUM_BYTE_SEQ + 1];
const t_u8 ipv4_mc_mac[] = { 0x33, 0x33 };
const t_u8 ipv6_mc_mac[] = { 0x01, 0x00, 0x5e };
moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_STA);
mlan_ds_hs_cfg hscfg;
PRINTM(MCMND, "<--- Enter woal_cfg80211_suspend --->\n");
for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) {
if (handle->priv[i] &&
(GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) {
if (handle->scan_request) {
PRINTM(MIOCTL,
"Cancel pending scan in woal_cfg80211_suspend\n");
woal_cancel_scan(handle->priv[i],
MOAL_IOCTL_WAIT);
}
handle->priv[i]->last_event = 0;
}
}
handle->cfg80211_suspend = MTRUE;
if (!wow) {
PRINTM(MERROR, "None of the WOWLAN triggers enabled\n");
ret = 0;
goto done;
}
if (!priv || !priv->media_connected) {
PRINTM(MERROR,
"Can not configure WOWLAN in disconnected state\n");
ret = 0;
goto done;
}
PRINTM(MCMND, "wow->n_patterns=%d\n", wow->n_patterns);
PRINTM(MCMND, "wow->any=%d\n", wow->any);
PRINTM(MCMND, "wow->disconnect=%d\n", wow->disconnect);
PRINTM(MCMND, "wow->magic_pkt=%d\n", wow->magic_pkt);
PRINTM(MCMND, "wow->gtk_rekey_failure=%d\n", wow->gtk_rekey_failure);
PRINTM(MCMND, "wow->eap_identity_req=%d\n", wow->eap_identity_req);
PRINTM(MCMND, "wow->four_way_handshake=%d\n", wow->four_way_handshake);
PRINTM(MCMND, "wow->rfkill_release=%d\n", wow->rfkill_release);
if (!(wow->n_patterns) && !(wow->magic_pkt)) {
PRINTM(MCMND, "No pattern or magic packet configured\n");
ret = 0;
goto done;
}
memset(&mef_cfg, 0, sizeof(mef_cfg));
mef_cfg.mef_act_type = MEF_ACT_WOWLAN;
mef_entry = &mef_cfg.mef_entry;
mef_entry->mode = MEF_MODE_HOST_SLEEP;
mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
for (i = 0; i < wow->n_patterns; i++) {
memset(byte_seq, 0, sizeof(byte_seq));
if (!is_wowlan_pattern_supported(priv, &wow->patterns[i],
byte_seq)) {
PRINTM(MERROR, "Pattern not supported\n");
ret = -EOPNOTSUPP;
goto done;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
if (!wow->patterns[i].pkt_offset) {
#endif
if (!(byte_seq[0] & 0x01) &&
(byte_seq[MAX_NUM_BYTE_SEQ] == 1)) {
mef_cfg.criteria |= CRITERIA_UNICAST;
continue;
} else if (is_broadcast_ether_addr(byte_seq)) {
mef_cfg.criteria |= CRITERIA_BROADCAST;
continue;
} else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
(byte_seq[MAX_NUM_BYTE_SEQ] == 2)) ||
(!memcmp(byte_seq, ipv6_mc_mac, 3) &&
(byte_seq[MAX_NUM_BYTE_SEQ] == 3))) {
mef_cfg.criteria |= CRITERIA_MULTICAST;
continue;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
}
mef_entry->filter_item[filt_num].fill_flag =
(FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ |
FILLING_OFFSET);
mef_entry->filter_item[filt_num].repeat = 1;
mef_entry->filter_item[filt_num].offset =
wow->patterns[i].pkt_offset;
moal_memcpy_ext(priv->phandle,
mef_entry->filter_item[filt_num].byte_seq,
byte_seq, MAX_NUM_BYTE_SEQ,
sizeof(mef_entry->filter_item[filt_num].
byte_seq));
mef_entry->filter_item[filt_num].num_byte_seq =
byte_seq[MAX_NUM_BYTE_SEQ];
mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ;
if (first_pat)
first_pat = MFALSE;
else
mef_entry->rpn[filt_num] = RPN_TYPE_OR;
filt_num++;
#endif
}
if (wow->magic_pkt) {
mef_cfg.criteria |= CRITERIA_UNICAST | CRITERIA_BROADCAST |
CRITERIA_MULTICAST;
mef_entry->filter_item[filt_num].fill_flag =
(FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ |
FILLING_OFFSET);
mef_entry->filter_item[filt_num].repeat = 16;
moal_memcpy_ext(priv->phandle,
mef_entry->filter_item[filt_num].byte_seq,
priv->current_addr, ETH_ALEN,
sizeof(mef_entry->filter_item[filt_num].
byte_seq));
mef_entry->filter_item[filt_num].num_byte_seq = ETH_ALEN;
mef_entry->filter_item[filt_num].offset = 56;
mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ;
if (filt_num)
mef_entry->rpn[filt_num] = RPN_TYPE_OR;
filt_num++;
mef_entry->filter_item[filt_num].fill_flag =
(FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ |
FILLING_OFFSET);
mef_entry->filter_item[filt_num].repeat = 16;
moal_memcpy_ext(priv->phandle,
mef_entry->filter_item[filt_num].byte_seq,
priv->current_addr, ETH_ALEN,
sizeof(mef_entry->filter_item[filt_num].
byte_seq));
mef_entry->filter_item[filt_num].num_byte_seq = ETH_ALEN;
mef_entry->filter_item[filt_num].offset = 28;
mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ;
if (filt_num)
mef_entry->rpn[filt_num] = RPN_TYPE_OR;
filt_num++;
}
mef_entry->filter_num = filt_num;
if (!mef_cfg.criteria)
mef_cfg.criteria = CRITERIA_BROADCAST | CRITERIA_UNICAST |
CRITERIA_MULTICAST;
status = woal_set_get_wowlan_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT,
&mef_cfg);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "woal_set_get_wowlan_config fail!\n");
ret = -EFAULT;
goto done;
}
memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg));
status = woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT,
&hscfg);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR,
"Fail to get HS parameter in woal_cfg80211_suspend: 0x%x 0x%x 0x%x\n",
hscfg.conditions, hscfg.gap, hscfg.gpio);
ret = -EFAULT;
goto done;
}
hscfg.is_invoke_hostcmd = MFALSE;
if (wow->n_patterns || wow->magic_pkt)
hscfg.conditions = 0;
status = woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT,
&hscfg);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR,
"Fail to set HS parameter in woal_cfg80211_suspend: 0x%x 0x%x 0x%x\n",
hscfg.conditions, hscfg.gap, hscfg.gpio);
ret = -EFAULT;
goto done;
}
done:
PRINTM(MCMND, "<--- Leave woal_cfg80211_suspend --->\n");
return ret;
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
static void
woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled)
{
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
device_set_wakeup_enable(handle->hotplug_device, enabled);
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
/**
* @brief TDLS operation ioctl handler
*
* @param priv A pointer to moal_private structure
* @param peer A pointer to peer mac
* @apram action action for TDLS
* @return 0 --success, otherwise fail
*/
static int
woal_tdls_oper(moal_private *priv, u8 *peer, t_u8 action)
{
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_TDLS_OPER;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
ioctl_req->action = MLAN_ACT_SET;
misc->param.tdls_oper.tdls_action = action;
moal_memcpy_ext(priv->phandle, misc->param.tdls_oper.peer_mac, peer,
ETH_ALEN, sizeof(misc->param.tdls_oper.peer_mac));
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
/**
* @brief TDLS operation ioctl handler
*
* @param priv A pointer to moal_private structure
* @param peer A pointer to peer mac
* @param tdls_ies A pointer to mlan_ds_misc_tdls_ies structure
* @param flags TDLS ie flags
*
* @return 0 --success, otherwise fail
*/
static int
woal_tdls_get_ies(moal_private *priv, u8 *peer,
mlan_ds_misc_tdls_ies *tdls_ies, t_u16 flags)
{
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_GET_TDLS_IES;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
ioctl_req->action = MLAN_ACT_GET;
misc->param.tdls_ies.flags = flags;
moal_memcpy_ext(priv->phandle, misc->param.tdls_ies.peer_mac, peer,
ETH_ALEN, sizeof(misc->param.tdls_ies.peer_mac));
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
ret = -EFAULT;
goto done;
}
if (tdls_ies)
moal_memcpy_ext(priv->phandle, tdls_ies, &misc->param.tdls_ies,
sizeof(mlan_ds_misc_tdls_ies),
sizeof(mlan_ds_misc_tdls_ies));
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
/**
* @brief append tdls ext_capability
*
* @param skb A pointer to sk_buff structure
*
* @return N/A
*/
static void
woal_tdls_add_ext_capab(moal_private *priv, struct sk_buff *skb,
mlan_ds_misc_tdls_ies *tdls_ies)
{
u8 *pos = NULL;
if (tdls_ies->ext_cap[0] == WLAN_EID_EXT_CAPABILITY) {
pos = (void *)skb_put(skb, sizeof(IEEEtypes_ExtCap_t));
moal_memcpy_ext(priv->phandle, pos, tdls_ies->ext_cap,
sizeof(IEEEtypes_ExtCap_t),
sizeof(IEEEtypes_ExtCap_t));
} else {
PRINTM(MERROR, "Fail to append tdls ext_capability\n");
}
}
/**
* @brief append supported rates
*
* @param priv A pointer to moal_private structure
* @param skb A pointer to sk_buff structure
* @param band AP's band
*
* @return N/A
*/
static void
woal_add_supported_rates_ie(moal_private *priv, struct sk_buff *skb,
enum ieee80211_band band)
{
t_u8 basic_rates[] = { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24 };
t_u8 basic_rates_5G[] = { 0x0c, 0x12, 0x18, 0x24,
0x30, 0x48, 0x60, 0x6c
};
t_u8 *pos;
t_u8 rate_num = 0;
if (band == IEEE80211_BAND_2GHZ)
rate_num = sizeof(basic_rates);
else
rate_num = sizeof(basic_rates_5G);
if (skb_tailroom(skb) < rate_num + 2)
return;
pos = skb_put(skb, rate_num + 2);
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = rate_num;
if (band == IEEE80211_BAND_2GHZ)
moal_memcpy_ext(priv->phandle, pos, basic_rates, rate_num,
rate_num);
else
moal_memcpy_ext(priv->phandle, pos, basic_rates_5G, rate_num,
rate_num);
return;
}
/**
* @brief append ext_supported rates
*
* @param priv A pointer to moal_private structure
* @param skb A pointer to sk_buff structure
* @param band AP's band
*
* @return N/A
*/
static void
woal_add_ext_supported_rates_ie(moal_private *priv,
struct sk_buff *skb, enum ieee80211_band band)
{
t_u8 ext_rates[] = { 0x0c, 0x12, 0x18, 0x60 };
t_u8 *pos;
t_u8 rate_num = sizeof(ext_rates);
if (band != IEEE80211_BAND_2GHZ)
return;
if (skb_tailroom(skb) < rate_num + 2)
return;
pos = skb_put(skb, rate_num + 2);
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = rate_num;
moal_memcpy_ext(priv->phandle, pos, ext_rates, rate_num, rate_num);
return;
}
/**
* @brief append wmm ie
*
* @param priv A pointer to moal_private structure
* @param skb A pointer to sk_buff structure
* @param wmm_type WMM_TYPE_INFO/WMM_TYPE_PARAMETER
* @param pQosInfo A pointer to qos info
*
* @return N/A
*/
static void
woal_add_wmm_ie(moal_private *priv, struct sk_buff *skb,
t_u8 wmm_type, t_u8 *pQosInfo)
{
t_u8 wmmInfoElement[] = { 0x00, 0x50, 0xf2, 0x02, 0x00, 0x01 };
t_u8 wmmParamElement[] = { 0x00, 0x50, 0xf2, 0x02, 0x01, 0x01 };
t_u8 ac_vi[] = { 0x42, 0x43, 0x5e, 0x00 };
t_u8 ac_vo[] = { 0x62, 0x32, 0x2f, 0x00 };
t_u8 ac_be[] = { 0x03, 0xa4, 0x00, 0x00 };
t_u8 ac_bk[] = { 0x27, 0xa4, 0x00, 0x00 };
t_u8 qosInfo = 0x0;
t_u8 reserved = 0;
t_u8 wmm_id = 221;
t_u8 wmmParamIe_len = 25;
t_u8 wmmInfoIe_len = 7;
t_u8 len = 0;
t_u8 *pos;
qosInfo = (pQosInfo == NULL) ? 0xf : (*pQosInfo);
/*wmm parameter */
if (wmm_type == WMM_TYPE_PARAMETER) {
if (skb_tailroom(skb) < (wmmParamIe_len + 2))
return;
pos = skb_put(skb, wmmParamIe_len + 2);
len = wmmParamIe_len;
} else {
if (skb_tailroom(skb) < (wmmInfoIe_len + 2))
return;
pos = skb_put(skb, wmmInfoIe_len + 2);
len = wmmInfoIe_len;
}
*pos++ = wmm_id;
*pos++ = len;
/*wmm parameter */
if (wmm_type == WMM_TYPE_PARAMETER) {
moal_memcpy_ext(priv->phandle, pos, wmmParamElement,
sizeof(wmmParamElement),
sizeof(wmmParamElement));
pos += sizeof(wmmParamElement);
} else {
moal_memcpy_ext(priv->phandle, pos, wmmInfoElement,
sizeof(wmmInfoElement), sizeof(wmmInfoElement));
pos += sizeof(wmmInfoElement);
}
*pos++ = qosInfo;
/*wmm parameter */
if (wmm_type == WMM_TYPE_PARAMETER) {
*pos++ = reserved;
moal_memcpy_ext(priv->phandle, pos, ac_be, sizeof(ac_be),
sizeof(ac_be));
pos += sizeof(ac_be);
moal_memcpy_ext(priv->phandle, pos, ac_bk, sizeof(ac_bk),
sizeof(ac_bk));
pos += sizeof(ac_bk);
moal_memcpy_ext(priv->phandle, pos, ac_vi, sizeof(ac_vi),
sizeof(ac_vi));
pos += sizeof(ac_vi);
moal_memcpy_ext(priv->phandle, pos, ac_vo, sizeof(ac_vo),
sizeof(ac_vo));
}
return;
}
/**
* @brief update tdls peer status
*
* @param priv A pointer to moal_private structure
* @param peer_addr A point to peer mac address
* @param link_status link status
*
* @return N/A
*/
static t_void
woal_updata_peer_status(moal_private *priv, t_u8 *peer_addr,
tdlsStatus_e link_status)
{
struct tdls_peer *peer = NULL;
unsigned long flags;
if (priv && priv->enable_auto_tdls) {
spin_lock_irqsave(&priv->tdls_lock, flags);
list_for_each_entry(peer, &priv->tdls_list, link) {
if (!memcmp(peer->peer_addr, peer_addr, ETH_ALEN)) {
if ((link_status == TDLS_NOT_SETUP) &&
(peer->link_status ==
TDLS_SETUP_INPROGRESS))
peer->num_failure++;
else if (link_status == TDLS_SETUP_COMPLETE)
peer->num_failure = 0;
peer->link_status = link_status;
break;
}
}
spin_unlock_irqrestore(&priv->tdls_lock, flags);
}
}
/**
* @brief add tdls peer
*
* @param priv A pointer to moal_private structure
* @param peer A point to peer address
*
* @return N/A
*/
static t_void
woal_add_tdls_peer(moal_private *priv, t_u8 *peer)
{
struct tdls_peer *tdls_peer = NULL;
unsigned long flags;
t_u8 find_peer = MFALSE;
if (priv && priv->enable_auto_tdls) {
spin_lock_irqsave(&priv->tdls_lock, flags);
list_for_each_entry(tdls_peer, &priv->tdls_list, link) {
if (!memcmp(tdls_peer->peer_addr, peer, ETH_ALEN)) {
tdls_peer->link_status = TDLS_SETUP_INPROGRESS;
tdls_peer->rssi_jiffies = jiffies;
find_peer = MTRUE;
break;
}
}
if (!find_peer) {
/* create new TDLS peer */
tdls_peer =
kzalloc(sizeof(struct tdls_peer), GFP_ATOMIC);
if (tdls_peer) {
moal_memcpy_ext(priv->phandle,
tdls_peer->peer_addr, peer,
ETH_ALEN,
sizeof(tdls_peer->peer_addr));
tdls_peer->link_status = TDLS_SETUP_INPROGRESS;
tdls_peer->rssi_jiffies = jiffies;
INIT_LIST_HEAD(&tdls_peer->link);
list_add_tail(&tdls_peer->link,
&priv->tdls_list);
PRINTM(MCMND,
"Add to TDLS list: peer=" MACSTR "\n",
MAC2STR(peer));
}
}
spin_unlock_irqrestore(&priv->tdls_lock, flags);
}
}
/**
* @brief check auto tdls
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
*
* @return N/A
*/
void
woal_check_auto_tdls(struct wiphy *wiphy, struct net_device *dev)
{
t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct tdls_peer *tdls_peer = NULL;
unsigned long flags;
t_u8 tdls_discovery = MFALSE;
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
ENTER();
if (priv && priv->enable_auto_tdls) {
priv->tdls_check_tx = MFALSE;
spin_lock_irqsave(&priv->tdls_lock, flags);
list_for_each_entry(tdls_peer, &priv->tdls_list, link) {
if ((jiffies - tdls_peer->rssi_jiffies) >
TDLS_IDLE_TIME) {
tdls_peer->rssi = 0;
if (tdls_peer->num_failure <
TDLS_MAX_FAILURE_COUNT)
tdls_discovery = MTRUE;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (tdls_peer->rssi &&
(tdls_peer->rssi >= TDLS_RSSI_LOW_THRESHOLD)) {
if (tdls_peer->link_status ==
TDLS_SETUP_COMPLETE) {
tdls_peer->link_status = TDLS_TEAR_DOWN;
PRINTM(MMSG,
"Wlan: Tear down TDLS link, peer="
MACSTR " rssi=%d\n",
MAC2STR(tdls_peer->peer_addr),
-tdls_peer->rssi);
cfg80211_tdls_oper_request(dev,
tdls_peer->
peer_addr,
NL80211_TDLS_TEARDOWN,
TDLS_TEARN_DOWN_REASON_UNSPECIFIC,
GFP_ATOMIC);
}
} else if (tdls_peer->rssi &&
(tdls_peer->rssi <=
TDLS_RSSI_HIGH_THRESHOLD)) {
if ((tdls_peer->link_status ==
TDLS_NOT_SETUP) &&
(tdls_peer->num_failure <
TDLS_MAX_FAILURE_COUNT)) {
priv->tdls_check_tx = MTRUE;
PRINTM(MCMND,
"Wlan: Find TDLS peer=" MACSTR
" rssi=%d\n",
MAC2STR(tdls_peer->peer_addr),
-tdls_peer->rssi);
}
}
#endif
}
spin_unlock_irqrestore(&priv->tdls_lock, flags);
}
if (tdls_discovery)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr,
TDLS_DISCOVERY_REQUEST, 1, 0, 0, 0,
NULL, 0);
#else
woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr,
TDLS_DISCOVERY_REQUEST, 1, 0, 0, NULL,
0);
#endif
#else
woal_cfg80211_tdls_mgmt(wiphy, dev, bcast_addr,
TDLS_DISCOVERY_REQUEST, 1, 0, NULL, 0);
#endif
LEAVE();
}
/**
* @brief woal construct tdls data frame
*
* @param priv A pointer to moal_private structure
* @param peer A pointer to peer mac
* @param action_code tdls action code
* @param dialog_token dialog_token
* @param status_code status_code
* @param skb skb buffer
*
* @return 0 -- success, otherwise fail
*/
static int
woal_construct_tdls_data_frame(moal_private *priv, t_u8 *peer,
t_u8 action_code, t_u8 dialog_token,
t_u16 status_code, struct sk_buff *skb)
{
struct ieee80211_tdls_data *tdata;
t_u16 capability;
IEEEtypes_HTCap_t *HTcap;
IEEEtypes_HTInfo_t *HTInfo;
IEEEtypes_2040BSSCo_t *BSSCo;
IEEEtypes_VHTCap_t *VHTcap;
IEEEtypes_VHTOprat_t *vht_oprat;
IEEEtypes_AID_t *AidInfo;
IEEEtypes_Generic_t *pSupp_chan = NULL, *pRegulatory_class = NULL;
mlan_ds_misc_tdls_ies *tdls_ies = NULL;
int ret = 0;
mlan_bss_info bss_info;
enum ieee80211_band band;
mlan_fw_info fw_info;
ENTER();
memset(&bss_info, 0, sizeof(bss_info));
if (MLAN_STATUS_SUCCESS !=
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
PRINTM(MERROR, "Fail to get bss info\n");
LEAVE();
return -EFAULT;
}
band = woal_band_cfg_to_ieee_band(bss_info.bss_band);
tdls_ies = kzalloc(sizeof(mlan_ds_misc_tdls_ies), GFP_KERNEL);
if (!tdls_ies) {
PRINTM(MERROR, "Fail to alloc memory for tdls_ies\n");
LEAVE();
return -ENOMEM;
}
capability = 0x2421;
memset(&fw_info, 0, sizeof(mlan_fw_info));
tdata = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
moal_memcpy_ext(priv->phandle, tdata->da, peer, ETH_ALEN,
sizeof(tdata->da));
moal_memcpy_ext(priv->phandle, tdata->sa, priv->current_addr, ETH_ALEN,
sizeof(tdata->sa));
tdata->ether_type = cpu_to_be16(MLAN_ETHER_PKT_TYPE_TDLS_ACTION);
tdata->payload_type = WLAN_TDLS_SNAP_RFTYPE;
woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
if (fw_info.fw_bands & BAND_AAC)
woal_tdls_get_ies(priv, peer, tdls_ies,
TDLS_IE_FLAGS_SETUP |
TDLS_IE_FLAGS_EXTCAP |
TDLS_IE_FLAGS_HTCAP |
TDLS_IE_FLAGS_VHTCAP |
TDLS_IE_FLAGS_AID |
TDLS_IE_FLAGS_SUPP_CS_IE);
else
woal_tdls_get_ies(priv, peer, tdls_ies,
TDLS_IE_FLAGS_SETUP |
TDLS_IE_FLAGS_EXTCAP |
TDLS_IE_FLAGS_HTCAP |
TDLS_IE_FLAGS_SUPP_CS_IE);
tdata->category = WLAN_CATEGORY_TDLS;
tdata->action_code = WLAN_TDLS_SETUP_REQUEST;
skb_put(skb, sizeof(tdata->u.setup_req));
tdata->u.setup_req.dialog_token = dialog_token;
tdata->u.setup_req.capability = cpu_to_le16(capability);
woal_add_supported_rates_ie(priv, skb, band);
woal_add_ext_supported_rates_ie(priv, skb, band);
break;
case WLAN_TDLS_SETUP_RESPONSE:
if (fw_info.fw_bands & BAND_AAC)
woal_tdls_get_ies(priv, peer, tdls_ies,
TDLS_IE_FLAGS_EXTCAP |
TDLS_IE_FLAGS_HTCAP |
TDLS_IE_FLAGS_VHTCAP |
TDLS_IE_FLAGS_AID |
TDLS_IE_FLAGS_SUPP_CS_IE);
else
woal_tdls_get_ies(priv, peer, tdls_ies,
TDLS_IE_FLAGS_EXTCAP |
TDLS_IE_FLAGS_HTCAP |
TDLS_IE_FLAGS_SUPP_CS_IE);
tdata->category = WLAN_CATEGORY_TDLS;
tdata->action_code = WLAN_TDLS_SETUP_RESPONSE;
skb_put(skb, sizeof(tdata->u.setup_resp));
tdata->u.setup_resp.status_code = cpu_to_le16(status_code);
tdata->u.setup_resp.dialog_token = dialog_token;
tdata->u.setup_resp.capability = cpu_to_le16(capability);
woal_add_supported_rates_ie(priv, skb, band);
woal_add_ext_supported_rates_ie(priv, skb, band);
break;
case WLAN_TDLS_SETUP_CONFIRM:
if (fw_info.fw_bands & BAND_AAC)
woal_tdls_get_ies(priv, peer, tdls_ies,
TDLS_IE_FLAGS_EXTCAP |
TDLS_IE_FLAGS_HTINFO |
TDLS_IE_FLAGS_VHTOPRAT |
TDLS_IE_FLAGS_QOS_INFO);
else
woal_tdls_get_ies(priv, peer, tdls_ies,
TDLS_IE_FLAGS_EXTCAP |
TDLS_IE_FLAGS_HTINFO |
TDLS_IE_FLAGS_QOS_INFO);
tdata->category = WLAN_CATEGORY_TDLS;
tdata->action_code = WLAN_TDLS_SETUP_CONFIRM;
skb_put(skb, sizeof(tdata->u.setup_cfm));
tdata->u.setup_cfm.status_code = cpu_to_le16(status_code);
tdata->u.setup_cfm.dialog_token = dialog_token;
break;
case WLAN_TDLS_TEARDOWN:
tdata->category = WLAN_CATEGORY_TDLS;
tdata->action_code = WLAN_TDLS_TEARDOWN;
skb_put(skb, sizeof(tdata->u.teardown));
tdata->u.teardown.reason_code = cpu_to_le16(status_code);
break;
case WLAN_TDLS_DISCOVERY_REQUEST:
tdata->category = WLAN_CATEGORY_TDLS;
tdata->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
skb_put(skb, sizeof(tdata->u.discover_req));
tdata->u.discover_req.dialog_token = dialog_token;
break;
default:
ret = -EINVAL;
goto done;
}
if (action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_TDLS_SETUP_RESPONSE) {
/* supported chanel ie */
if (tdls_ies->supp_chan[0] == SUPPORTED_CHANNELS) {
pSupp_chan =
(void *)skb_put(skb,
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1]);
memset(pSupp_chan, 0,
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1]);
moal_memcpy_ext(priv->phandle, pSupp_chan,
tdls_ies->supp_chan,
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1],
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1]);
}
/* supported regulatory class ie */
if (tdls_ies->regulatory_class[0] == REGULATORY_CLASS) {
pRegulatory_class =
(void *)skb_put(skb,
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1]);
memset(pRegulatory_class, 0,
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1]);
moal_memcpy_ext(priv->phandle, pRegulatory_class,
tdls_ies->regulatory_class,
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1],
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1]);
}
woal_tdls_add_ext_capab(priv, skb, tdls_ies);
}
/* TODO we should fill in ht_cap and htinfo with correct value */
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
case WLAN_TDLS_SETUP_RESPONSE:
/*HT capability */
if (tdls_ies->ht_cap[0] == HT_CAPABILITY) {
HTcap = (void *)skb_put(skb, sizeof(IEEEtypes_HTCap_t));
memset(HTcap, 0, sizeof(IEEEtypes_HTCap_t));
moal_memcpy_ext(priv->phandle, HTcap, tdls_ies->ht_cap,
sizeof(IEEEtypes_HTCap_t),
sizeof(IEEEtypes_HTCap_t));
} else {
PRINTM(MIOCTL, "No TDLS HT capability\n");
}
/*20_40_bss_coexist */
BSSCo = (void *)skb_put(skb, sizeof(IEEEtypes_2040BSSCo_t));
memset(BSSCo, 0, sizeof(IEEEtypes_2040BSSCo_t));
BSSCo->ieee_hdr.element_id = BSSCO_2040;
BSSCo->ieee_hdr.len = sizeof(IEEEtypes_2040BSSCo_t) -
sizeof(IEEEtypes_Header_t);
BSSCo->bss_co_2040.bss_co_2040_value = 0x01;
/* VHT capability */
if (tdls_ies->vht_cap[0] == VHT_CAPABILITY) {
VHTcap = (void *)skb_put(skb,
sizeof(IEEEtypes_VHTCap_t));
memset(VHTcap, 0, sizeof(IEEEtypes_VHTCap_t));
moal_memcpy_ext(priv->phandle, VHTcap,
tdls_ies->vht_cap,
sizeof(IEEEtypes_VHTCap_t),
sizeof(IEEEtypes_VHTCap_t));
} else {
PRINTM(MIOCTL, "NO TDLS VHT capability\n");
}
/* AID info */
if (tdls_ies->aid_info[0] == AID_INFO) {
AidInfo = (void *)skb_put(skb, sizeof(IEEEtypes_AID_t));
memset(AidInfo, 0, sizeof(IEEEtypes_AID_t));
moal_memcpy_ext(priv->phandle, AidInfo,
tdls_ies->aid_info,
sizeof(IEEEtypes_AID_t),
sizeof(IEEEtypes_AID_t));
} else {
PRINTM(MIOCTL, "No TDLS AID info\n");
}
break;
case WLAN_TDLS_SETUP_CONFIRM:
/*HT information */
if (tdls_ies->ht_info[0] == HT_OPERATION) {
HTInfo = (void *)skb_put(skb,
sizeof(IEEEtypes_HTInfo_t));
memset(HTInfo, 0, sizeof(IEEEtypes_HTInfo_t));
moal_memcpy_ext(priv->phandle, HTInfo,
tdls_ies->ht_info,
sizeof(IEEEtypes_HTInfo_t),
sizeof(IEEEtypes_HTInfo_t));
} else
PRINTM(MIOCTL, "No TDLS HT information\n");
/** VHT operation */
if (tdls_ies->vht_oprat[0] == VHT_OPERATION) {
vht_oprat =
(void *)skb_put(skb,
sizeof(IEEEtypes_VHTOprat_t));
memset(vht_oprat, 0, sizeof(IEEEtypes_VHTOprat_t));
moal_memcpy_ext(priv->phandle, vht_oprat,
tdls_ies->vht_oprat,
sizeof(IEEEtypes_VHTOprat_t),
sizeof(IEEEtypes_VHTOprat_t));
} else
PRINTM(MIOCTL, "NO TDLS VHT Operation IE\n");
break;
default:
break;
}
if (action_code == WLAN_TDLS_SETUP_REQUEST ||
action_code == WLAN_TDLS_SETUP_RESPONSE) {
/*wmm info */
woal_add_wmm_ie(priv, skb, WMM_TYPE_INFO, NULL);
} else if (action_code == WLAN_TDLS_SETUP_CONFIRM) {
/*wmm parameter */
woal_add_wmm_ie(priv, skb, WMM_TYPE_PARAMETER,
&tdls_ies->QosInfo);
}
done:
kfree(tdls_ies);
return ret;
}
/**
* @brief woal construct tdls action frame
*
* @param priv A pointer to moal_private structure
* @param peer A pointer to peer mac
* @param action_code tdls action code
* @param dialog_token dialog_token
* @param status_code status_code
* @param skb skb buffer
*
* @return 0 -- success, otherwise fail
*/
static int
woal_construct_tdls_action_frame(moal_private *priv, t_u8 *peer,
t_u8 action_code, t_u8 dialog_token,
t_u16 status_code, struct sk_buff *skb)
{
struct ieee80211_mgmt *mgmt;
t_u8 addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
t_u16 capability;
t_u8 *pos = NULL;
mlan_ds_misc_tdls_ies *tdls_ies = NULL;
mlan_bss_info bss_info;
enum ieee80211_band band;
IEEEtypes_Generic_t *pSupp_chan = NULL, *pRegulatory_class = NULL;
int ret = 0;
ENTER();
memset(&bss_info, 0, sizeof(bss_info));
if (MLAN_STATUS_SUCCESS !=
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) {
PRINTM(MERROR, "Fail to get bss info\n");
LEAVE();
return -EFAULT;
}
band = woal_band_cfg_to_ieee_band(bss_info.bss_band);
tdls_ies = kzalloc(sizeof(mlan_ds_misc_tdls_ies), GFP_KERNEL);
if (!tdls_ies) {
PRINTM(MERROR, "Fail to alloc memory for tdls_ies\n");
LEAVE();
return -ENOMEM;
}
mgmt = (void *)skb_put(skb, 24);
memset(mgmt, 0, 24);
moal_memcpy_ext(priv->phandle, mgmt->da, peer, ETH_ALEN,
sizeof(mgmt->da));
moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN,
sizeof(mgmt->sa));
moal_memcpy_ext(priv->phandle, mgmt->bssid, priv->cfg_bssid, ETH_ALEN,
sizeof(mgmt->bssid));
mgmt->frame_control =
cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION);
/* add address 4 */
pos = skb_put(skb, ETH_ALEN);
capability = 0x2421;
switch (action_code) {
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
woal_tdls_get_ies(priv, peer, tdls_ies,
TDLS_IE_FLAGS_EXTCAP |
TDLS_IE_FLAGS_SUPP_CS_IE);
skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
mgmt->u.action.u.tdls_discover_resp.action_code =
WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token;
mgmt->u.action.u.tdls_discover_resp.capability =
cpu_to_le16(capability);
/* move back for addr4 */
memmove(pos + ETH_ALEN, &mgmt->u.action,
1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
/** init address 4 */
moal_memcpy_ext(priv->phandle, pos, addr, ETH_ALEN, ETH_ALEN);
woal_add_supported_rates_ie(priv, skb, band);
woal_add_ext_supported_rates_ie(priv, skb, band);
woal_tdls_add_ext_capab(priv, skb, tdls_ies);
/* supported chanel ie */
if (tdls_ies->supp_chan[0] == SUPPORTED_CHANNELS) {
pSupp_chan =
(void *)skb_put(skb,
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1]);
memset(pSupp_chan, 0,
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1]);
moal_memcpy_ext(priv->phandle, pSupp_chan,
tdls_ies->supp_chan,
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1],
sizeof(IEEEtypes_Header_t) +
tdls_ies->supp_chan[1]);
}
/* supported regulatory class ie */
if (tdls_ies->regulatory_class[0] == REGULATORY_CLASS) {
pRegulatory_class =
(void *)skb_put(skb,
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1]);
memset(pRegulatory_class, 0,
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1]);
moal_memcpy_ext(priv->phandle, pRegulatory_class,
tdls_ies->regulatory_class,
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1],
sizeof(IEEEtypes_Header_t) +
tdls_ies->regulatory_class[1]);
}
break;
default:
ret = -EINVAL;
break;
}
if (tdls_ies)
kfree(tdls_ies);
return ret;
}
/**
* @brief woal add tdls link identifier ie
*
* @param skb skb buffer
* @param src_addr source address
* @param peer peer address
* @param bssid AP's bssid
*
* @return NA
*/
static void
woal_tdls_add_link_ie(moal_private *priv, struct sk_buff *skb,
u8 *src_addr, u8 *peer, u8 *bssid)
{
struct ieee80211_tdls_lnkie *lnkid;
lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
lnkid->ie_type = WLAN_EID_LINK_ID;
lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
moal_memcpy_ext(priv->phandle, lnkid->bssid, bssid, ETH_ALEN,
sizeof(lnkid->bssid));
moal_memcpy_ext(priv->phandle, lnkid->init_sta, src_addr, ETH_ALEN,
sizeof(lnkid->init_sta));
moal_memcpy_ext(priv->phandle, lnkid->resp_sta, peer, ETH_ALEN,
sizeof(lnkid->resp_sta));
}
/**
* @brief woal send tdls action frame
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param peer A pointer to peer mac
* @param action_code tdls action code
* @param dialog_token dialog_token
* @param status_code status_code
* @param extra_ies A pointer to extra ie buffer
* @param extra_ies_len etra ie len
* @param skb skb buffer
*
* @return 0 -- success, otherwise fail
*/
static int
woal_send_tdls_action_frame(struct wiphy *wiphy,
struct net_device *dev, t_u8 *peer,
u8 action_code, t_u8 dialog_token,
t_u16 status_code, const t_u8 *extra_ies,
size_t extra_ies_len)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
pmlan_buffer pmbuf = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
struct sk_buff *skb = NULL;
t_u8 *pos;
t_u32 pkt_type;
t_u32 tx_control;
t_u16 pkt_len;
int ret = 0;
ENTER();
#define HEADER_SIZE 8 /* pkt_type + tx_control */
pmbuf = woal_alloc_mlan_buffer(priv->phandle, MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + sizeof(pkt_len) + max(sizeof(struct ieee80211_mgmt), sizeof(struct ieee80211_tdls_data)) + 50 + /* supported rates */
sizeof(IEEEtypes_ExtCap_t) + /* ext capab */
extra_ies_len +
sizeof(IEEEtypes_tdls_linkie));
if (!pmbuf) {
PRINTM(MERROR, "Fail to allocate mlan_buffer\n");
ret = -ENOMEM;
goto done;
}
skb = (struct sk_buff *)pmbuf->pdesc;
skb_put(skb, MLAN_MIN_DATA_HEADER_LEN);
pos = skb_put(skb, HEADER_SIZE + sizeof(pkt_len));
pkt_type = MRVL_PKT_TYPE_MGMT_FRAME;
tx_control = 0;
memset(pos, 0, HEADER_SIZE + sizeof(pkt_len));
moal_memcpy_ext(priv->phandle, pos, &pkt_type, sizeof(pkt_type),
sizeof(pkt_type));
moal_memcpy_ext(priv->phandle, pos + sizeof(pkt_type), &tx_control,
sizeof(tx_control), sizeof(tx_control));
woal_construct_tdls_action_frame(priv, peer, action_code, dialog_token,
status_code, skb);
if (extra_ies_len)
moal_memcpy_ext(priv->phandle, skb_put(skb, extra_ies_len),
extra_ies, extra_ies_len, extra_ies_len);
/* the TDLS link IE is always added last */
/* we are the responder */
woal_tdls_add_link_ie(priv, skb, peer, priv->current_addr,
priv->cfg_bssid);
/*
* According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
* we should default to AC_VI.
*/
skb_set_queue_mapping(skb, WMM_AC_VI);
skb->priority = 5;
pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN;
pmbuf->data_len = skb->len - pmbuf->data_offset;
pmbuf->priority = skb->priority;
pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA;
pmbuf->bss_index = priv->bss_index;
pkt_len = pmbuf->data_len - HEADER_SIZE - sizeof(pkt_len);
moal_memcpy_ext(priv->phandle,
pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE,
&pkt_len, sizeof(pkt_len), sizeof(pkt_len));
DBG_HEXDUMP(MDAT_D, "TDLS action:", pmbuf->pbuf + pmbuf->data_offset,
pmbuf->data_len);
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
switch (status) {
case MLAN_STATUS_PENDING:
atomic_inc(&priv->phandle->tx_pending);
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
break;
case MLAN_STATUS_SUCCESS:
woal_free_mlan_buffer(priv->phandle, pmbuf);
break;
case MLAN_STATUS_FAILURE:
default:
woal_free_mlan_buffer(priv->phandle, pmbuf);
ret = -EFAULT;
break;
}
done:
LEAVE();
return ret;
}
/**
* @brief woal send tdls data frame
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param peer A pointer to peer mac
* @param action_code tdls action code
* @param dialog_token dialog_token
* @param status_code status_code
* @param extra_ies A pointer to extra ie buffer
* @param extra_ies_len etra ie len
* @param skb skb buffer
*
* @return 0 -- success, otherwise fail
*/
static int
woal_send_tdls_data_frame(struct wiphy *wiphy,
struct net_device *dev, t_u8 *peer,
u8 action_code, t_u8 dialog_token,
t_u16 status_code, const t_u8 *extra_ies,
size_t extra_ies_len)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
pmlan_buffer pmbuf = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
struct sk_buff *skb = NULL;
int ret = 0;
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
t_u32 index = 0;
#endif
ENTER();
skb = dev_alloc_skb(priv->extra_tx_head_len + MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + max(sizeof(struct ieee80211_mgmt), sizeof(struct ieee80211_tdls_data)) + 50 + /* supported rates */
sizeof(IEEEtypes_ExtCap_t) + /* ext capab */
3 + /* Qos Info */
sizeof(IEEEtypes_WmmParameter_t) + /*wmm ie */
sizeof(IEEEtypes_HTCap_t) +
sizeof(IEEEtypes_2040BSSCo_t) +
sizeof(IEEEtypes_HTInfo_t) +
sizeof(IEEEtypes_VHTCap_t) +
sizeof(IEEEtypes_VHTOprat_t) +
sizeof(IEEEtypes_AID_t) + extra_ies_len +
sizeof(IEEEtypes_tdls_linkie));
if (!skb)
return -ENOMEM;
skb_reserve(skb, MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) +
priv->extra_tx_head_len);
woal_construct_tdls_data_frame(priv, peer, action_code, dialog_token,
status_code, skb);
if (extra_ies_len)
moal_memcpy_ext(priv->phandle, skb_put(skb, extra_ies_len),
extra_ies, extra_ies_len, extra_ies_len);
/* the TDLS link IE is always added last */
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_TEARDOWN:
case WLAN_TDLS_DISCOVERY_REQUEST:
/* we are the initiator */
woal_tdls_add_link_ie(priv, skb, priv->current_addr, peer,
priv->cfg_bssid);
break;
case WLAN_TDLS_SETUP_RESPONSE:
/* we are the responder */
woal_tdls_add_link_ie(priv, skb, peer, priv->current_addr,
priv->cfg_bssid);
break;
default:
ret = -ENOTSUPP;
goto fail;
}
/*
* According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
* we should default to AC_VI.
*/
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
case WLAN_TDLS_SETUP_RESPONSE:
skb_set_queue_mapping(skb, WMM_AC_BK);
skb->priority = 2;
break;
default:
skb_set_queue_mapping(skb, WMM_AC_VI);
skb->priority = 5;
break;
}
pmbuf = (mlan_buffer *)skb->head;
memset((t_u8 *)pmbuf, 0, sizeof(mlan_buffer));
pmbuf->bss_index = priv->bss_index;
pmbuf->pdesc = skb;
pmbuf->pbuf = skb->head + sizeof(mlan_buffer);
pmbuf->data_offset = skb->data - (skb->head + sizeof(mlan_buffer));
pmbuf->data_len = skb->len;
pmbuf->priority = skb->priority;
pmbuf->buf_type = MLAN_BUF_TYPE_DATA;
DBG_HEXDUMP(MDAT_D, "TDLS data:", pmbuf->pbuf + pmbuf->data_offset,
pmbuf->data_len);
status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf);
switch (status) {
case MLAN_STATUS_PENDING:
atomic_inc(&priv->phandle->tx_pending);
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
index = skb_get_queue_mapping(skb);
atomic_inc(&priv->wmm_tx_pending[index]);
#endif
queue_work(priv->phandle->workqueue, &priv->phandle->main_work);
/*delay 10 ms to guarantee the teardown/confirm frame can be
* sent out before disalbe/enable tdls link if we don't delay
* and return immediately, wpa_supplicant will call
* disalbe/enable tdls link this may cause tdls link
* disabled/enabled before teardown/confirm frame sent out */
if (action_code == WLAN_TDLS_TEARDOWN ||
action_code == WLAN_TDLS_SETUP_CONFIRM)
woal_sched_timeout(10);
break;
case MLAN_STATUS_SUCCESS:
dev_kfree_skb(skb);
break;
case MLAN_STATUS_FAILURE:
default:
dev_kfree_skb(skb);
ret = -ENOTSUPP;
break;
}
LEAVE();
return ret;
fail:
dev_kfree_skb(skb);
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
/**
* @brief Tx TDLS packet
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param peer A pointer to peer mac
* @param action_code tdls action code
* @param dialog_token dialog_token
* @param status_code status_code
* @param peer_capability peer capability
* @param initiator initiator
* @param extra_ies A pointer to extra ie buffer
* @param extra_ies_len etra ie len
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
const t_u8 *peer, u8 action_code, t_u8 dialog_token,
t_u16 status_code, t_u32 peer_capability,
bool initiator, const t_u8 *extra_ies,
size_t extra_ies_len)
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
/**
* @brief Tx TDLS packet
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param peer A pointer to peer mac
* @param action_code tdls action code
* @param dialog_token dialog_token
* @param status_code status_code
* @param peer_capability peer capability
* @param extra_ies A pointer to extra ie buffer
* @param extra_ies_len etra ie len
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const t_u8 *peer,
#else
t_u8 *peer,
#endif
u8 action_code, t_u8 dialog_token,
t_u16 status_code, t_u32 peer_capability,
const t_u8 *extra_ies, size_t extra_ies_len)
#else
/**
* @brief Tx TDLS packet
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param peer A pointer to peer mac
* @param action_code tdls action code
* @param dialog_token dialog_token
* @param status_code status_code
* @param extra_ies A pointer to extra ie buffer
* @param extra_ies_len etra ie len
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
t_u8 *peer, u8 action_code, t_u8 dialog_token,
t_u16 status_code, const t_u8 *extra_ies,
size_t extra_ies_len)
#endif
#endif
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
int ret = 0;
mlan_bss_info bss_info;
ENTER();
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) {
LEAVE();
return -ENOTSUPP;
}
/* make sure we are not in uAP mode and Go mode */
if (priv->bss_type != MLAN_BSS_TYPE_STA) {
LEAVE();
return -ENOTSUPP;
}
/* check if AP prohited TDLS */
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
if (IS_EXTCAP_TDLS_PROHIBITED(bss_info.ext_cap)) {
PRINTM(MMSG, "TDLS is prohibited by AP\n");
LEAVE();
return -ENOTSUPP;
}
switch (action_code) {
case TDLS_SETUP_REQUEST:
woal_add_tdls_peer(priv, (t_u8 *)peer);
PRINTM(MMSG,
"wlan: Send TDLS Setup Request to " MACSTR
" status_code=%d\n", MAC2STR(peer), status_code);
ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
action_code, dialog_token,
status_code, extra_ies,
extra_ies_len);
break;
case TDLS_SETUP_RESPONSE:
PRINTM(MMSG,
"wlan: Send TDLS Setup Response to " MACSTR
" status_code=%d\n", MAC2STR(peer), status_code);
ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
action_code, dialog_token,
status_code, extra_ies,
extra_ies_len);
break;
case TDLS_SETUP_CONFIRM:
PRINTM(MMSG,
"wlan: Send TDLS Confirm to " MACSTR " status_code=%d\n",
MAC2STR(peer), status_code);
ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
action_code, dialog_token,
status_code, extra_ies,
extra_ies_len);
break;
case TDLS_TEARDOWN:
PRINTM(MMSG, "wlan: Send TDLS Tear down to " MACSTR "\n",
MAC2STR(peer));
ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
action_code, dialog_token,
status_code, extra_ies,
extra_ies_len);
break;
case TDLS_DISCOVERY_REQUEST:
PRINTM(MMSG,
"wlan: Send TDLS Discovery Request to " MACSTR "\n",
MAC2STR(peer));
ret = woal_send_tdls_data_frame(wiphy, dev, (t_u8 *)peer,
action_code, dialog_token,
status_code, extra_ies,
extra_ies_len);
break;
case TDLS_DISCOVERY_RESPONSE:
PRINTM(MMSG,
"wlan: Send TDLS Discovery Response to " MACSTR "\n",
MAC2STR(peer));
ret = woal_send_tdls_action_frame(wiphy, dev, (t_u8 *)peer,
action_code, dialog_token,
status_code, extra_ies,
extra_ies_len);
break;
default:
break;
}
LEAVE();
return ret;
}
/**
* @brief cfg80211_tdls_oper handler
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param peer tdls peer mac
* @param oper tdls operation code
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *peer,
#else
u8 *peer,
#endif
enum nl80211_tdls_operation oper)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
t_u8 action;
int ret = 0;
t_u8 event_buf[32];
int custom_len = 0;
ENTER();
if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
return -ENOTSUPP;
if (!(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
return -ENOTSUPP;
/* make sure we are in managed mode, and associated */
if (priv->bss_type != MLAN_BSS_TYPE_STA)
return -ENOTSUPP;
PRINTM(MIOCTL, "wlan: TDLS peer=" MACSTR ", oper=%d\n", MAC2STR(peer),
oper);
switch (oper) {
case NL80211_TDLS_ENABLE_LINK:
/*Configure TDLS link first */
woal_tdls_oper(priv, (u8 *)peer, WLAN_TDLS_CONFIG_LINK);
woal_updata_peer_status(priv, (t_u8 *)peer,
TDLS_SETUP_COMPLETE);
PRINTM(MMSG, "wlan: TDLS_ENABLE_LINK: peer=" MACSTR "\n",
MAC2STR(peer));
action = WLAN_TDLS_ENABLE_LINK;
memset(event_buf, 0, sizeof(event_buf));
custom_len = strlen(CUS_EVT_TDLS_CONNECTED);
moal_memcpy_ext(priv->phandle, event_buf,
CUS_EVT_TDLS_CONNECTED, custom_len,
sizeof(event_buf));
moal_memcpy_ext(priv->phandle, event_buf + custom_len, peer,
ETH_ALEN, sizeof(event_buf) - custom_len);
woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN);
break;
case NL80211_TDLS_DISABLE_LINK:
woal_updata_peer_status(priv, (t_u8 *)peer, TDLS_NOT_SETUP);
PRINTM(MMSG, "wlan: TDLS_DISABLE_LINK: peer=" MACSTR "\n",
MAC2STR(peer));
action = WLAN_TDLS_DISABLE_LINK;
memset(event_buf, 0, sizeof(event_buf));
custom_len = strlen(CUS_EVT_TDLS_TEARDOWN);
moal_memcpy_ext(priv->phandle, event_buf, CUS_EVT_TDLS_TEARDOWN,
custom_len, sizeof(event_buf));
moal_memcpy_ext(priv->phandle, event_buf + custom_len, peer,
ETH_ALEN, sizeof(event_buf) - custom_len);
woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN);
break;
case NL80211_TDLS_TEARDOWN:
case NL80211_TDLS_SETUP:
case NL80211_TDLS_DISCOVERY_REQ:
return 0;
default:
return -ENOTSUPP;
}
ret = woal_tdls_oper(priv, (u8 *)peer, action);
LEAVE();
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
/**
* @brief tdls channel switch
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param addr A pointer to peer addr
* @param oper_class The operating class
* @param chandef A pointer to cfg80211_chan_def structure
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_tdls_channel_switch(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr, u8 oper_class,
struct cfg80211_chan_def *chandef)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_tdls_config *tdls_data = NULL;
tdls_all_config *tdls_all_cfg = NULL;
int ret = 0;
mlan_status status = MLAN_STATUS_SUCCESS;
mlan_bss_info bss_info;
ENTER();
/* check if AP prohited TDLS channel switch */
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
if (IS_EXTCAP_TDLS_CHLSWITCHPROHIB(bss_info.ext_cap)) {
PRINTM(MMSG, "TDLS Channel Switching is prohibited by AP\n");
LEAVE();
return -ENOTSUPP;
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
ret = -ENOMEM;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_TDLS_OPER;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
ioctl_req->action = MLAN_ACT_SET;
tdls_data = &misc->param.tdls_config;
tdls_data->tdls_action = WLAN_TDLS_INIT_CHAN_SWITCH;
tdls_all_cfg = (tdls_all_config *)tdls_data->tdls_data;
moal_memcpy_ext(priv->phandle,
tdls_all_cfg->u.tdls_chan_switch.peer_mac_addr, addr,
ETH_ALEN,
sizeof(tdls_all_cfg->u.tdls_chan_switch.peer_mac_addr));
tdls_all_cfg->u.tdls_chan_switch.primary_channel =
chandef->chan->hw_value;
tdls_all_cfg->u.tdls_chan_switch.band = chandef->chan->band;
tdls_all_cfg->u.tdls_chan_switch.regulatory_class = oper_class;
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR, "TDLS channel switch request failed.\n");
ret = -EFAULT;
goto done;
}
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
return ret;
}
/**
* @brief tdls cancel channel switch
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param addr A pointer to peer addr
*
*/
void
woal_cfg80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
struct net_device *dev, const u8 *addr)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
mlan_ioctl_req *ioctl_req = NULL;
mlan_ds_misc_cfg *misc = NULL;
mlan_ds_misc_tdls_config *tdls_data = NULL;
mlan_status status = MLAN_STATUS_SUCCESS;
ENTER();
if (!priv || !priv->phandle) {
PRINTM(MERROR, "priv or handle is null\n");
status = MLAN_STATUS_FAILURE;
goto done;
}
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg));
if (ioctl_req == NULL) {
status = MLAN_STATUS_FAILURE;
goto done;
}
misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf;
misc->sub_command = MLAN_OID_MISC_TDLS_CONFIG;
ioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
ioctl_req->action = MLAN_ACT_SET;
tdls_data = &misc->param.tdls_config;
tdls_data->tdls_action = WLAN_TDLS_STOP_CHAN_SWITCH;
moal_memcpy_ext(priv->phandle, tdls_data->tdls_data, addr, ETH_ALEN,
sizeof(tdls_data->tdls_data));
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_SUCCESS)
goto done;
PRINTM(MIOCTL, "Tdls channel switch stop!\n");
done:
if (status != MLAN_STATUS_PENDING)
kfree(ioctl_req);
LEAVE();
}
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
/**
* @brief change station info
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param mac A pointer to peer mac
* @param params station parameters
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *mac,
#else
u8 *mac,
#endif
struct station_parameters *params)
{
int ret = 0;
ENTER();
/**do nothing*/
LEAVE();
return ret;
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
#ifdef UAP_SUPPORT
/**
* @brief add station
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param mac A pointer to peer mac
* @param params station parameters
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
const u8 *mac,
#else
u8 *mac,
#endif
struct station_parameters *params)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
int ret = 0;
ENTER();
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
#ifdef UAP_SUPPORT
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME) &&
(priv->bss_role == MLAN_BSS_ROLE_UAP)) {
ret = woal_cfg80211_uap_add_station(wiphy, dev, (u8 *)mac,
params);
LEAVE();
return ret;
}
#endif
#endif
if (!(params->sta_flags_set & MBIT(NL80211_STA_FLAG_TDLS_PEER)))
goto done;
/* make sure we are in connected mode */
if ((priv->bss_type != MLAN_BSS_TYPE_STA) ||
(priv->media_connected == MFALSE)) {
ret = -ENOTSUPP;
goto done;
}
PRINTM(MMSG, "wlan: TDLS add peer station, address =" MACSTR "\n",
MAC2STR(mac));
ret = woal_tdls_oper(priv, (u8 *)mac, WLAN_TDLS_CREATE_LINK);
done:
LEAVE();
return ret;
}
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
/**
* @brief Update ft ie for Fast BSS Transition
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param ftie A pointer to cfg80211_update_ft_ies_params structure
*
* @return 0 success , other failure
*/
int
woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_update_ft_ies_params *ftie)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
IEEEtypes_MobilityDomain_t *md_ie = NULL;
int ret = 0;
mlan_ds_misc_assoc_rsp assoc_rsp;
IEEEtypes_AssocRsp_t *passoc_rsp = NULL;
mlan_bss_info bss_info;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
struct cfg80211_roam_info roam_info = { };
#endif
ENTER();
if (!ftie) {
LEAVE();
return ret;
}
#ifdef MLAN_64BIT
PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %lx \n", ftie->ie_len);
#else
PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %x \n", ftie->ie_len);
#endif
md_ie = (IEEEtypes_MobilityDomain_t *)woal_parse_ie_tlv(ftie->ie,
ftie->ie_len,
MOBILITY_DOMAIN);
if (!md_ie) {
PRINTM(MERROR, "No Mobility domain IE\n");
LEAVE();
return ret;
}
priv->ft_cap = md_ie->ft_cap;
if (priv->ft_ie_len) {
priv->pre_ft_ie_len = priv->ft_ie_len;
moal_memcpy_ext(priv->phandle, priv->pre_ft_ie, priv->ft_ie,
priv->ft_ie_len, MAX_IE_SIZE);
}
memset(priv->ft_ie, 0, MAX_IE_SIZE);
moal_memcpy_ext(priv->phandle, priv->ft_ie, ftie->ie,
MIN(ftie->ie_len, MAX_IE_SIZE), sizeof(priv->ft_ie));
priv->ft_ie_len = ftie->ie_len;
priv->ft_md = ftie->md;
if (!priv->ft_pre_connect) {
LEAVE();
return ret;
}
/* check if is different AP */
if (!memcmp(&priv->target_ap_bssid, priv->cfg_bssid,
MLAN_MAC_ADDR_LENGTH)) {
PRINTM(MMSG, "This is the same AP, no Fast bss transition\n");
priv->ft_pre_connect = MFALSE;
priv->ft_ie_len = 0;
LEAVE();
return 0;
}
/* start fast BSS transition to target AP */
priv->assoc_status = 0;
priv->sme_current.bssid = priv->conn_bssid;
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
&priv->target_ap_bssid, MLAN_MAC_ADDR_LENGTH,
sizeof(priv->conn_bssid));
memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp));
ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current,
MOAL_IOCTL_WAIT, &assoc_rsp);
if ((priv->ft_cap & MBIT(0)) || priv->ft_roaming_triggered_by_driver) {
if (!ret) {
woal_inform_bss_from_scan_result(priv, NULL,
MOAL_IOCTL_WAIT);
passoc_rsp = (IEEEtypes_AssocRsp_t *)
assoc_rsp.assoc_resp_buf;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
roam_info.bssid = priv->cfg_bssid;
roam_info.req_ie = priv->sme_current.ie;
roam_info.req_ie_len = priv->sme_current.ie_len;
roam_info.resp_ie = passoc_rsp->ie_buffer;
roam_info.resp_ie_len = assoc_rsp.assoc_resp_len -
ASSOC_RESP_FIXED_SIZE;
cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL);
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid,
priv->sme_current.ie,
priv->sme_current.ie_len,
passoc_rsp->ie_buffer,
assoc_rsp.assoc_resp_len -
ASSOC_RESP_FIXED_SIZE, GFP_KERNEL);
#else
cfg80211_roamed(priv->netdev, priv->cfg_bssid,
priv->sme_current.ie,
priv->sme_current.ie_len,
passoc_rsp->ie_buffer,
assoc_rsp.assoc_resp_len -
ASSOC_RESP_FIXED_SIZE, GFP_KERNEL);
#endif
#endif
PRINTM(MMSG,
"Fast BSS transition to bssid " MACSTR
" successfully\n", MAC2STR(priv->cfg_bssid));
} else {
PRINTM(MMSG,
"Fast BSS transition failed, keep connect to "
MACSTR " \n", MAC2STR(priv->cfg_bssid));
moal_memcpy_ext(priv->phandle,
(void *)priv->sme_current.bssid,
&priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH,
sizeof(priv->conn_bssid));
priv->ft_ie_len = priv->pre_ft_ie_len;
moal_memcpy_ext(priv->phandle, priv->ft_ie,
priv->pre_ft_ie, priv->pre_ft_ie_len,
MAX_IE_SIZE);
}
priv->ft_roaming_triggered_by_driver = MFALSE;
} else {
if (!ret) {
memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp));
woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_IOCTL_WAIT);
passoc_rsp = (IEEEtypes_AssocRsp_t *)
assoc_rsp.assoc_resp_buf;
cfg80211_connect_result(priv->netdev, priv->cfg_bssid,
NULL, 0, passoc_rsp->ie_buffer,
assoc_rsp.assoc_resp_len -
ASSOC_RESP_FIXED_SIZE,
WLAN_STATUS_SUCCESS,
GFP_KERNEL);
PRINTM(MMSG,
"wlan: Fast Bss transition to bssid " MACSTR
" successfully\n", MAC2STR(priv->cfg_bssid));
memset(&bss_info, 0, sizeof(bss_info));
woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
priv->channel = bss_info.bss_chan;
} else {
PRINTM(MMSG,
"wlan: Failed to connect to bssid " MACSTR "\n",
MAC2STR(priv->target_ap_bssid));
cfg80211_connect_result(priv->netdev,
priv->target_ap_bssid, NULL, 0,
NULL, 0,
woal_get_assoc_status(priv),
GFP_KERNEL);
moal_memcpy_ext(priv->phandle,
(void *)priv->sme_current.bssid,
&priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH,
sizeof(priv->conn_bssid));
memset(priv->target_ap_bssid, 0, ETH_ALEN);
priv->ft_ie_len = priv->pre_ft_ie_len;
moal_memcpy_ext(priv->phandle, priv->ft_ie,
priv->pre_ft_ie, priv->pre_ft_ie_len,
MAX_IE_SIZE);
// priv->ft_ie_len = 0;
}
}
priv->ft_pre_connect = MFALSE;
LEAVE();
return 0;
}
#endif
/**
* @brief Save connect parameters for roaming
*
* @param priv A pointer to moal_private
* @param sme A pointer to cfg80211_connect_params structure
*/
void
woal_save_conn_params(moal_private *priv, struct cfg80211_connect_params *sme)
{
ENTER();
woal_clear_conn_params(priv);
moal_memcpy_ext(priv->phandle, &priv->sme_current, sme,
sizeof(struct cfg80211_connect_params),
sizeof(priv->sme_current));
if (sme->channel) {
priv->sme_current.channel = &priv->conn_chan;
moal_memcpy_ext(priv->phandle, priv->sme_current.channel,
sme->channel, sizeof(struct ieee80211_channel),
sizeof(priv->conn_chan));
}
if (sme->bssid) {
priv->sme_current.bssid = priv->conn_bssid;
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
sme->bssid, MLAN_MAC_ADDR_LENGTH,
sizeof(priv->conn_bssid));
}
if (sme->ssid && sme->ssid_len) {
priv->sme_current.ssid = priv->conn_ssid;
memset(priv->conn_ssid, 0, MLAN_MAX_SSID_LENGTH);
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ssid,
sme->ssid, sme->ssid_len,
sizeof(priv->conn_ssid));
}
if (sme->ie && sme->ie_len) {
priv->sme_current.ie = kzalloc(sme->ie_len, GFP_KERNEL);
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ie,
sme->ie, sme->ie_len, sme->ie_len);
}
if (sme->key && sme->key_len && (sme->key_len <= MAX_WEP_KEY_SIZE)) {
priv->sme_current.key = priv->conn_wep_key;
moal_memcpy_ext(priv->phandle, (t_u8 *)priv->sme_current.key,
sme->key, sme->key_len,
sizeof(priv->conn_wep_key));
}
if (priv->sinfo)
memset(priv->sinfo, 0, sizeof(struct station_info));
else
priv->sinfo = kzalloc(sizeof(struct station_info), GFP_KERNEL);
}
/**
* @brief clear connect parameters for ing
*
* @param priv A pointer to moal_private
*/
void
woal_clear_conn_params(moal_private *priv)
{
ENTER();
if (priv->sme_current.ie_len)
kfree(priv->sme_current.ie);
memset(&priv->sme_current, 0, sizeof(struct cfg80211_connect_params));
priv->roaming_required = MFALSE;
priv->assoc_bss = NULL;
if (priv->sinfo) {
kfree(priv->sinfo);
priv->sinfo = NULL;
}
LEAVE();
}
/**
* @brief Build new roaming connect ie for okc
*
* @param priv A pointer to moal_private
* @param entry A pointer to pmksa_entry
**/
static int
woal_update_okc_roaming_ie(moal_private *priv, struct pmksa_entry *entry)
{
struct cfg80211_connect_params *sme = &priv->sme_current;
int ret = MLAN_STATUS_SUCCESS;
const t_u8 *sme_pos, *sme_ptr;
t_u8 *okc_ie_pos;
t_u8 id, ie_len;
int left_len;
ENTER();
if (!sme->ie || !sme->ie_len) {
PRINTM(MERROR, "No connect ie saved in driver\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (!entry) {
PRINTM(MERROR, "No roaming ap pmkid\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (!priv->okc_roaming_ie) {
int okc_ie_len = sme->ie_len + sizeof(t_u16) + PMKID_LEN;
/** Alloc new buffer for okc roaming ie */
priv->okc_roaming_ie = kzalloc(okc_ie_len, GFP_KERNEL);
if (!priv->okc_roaming_ie) {
PRINTM(MERROR, "Fail to allocate assoc req ie\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
}
/* Build OKC RSN IE with PMKID list
* Format of RSN IE: length(bytes) and container
* | 1| 1 | 2 | 4 | 2 |
* |id|len|version|group data cipher suite|pairwise cipher suite count|
* | 4 * m | 2 | 4 * n | 2 |
* |pairwise cipher suite list|AKM suite count|AKM suite list|RSN Cap |
* | 2 | 16 * s | 4 |
* |PMKIDCount|PMKID List|Group Management Cipher Suite|
*/
#define PAIRWISE_CIPHER_COUNT_OFFSET 8
#define AKM_SUITE_COUNT_OFFSET(n) (10 + (n)*4)
#define PMKID_COUNT_OFFSET(n) (14 + (n)*4)
sme_pos = sme->ie;
left_len = sme->ie_len;
okc_ie_pos = priv->okc_roaming_ie;
priv->okc_ie_len = 0;
while (left_len >= 2) {
id = *sme_pos;
ie_len = *(sme_pos + 1);
if ((ie_len + 2) > left_len) {
PRINTM(MERROR, "Invalid ie len %d\n", ie_len);
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (id == RSN_IE) {
t_u16 pairwise_count, akm_count;
t_u8 *rsn_ie_len;
int rsn_offset;
pairwise_count =
*(t_u16 *)(sme_pos +
PAIRWISE_CIPHER_COUNT_OFFSET);
akm_count =
*(t_u16 *)(sme_pos +
AKM_SUITE_COUNT_OFFSET
(pairwise_count));
rsn_offset =
PMKID_COUNT_OFFSET(pairwise_count + akm_count);
sme_ptr = (t_u8 *)(sme_pos + rsn_offset);
moal_memcpy_ext(priv->phandle, okc_ie_pos, sme_pos,
rsn_offset, rsn_offset);
rsn_ie_len = okc_ie_pos + 1;
okc_ie_pos += rsn_offset;
*(t_u16 *)okc_ie_pos = 1;
okc_ie_pos += sizeof(t_u16);
moal_memcpy_ext(priv->phandle, okc_ie_pos, entry->pmkid,
PMKID_LEN, PMKID_LEN);
okc_ie_pos += PMKID_LEN;
priv->okc_ie_len +=
rsn_offset + sizeof(t_u16) + PMKID_LEN;
*rsn_ie_len =
rsn_offset - 2 + sizeof(t_u16) + PMKID_LEN;
if ((ie_len + 2) > rsn_offset) {
/** Previous conn ie include pmkid list */
u16 pmkid_count = *(t_u16 *)sme_ptr;
rsn_offset += (sizeof(t_u16) +
PMKID_LEN * pmkid_count);
if ((ie_len + 2) > rsn_offset) {
sme_ptr += (sizeof(t_u16) +
PMKID_LEN * pmkid_count);
moal_memcpy_ext(priv->phandle,
okc_ie_pos, sme_ptr,
(ie_len + 2 -
rsn_offset),
(ie_len + 2 -
rsn_offset));
okc_ie_pos += (ie_len + 2 - rsn_offset);
priv->okc_ie_len +=
(ie_len + 2 - rsn_offset);
*rsn_ie_len +=
(ie_len + 2 - rsn_offset);
}
}
} else {
moal_memcpy_ext(priv->phandle, okc_ie_pos, sme_pos,
ie_len + 2, ie_len + 2);
okc_ie_pos += ie_len + 2;
priv->okc_ie_len += ie_len + 2;
}
sme_pos += (ie_len + 2);
left_len -= (ie_len + 2);
}
done:
if (ret != MLAN_STATUS_SUCCESS) {
if (priv->okc_roaming_ie) {
kfree(priv->okc_roaming_ie);
priv->okc_roaming_ie = NULL;
priv->okc_ie_len = 0;
}
}
LEAVE();
return ret;
}
/**
* @brief Start roaming: driver handle roaming
*
* @param priv A pointer to moal_private structure
*
* @return N/A
*/
void
woal_start_roaming(moal_private *priv)
{
mlan_ds_get_signal signal;
mlan_ssid_bssid ssid_bssid;
char rssi_low[10];
int ret = 0;
mlan_ds_misc_assoc_rsp *assoc_rsp;
IEEEtypes_AssocRsp_t *passoc_rsp = NULL;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
struct cfg80211_roam_info roam_info = { };
#endif
ENTER();
if (priv->ft_roaming_triggered_by_driver) {
PRINTM(MIOCTL, "FT roaming is in processing ...... \n");
LEAVE();
return;
}
if (priv->last_event & EVENT_BG_SCAN_REPORT) {
woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT);
PRINTM(MIOCTL, "Report bgscan result\n");
}
if (priv->media_connected == MFALSE || !priv->sme_current.ssid_len) {
PRINTM(MIOCTL, "Not connected, ignore roaming\n");
LEAVE();
return;
}
/* Get signal information from the firmware */
memset(&signal, 0, sizeof(mlan_ds_get_signal));
if (MLAN_STATUS_SUCCESS !=
woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
PRINTM(MERROR, "Error getting signal information\n");
ret = -EFAULT;
goto done;
}
memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid));
ssid_bssid.ssid.ssid_len = priv->sme_current.ssid_len;
moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid,
priv->sme_current.ssid, priv->sme_current.ssid_len,
sizeof(ssid_bssid.ssid.ssid));
if (MLAN_STATUS_SUCCESS !=
woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) {
PRINTM(MIOCTL, "Can not find better network\n");
ret = -EFAULT;
goto done;
}
/* check if we found different AP */
if (!memcmp(&ssid_bssid.bssid, priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH)) {
PRINTM(MIOCTL, "This is the same AP, no roaming\n");
ret = -EFAULT;
goto done;
}
PRINTM(MIOCTL, "Find AP: bssid=" MACSTR ", signal=%d\n",
MAC2STR(ssid_bssid.bssid), ssid_bssid.rssi);
/* check signal */
if (!(priv->last_event & EVENT_PRE_BCN_LOST)) {
if ((abs(signal.bcn_rssi_avg) - abs(ssid_bssid.rssi)) <
DELTA_RSSI) {
PRINTM(MERROR, "New AP's signal is not good too.\n");
ret = -EFAULT;
goto done;
}
}
/**check if need start FT Roaming*/
if (priv->ft_ie_len && (priv->ft_md == ssid_bssid.ft_md) &&
(priv->ft_cap == ssid_bssid.ft_cap)) {
priv->ft_roaming_triggered_by_driver = MTRUE;
woal_start_ft_roaming(priv, &ssid_bssid);
goto done;
}
/* start roaming to new AP */
priv->sme_current.bssid = priv->conn_bssid;
moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid,
&ssid_bssid.bssid, MLAN_MAC_ADDR_LENGTH,
sizeof(priv->conn_bssid));
#ifdef STA_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) {
/** Check if current roaming support OKC offload roaming */
if (priv->sme_current.crypto.n_akm_suites &&
priv->sme_current.crypto.akm_suites[0] ==
WLAN_AKM_SUITE_8021X) {
struct pmksa_entry *entry = NULL;
/** Get OKC PMK Cache entry
* Firstly try to get pmksa from cfg80211
*/
priv->wait_target_ap_pmkid = MTRUE;
cfg80211_pmksa_candidate_notify(priv->netdev, 0,
priv->sme_current.bssid,
MTRUE, GFP_ATOMIC);
if (wait_event_interruptible_timeout(priv->okc_wait_q,
!priv->
wait_target_ap_pmkid,
OKC_WAIT_TARGET_PMKSA_TIMEOUT))
{
PRINTM(MIOCTL, "OKC Roaming is ready\n");
entry = priv->target_ap_pmksa;
} else {
/** Try to get pmksa from pmksa list */
priv->wait_target_ap_pmkid = MFALSE;
entry = woal_get_pmksa_entry(priv,
priv->sme_current.
bssid);
}
/** Build okc roaming ie */
woal_update_okc_roaming_ie(priv, entry);
priv->target_ap_pmksa = NULL;
}
}
#endif
#endif
assoc_rsp = kzalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_ATOMIC);
if (!assoc_rsp) {
PRINTM(MERROR, "Failed to allocate memory for assoc_rsp\n");
ret = -ENOMEM;
goto done;
}
ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current,
MOAL_IOCTL_WAIT, assoc_rsp);
if (!ret) {
const t_u8 *ie;
int ie_len;
woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT);
passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp->assoc_resp_buf;
/** Update connect ie in roam event */
ie = priv->sme_current.ie;
ie_len = priv->sme_current.ie_len;
#ifdef STA_CFG80211
if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) {
/** Check if current roaming support OKC offload roaming
*/
if (priv->sme_current.crypto.n_akm_suites &&
priv->sme_current.crypto.akm_suites[0] ==
WLAN_AKM_SUITE_8021X) {
if (priv->okc_roaming_ie && priv->okc_ie_len) {
ie = priv->okc_roaming_ie;
ie_len = priv->okc_ie_len;
}
}
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
roam_info.bssid = priv->cfg_bssid;
roam_info.req_ie = ie;
roam_info.req_ie_len = ie_len;
roam_info.resp_ie = passoc_rsp->ie_buffer;
roam_info.resp_ie_len =
assoc_rsp->assoc_resp_len - ASSOC_RESP_FIXED_SIZE;
cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL);
#else
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, ie, ie_len,
passoc_rsp->ie_buffer,
assoc_rsp->assoc_resp_len -
ASSOC_RESP_FIXED_SIZE, GFP_KERNEL);
#else
cfg80211_roamed(priv->netdev, priv->cfg_bssid, ie, ie_len,
passoc_rsp->ie_buffer,
assoc_rsp->assoc_resp_len -
ASSOC_RESP_FIXED_SIZE, GFP_KERNEL);
#endif
#endif
PRINTM(MMSG, "Roamed to bssid " MACSTR " successfully\n",
MAC2STR(priv->cfg_bssid));
} else {
PRINTM(MIOCTL, "Roaming to bssid " MACSTR " failed\n",
MAC2STR(ssid_bssid.bssid));
}
kfree(assoc_rsp);
done:
/* config rssi low threshold again */
priv->last_event = 0;
priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD;
sprintf(rssi_low, "%d", priv->rssi_low);
woal_set_rssi_low_threshold(priv, rssi_low, MOAL_IOCTL_WAIT);
LEAVE();
return;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
#ifdef UAP_SUPPORT
/**
* @brief add uap station
*
* @param wiphy A pointer to wiphy structure
* @param dev A pointer to net_device structure
* @param mac A pointer to peer mac
* @param params station parameters
*
* @return 0 -- success, otherwise fail
*/
int
woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac, struct station_parameters *params)
{
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
mlan_ioctl_req *req = NULL;
t_u32 req_len = 0;
mlan_ds_bss *bss = NULL;
t_u8 *pos;
t_u8 qosinfo;
MrvlIEtypes_Data_t *tlv;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
MrvlExtIEtypes_Data_t *ext_tlv;
#endif
mlan_status status;
int ret = 0;
ENTER();
req_len = sizeof(mlan_ds_bss);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
if (params->ext_capab_len)
req_len += sizeof(MrvlIEtypesHeader_t) + params->ext_capab_len;
#endif
if (params->supported_rates_len)
req_len += sizeof(MrvlIEtypesHeader_t) +
params->supported_rates_len;
if (params->uapsd_queues || params->max_sp)
req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(qosinfo);
if (params->ht_capa)
req_len += sizeof(MrvlIEtypesHeader_t) +
sizeof(struct ieee80211_ht_cap);
if (params->vht_capa)
req_len += sizeof(MrvlIEtypesHeader_t) +
sizeof(struct ieee80211_vht_cap);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (params->opmode_notif_used)
req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(u8);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
if (params->he_capa_len)
req_len += sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len;
#endif
req = woal_alloc_mlan_ioctl_req(req_len);
if (req == NULL) {
ret = -ENOMEM;
goto done;
}
bss = (mlan_ds_bss *)req->pbuf;
bss->sub_command = MLAN_OID_UAP_ADD_STATION;
req->req_id = MLAN_IOCTL_BSS;
req->action = MLAN_ACT_SET;
bss->param.sta_info.listen_interval = params->listen_interval;
bss->param.sta_info.aid = params->aid;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
bss->param.sta_info.cap_info = params->capability;
#else
bss->param.sta_info.cap_info = 0;
#endif
bss->param.sta_info.tlv_len = 0;
bss->param.sta_info.sta_flags = params->sta_flags_set;
moal_memcpy_ext(priv->phandle, bss->param.sta_info.peer_mac, mac,
MLAN_MAC_ADDR_LENGTH,
sizeof(bss->param.sta_info.peer_mac));
PRINTM(MMSG, "wlan: UAP/GO add peer station, address =" MACSTR "\n",
MAC2STR(mac));
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
PRINTM(MCMND,
"sta_flags=0x%x listen_interval=%d aid=%d cap_info=0x%x\n",
params->sta_flags_set, params->listen_interval, params->aid,
params->capability);
#else
PRINTM(MCMND, "sta_flags=0x%x listen_interval=%d aid=%d\n",
params->sta_flags_set, params->listen_interval, params->aid);
#endif
pos = &bss->param.sta_info.tlv[0];
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
if (params->ext_capab_len) {
tlv = (MrvlIEtypes_Data_t *)pos;
tlv->header.type = EXT_CAPABILITY;
tlv->header.len = params->ext_capab_len;
moal_memcpy_ext(priv->phandle, tlv->data, params->ext_capab,
tlv->header.len, tlv->header.len);
pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
bss->param.sta_info.tlv_len +=
sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
tlv = (MrvlIEtypes_Data_t *)pos;
}
#endif
if (params->supported_rates_len) {
tlv = (MrvlIEtypes_Data_t *)pos;
tlv->header.type = SUPPORTED_RATES;
tlv->header.len = params->supported_rates_len;
moal_memcpy_ext(priv->phandle, tlv->data,
params->supported_rates, tlv->header.len,
tlv->header.len);
pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
bss->param.sta_info.tlv_len +=
sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
tlv = (MrvlIEtypes_Data_t *)pos;
}
if (params->uapsd_queues || params->max_sp) {
tlv = (MrvlIEtypes_Data_t *)pos;
tlv->header.type = QOS_INFO;
tlv->header.len = sizeof(qosinfo);
qosinfo = params->uapsd_queues | (params->max_sp << 5);
moal_memcpy_ext(priv->phandle, tlv->data, &qosinfo,
tlv->header.len, tlv->header.len);
pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
bss->param.sta_info.tlv_len +=
sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
tlv = (MrvlIEtypes_Data_t *)pos;
}
if (params->ht_capa) {
tlv = (MrvlIEtypes_Data_t *)pos;
tlv->header.type = HT_CAPABILITY;
tlv->header.len = sizeof(struct ieee80211_ht_cap);
moal_memcpy_ext(priv->phandle, tlv->data, params->ht_capa,
tlv->header.len, tlv->header.len);
pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
bss->param.sta_info.tlv_len +=
sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
tlv = (MrvlIEtypes_Data_t *)pos;
}
if (params->vht_capa) {
tlv = (MrvlIEtypes_Data_t *)pos;
tlv->header.type = VHT_CAPABILITY;
tlv->header.len = sizeof(struct ieee80211_vht_cap);
moal_memcpy_ext(priv->phandle, tlv->data, params->vht_capa,
tlv->header.len, tlv->header.len);
pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
bss->param.sta_info.tlv_len +=
sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
tlv = (MrvlIEtypes_Data_t *)pos;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (params->opmode_notif_used) {
tlv = (MrvlIEtypes_Data_t *)pos;
tlv->header.type = OPER_MODE_NTF;
tlv->header.len = sizeof(u8);
moal_memcpy_ext(priv->phandle, tlv->data, &params->opmode_notif,
tlv->header.len, tlv->header.len);
pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
bss->param.sta_info.tlv_len +=
sizeof(MrvlIEtypesHeader_t) + tlv->header.len;
tlv = (MrvlIEtypes_Data_t *)pos;
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
if (params->he_capa_len) {
ext_tlv = (MrvlExtIEtypes_Data_t *) pos;
ext_tlv->header.type = EXTENSION;
ext_tlv->header.len = params->he_capa_len + sizeof(u8);
ext_tlv->header.ext_id = HE_CAPABILITY;
moal_memcpy_ext(priv->phandle, ext_tlv->data,
(u8 *)params->he_capa, params->he_capa_len,
params->he_capa_len);
pos += sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len;
bss->param.sta_info.tlv_len +=
sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len;
tlv = (MrvlIEtypes_Data_t *)pos;
}
#endif
DBG_HEXDUMP(MCMD_D, "sta tlv", &bss->param.sta_info.tlv[0],
bss->param.sta_info.tlv_len);
/* Send IOCTL request to MLAN */
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
if (status != MLAN_STATUS_PENDING)
kfree(req);
done:
LEAVE();
return ret;
}
/**
* @brief This function is probe client handle.
*
* @param wiphy A pointer to wiphy.
*
* @param dev A pointer to net_device
*
* @param peer A pointer to peer
*
* @param cookie A pointer to cookie
*
* @return 0 -- success, otherwise fail
*/
static int
woal_cfg80211_probe_client(struct wiphy *wiphy,
struct net_device *dev, const u8 *peer, u64 * cookie)
{
return -1;
}
#endif
/**
* @brief Sends deauth packet to kernel
*
* @param priv A pointer to moal_private struct
* @param sa A pointer to source address
* @param reason_code disconnect reason code
* @return N/A
*/
void
woal_host_mlme_disconnect(moal_private *priv, u16 reason_code, u8 *sa)
{
t_u8 broadcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
t_u8 frame_buf[26];
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame_buf;
ENTER();
mgmt->frame_control = (__force __le16) IEEE80211_STYPE_DEAUTH;
mgmt->duration = 0;
mgmt->seq_ctrl = 0;
mgmt->u.deauth.reason_code = (__force __le16) reason_code;
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
moal_memcpy_ext(priv->phandle, mgmt->da, broadcast_addr,
ETH_ALEN, sizeof(mgmt->da));
moal_memcpy_ext(priv->phandle, mgmt->sa,
priv->sme_current.bssid, ETH_ALEN,
sizeof(mgmt->sa));
moal_memcpy_ext(priv->phandle, mgmt->bssid, priv->cfg_bssid,
ETH_ALEN, sizeof(mgmt->bssid));
priv->host_mlme = MFALSE;
priv->auth_flag = 0;
} else {
moal_memcpy_ext(priv->phandle, mgmt->da, priv->current_addr,
ETH_ALEN, sizeof(mgmt->da));
moal_memcpy_ext(priv->phandle, mgmt->sa, sa, ETH_ALEN,
sizeof(mgmt->sa));
moal_memcpy_ext(priv->phandle, mgmt->bssid, priv->current_addr,
ETH_ALEN, sizeof(mgmt->bssid));
PRINTM(MMSG,
"wlan: hostmlme notify deauth station " MACSTR "\n",
MAC2STR(sa));
}
if (GET_BSS_ROLE(priv) != MLAN_BSS_ROLE_UAP) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
mutex_lock(&priv->wdev->mtx);
cfg80211_rx_mlme_mgmt(priv->netdev, frame_buf, 26);
mutex_unlock(&priv->wdev->mtx);
#else
cfg80211_send_deauth(priv->netdev, frame_buf, 26);
#endif
} else {
int freq = ieee80211_channel_to_frequency(priv->channel
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
,
(priv->channel <=
14 ?
IEEE80211_BAND_2GHZ :
IEEE80211_BAND_5GHZ)
#endif
);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
cfg80211_rx_mgmt(
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
priv->wdev,
#else
priv->netdev,
#endif
freq, 0, frame_buf, 26
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
, 0
#endif
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
, GFP_ATOMIC
#endif
);
#else
cfg80211_rx_mgmt(priv->netdev, freq, frame_buf, 26, GFP_ATOMIC);
#endif
}
LEAVE();
return;
}
#endif
/**
* @brief Register the device with cfg80211
*
* @param dev A pointer to net_device structure
* @param bss_type BSS type
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status
woal_register_sta_cfg80211(struct net_device *dev, t_u8 bss_type)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
moal_private *priv = (moal_private *)netdev_priv(dev);
struct wireless_dev *wdev = NULL;
int psmode = 0;
enum ieee80211_band band;
ENTER();
wdev = (struct wireless_dev *)&priv->w_dev;
memset(wdev, 0, sizeof(struct wireless_dev));
wdev->wiphy = priv->phandle->wiphy;
if (!wdev->wiphy) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
if (bss_type == MLAN_BSS_TYPE_STA) {
wdev->iftype = NL80211_IFTYPE_STATION;
priv->roaming_enabled = MFALSE;
priv->roaming_required = MFALSE;
}
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT)
wdev->iftype = NL80211_IFTYPE_STATION;
#endif
#endif
dev_net_set(dev, wiphy_net(wdev->wiphy));
dev->ieee80211_ptr = wdev;
SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
priv->wdev = wdev;
/* Get IEEE power save mode */
if (MLAN_STATUS_SUCCESS == woal_set_get_power_mgmt(priv, MLAN_ACT_GET,
&psmode, 0,
MOAL_IOCTL_WAIT)) {
/* Save the IEEE power save mode to wiphy, because after
* warmreset wiphy power save should be updated instead
* of using the last saved configuration */
if (psmode)
priv->wdev->ps = MTRUE;
else
priv->wdev->ps = MFALSE;
}
if (priv->phandle->country_code[0] && priv->phandle->country_code[1]) {
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
if (priv->phandle->params.cntry_txpwr == CNTRY_RGPOWER_MODE
&& !priv->phandle->params.txpwrlimit_cfg)
queue_work(priv->phandle->evt_workqueue,
&priv->phandle->regulatory_work);
#endif
band = priv->phandle->band;
priv->phandle->band = IEEE80211_BAND_2GHZ;
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
priv->phandle->band = IEEE80211_BAND_5GHZ;
woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT);
priv->phandle->band = band;
}
LEAVE();
return ret;
}
/**
* @brief Initialize the wiphy
*
* @param priv A pointer to moal_private structure
* @param wiphy A pointer to structure wiphy
* @param wait_option Wait option
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
woal_cfg80211_init_wiphy(moal_private *priv, struct wiphy *wiphy,
t_u8 wait_option)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
int retry_count, rts_thr, frag_thr;
mlan_ioctl_req *req = NULL;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
mlan_ds_radio_cfg *radio = NULL;
#endif
pmlan_ds_11n_cfg cfg_11n = NULL;
t_u32 hw_dev_cap;
#ifdef UAP_SUPPORT
pmlan_uap_bss_param sys_cfg = NULL;
#endif
int mcs_supp = 0;
ENTER();
/* Get 11n tx parameters from MLAN */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
cfg_11n = (mlan_ds_11n_cfg *)req->pbuf;
cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG;
req->req_id = MLAN_IOCTL_11N_CFG;
req->action = MLAN_ACT_GET;
cfg_11n->param.htcap_cfg.hw_cap_req = MTRUE;
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
hw_dev_cap = cfg_11n->param.htcap_cfg.htcap;
/* Get supported MCS sets */
memset(req->pbuf, 0, sizeof(mlan_ds_11n_cfg));
cfg_11n->sub_command = MLAN_OID_11N_CFG_SUPPORTED_MCS_SET;
req->req_id = MLAN_IOCTL_11N_CFG;
req->action = MLAN_ACT_GET;
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
/* Initialize parameters for 2GHz and 5GHz bands */
if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
if (IS_CARD9098(priv->phandle->card_type) ||
IS_CARD9097(priv->phandle->card_type)) {
mcs_supp = priv->phandle->params.antcfg & 0xf;
if (mcs_supp != 3 && mcs_supp != 0)
cfg_11n->param.supported_mcs_set[1] = 0;
cfg_11n->param.supported_mcs_set[4] = 0;
}
woal_cfg80211_setup_ht_cap(&wiphy->bands[IEEE80211_BAND_2GHZ]->
ht_cap, hw_dev_cap,
cfg_11n->param.supported_mcs_set);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
woal_cfg80211_setup_vht_cap(priv,
&wiphy->bands[IEEE80211_BAND_2GHZ]->
vht_cap);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
woal_cfg80211_setup_he_cap(priv,
wiphy->bands[IEEE80211_BAND_2GHZ]);
#endif
}
/* For 2.4G band only card, this shouldn't be set */
if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
if (IS_CARD9098(priv->phandle->card_type) ||
IS_CARD9097(priv->phandle->card_type)) {
mcs_supp = (priv->phandle->params.antcfg & 0xf00) >> 8;
if (mcs_supp != 3 && mcs_supp != 0)
cfg_11n->param.supported_mcs_set[1] = 0;
}
woal_cfg80211_setup_ht_cap(&wiphy->bands[IEEE80211_BAND_5GHZ]->
ht_cap, hw_dev_cap,
cfg_11n->param.supported_mcs_set);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
woal_cfg80211_setup_vht_cap(priv,
&wiphy->bands[IEEE80211_BAND_5GHZ]->
vht_cap);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
woal_cfg80211_setup_he_cap(priv,
wiphy->bands[IEEE80211_BAND_5GHZ]);
#endif
}
kfree(req);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
/* Get antenna modes */
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg));
if (req == NULL) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
radio = (mlan_ds_radio_cfg *)req->pbuf;
radio->sub_command = MLAN_OID_ANT_CFG;
req->req_id = MLAN_IOCTL_RADIO_CFG;
req->action = MLAN_ACT_GET;
ret = woal_request_ioctl(priv, req, wait_option);
if (ret != MLAN_STATUS_SUCCESS)
goto done;
/* Set available antennas to wiphy */
if (IS_CARD9098(priv->phandle->card_type) ||
IS_CARD9097(priv->phandle->card_type)) {
woal_cfg80211_notify_antcfg(priv, wiphy, radio);
}
wiphy->available_antennas_tx = radio->param.ant_cfg.tx_antenna;
wiphy->available_antennas_rx = radio->param.ant_cfg.rx_antenna;
#endif /* CFG80211_VERSION_CODE */
/* Set retry limit count to wiphy */
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
if (MLAN_STATUS_SUCCESS !=
woal_set_get_retry(priv, MLAN_ACT_GET, wait_option,
&retry_count)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
}
#ifdef UAP_SUPPORT
else {
sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
if (!sys_cfg) {
PRINTM(MERROR,
"Fail to alloc memory for mlan_uap_bss_param\n");
ret = MLAN_STATUS_FAILURE;
kfree(sys_cfg);
goto done;
}
if (MLAN_STATUS_SUCCESS !=
woal_set_get_sys_config(priv, MLAN_ACT_GET, wait_option,
sys_cfg)) {
ret = MLAN_STATUS_FAILURE;
kfree(sys_cfg);
goto done;
}
retry_count = sys_cfg->retry_limit;
kfree(sys_cfg);
}
#endif
wiphy->retry_long = (t_u8)retry_count;
wiphy->retry_short = (t_u8)retry_count;
wiphy->max_scan_ie_len = MAX_IE_SIZE;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
wiphy->mgmt_stypes = ieee80211_mgmt_stypes;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION;
#endif /* KERNEL_VERSION */
/* Set RTS threshold to wiphy */
if (MLAN_STATUS_SUCCESS !=
woal_set_get_rts(priv, MLAN_ACT_GET, wait_option, &rts_thr)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (rts_thr < MLAN_RTS_MIN_VALUE || rts_thr > MLAN_RTS_MAX_VALUE)
rts_thr = MLAN_FRAG_RTS_DISABLED;
wiphy->rts_threshold = (t_u32)rts_thr;
/* Set fragment threshold to wiphy */
if (MLAN_STATUS_SUCCESS !=
woal_set_get_frag(priv, MLAN_ACT_GET, wait_option, &frag_thr)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (frag_thr < MLAN_RTS_MIN_VALUE || frag_thr > MLAN_RTS_MAX_VALUE)
frag_thr = MLAN_FRAG_RTS_DISABLED;
wiphy->frag_threshold = (t_u32)frag_thr;
done:
LEAVE();
if (ret != MLAN_STATUS_PENDING)
kfree(req);
return ret;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
/**
* @brief Update channel flag
*
* @param wiphy A pointer to wiphy structure
*
* @return N/A
*/
static void
woal_update_channel_flag(struct wiphy *wiphy, mlan_fw_info *fw_info)
{
enum ieee80211_band band;
struct ieee80211_supported_band *sband;
int i;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
sband = wiphy->bands[band];
if (!sband)
continue;
if (sband->band == IEEE80211_BAND_5GHZ &&
fw_info->prohibit_80mhz) {
for (i = 0; i < sband->n_channels; i++) {
sband->channels[i].flags |=
IEEE80211_CHAN_NO_80MHZ;
PRINTM(MCMND, "hw_value=%d channel flag %x\n",
sband->channels[i].hw_value,
sband->channels[i].flags);
}
}
}
}
#endif
/*
* This function registers the device with CFG802.11 subsystem.
*
* @param priv A pointer to moal_private
*
*/
mlan_status
woal_register_cfg80211(moal_private *priv)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
struct wiphy *wiphy;
void *wdev_priv = NULL;
mlan_fw_info fw_info;
char *country = NULL, *reg_alpha2 = NULL;
int index = 0;
ENTER();
memset(&fw_info, 0, sizeof(mlan_fw_info));
woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
reg_alpha2 = priv->phandle->params.reg_alpha2;
wiphy = wiphy_new(&woal_cfg80211_ops, sizeof(moal_handle *));
if (!wiphy) {
PRINTM(MERROR, "Could not allocate wiphy device\n");
ret = MLAN_STATUS_FAILURE;
goto err_wiphy;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) {
woal_cfg80211_ops.auth = woal_cfg80211_authenticate;
woal_cfg80211_ops.assoc = woal_cfg80211_associate;
woal_cfg80211_ops.disconnect = NULL;
woal_cfg80211_ops.connect = NULL;
#ifdef UAP_SUPPORT
woal_cfg80211_ops.probe_client = woal_cfg80211_probe_client;
#endif
}
#endif
#ifdef CONFIG_PM
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
if (fw_info.fw_supplicant_support)
wiphy->wowlan = &wowlan_support_with_gtk;
else
wiphy->wowlan = &wowlan_support;
#else
wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT;
if (fw_info.fw_supplicant_support) {
wiphy->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
WIPHY_WOWLAN_GTK_REKEY_FAILURE;
}
wiphy->wowlan.n_patterns = MAX_NUM_FILTERS;
wiphy->wowlan.pattern_min_len = 1;
wiphy->wowlan.pattern_max_len = WOWLAN_MAX_PATTERN_LEN;
wiphy->wowlan.max_pkt_offset = WOWLAN_MAX_OFFSET_LEN;
#endif
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
wiphy->coalesce = &coalesce_support;
#endif
wiphy->max_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH;
wiphy->max_scan_ie_len = MAX_IE_SIZE;
wiphy->interface_modes = 0;
wiphy->interface_modes = MBIT(NL80211_IFTYPE_STATION) |
MBIT(NL80211_IFTYPE_AP);
#ifdef WIFI_DIRECT_SUPPORT
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
wiphy->interface_modes |=
MBIT(NL80211_IFTYPE_P2P_GO) | MBIT(NL80211_IFTYPE_P2P_CLIENT);
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
woal_register_cfg80211_vendor_command(wiphy);
#endif
/* Make this wiphy known to this driver only */
wiphy->privid = mrvl_wiphy_privid;
if (!fw_info.fw_bands)
fw_info.fw_bands = BAND_B | BAND_G;
if (fw_info.fw_bands & BAND_A) {
if (priv->phandle->second_mac)
wiphy->bands[IEEE80211_BAND_5GHZ] =
&mac1_cfg80211_band_5ghz;
else
wiphy->bands[IEEE80211_BAND_5GHZ] = &cfg80211_band_5ghz;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
woal_update_channel_flag(wiphy, &fw_info);
#endif
priv->phandle->band = IEEE80211_BAND_5GHZ;
}
/* Supported bands */
if (fw_info.fw_bands & (BAND_B | BAND_G | BAND_GN | BAND_GAC)) {
if (priv->phandle->second_mac)
wiphy->bands[IEEE80211_BAND_2GHZ] =
&mac1_cfg80211_band_2ghz;
else
wiphy->bands[IEEE80211_BAND_2GHZ] = &cfg80211_band_2ghz;
/* If 2.4G enable, it will overwrite default to 2.4G */
priv->phandle->band = IEEE80211_BAND_2GHZ;
}
if (fw_info.fw_bands & BAND_A) {
/** reduce scan time from 110ms to 80ms */
woal_set_scan_time(priv, INIT_ACTIVE_SCAN_CHAN_TIME,
INIT_PASSIVE_SCAN_CHAN_TIME,
INIT_SPECIFIC_SCAN_CHAN_TIME);
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
cfg80211_iface_comb_ap_sta.radar_detect_widths |=
MBIT(NL80211_CHAN_WIDTH_40);
if (fw_info.fw_bands & BAND_AAC)
cfg80211_iface_comb_ap_sta.radar_detect_widths |=
MBIT(NL80211_CHAN_WIDTH_80);
#endif
} else
woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME,
PASSIVE_SCAN_CHAN_TIME,
SPECIFIC_SCAN_CHAN_TIME);
/* Initialize cipher suits */
wiphy->cipher_suites = cfg80211_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(cfg80211_cipher_suites);
#ifdef UAP_CFG80211
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (!moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
#endif
wiphy->max_acl_mac_addrs = MAX_MAC_FILTER_NUM;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
if (fw_info.max_ap_assoc_sta) {
wiphy->max_ap_assoc_sta = fw_info.max_ap_assoc_sta;
PRINTM(MCMND, "Set wiphy max_ap_assoc_sta=%d\n",
wiphy->max_ap_assoc_sta);
}
#endif
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
/* Initialize interface combinations */
wiphy->iface_combinations = &cfg80211_iface_comb_ap_sta;
wiphy->n_iface_combinations = 1;
#endif
moal_memcpy_ext(priv->phandle, wiphy->perm_addr, priv->current_addr,
ETH_ALEN, sizeof(wiphy->perm_addr));
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->flags = 0;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
wiphy->flags |= WIPHY_FLAG_NETNS_OK;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
wiphy->flags |=
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_OFFCHAN_TX;
wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS;
else
#endif
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
#endif
#ifdef ANDROID_KERNEL
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (!moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
#endif
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
if (priv->phandle->params.sched_scan) {
#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
#else
wiphy->max_sched_scan_reqs = 1;
#endif
wiphy->max_sched_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH;
wiphy->max_sched_scan_ie_len = MAX_IE_SIZE;
wiphy->max_match_sets = MRVDRV_MAX_SSID_LIST_LENGTH;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
wiphy->max_sched_scan_plans = 3;
wiphy->max_sched_scan_plan_iterations = 100;
#endif
}
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
wiphy->flags |=
WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RRM);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
wiphy->features |= NL80211_FEATURE_SAE;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN;
#endif
wiphy->reg_notifier = woal_cfg80211_reg_notifier;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
/* Indicate to cfg80211 that the driver can support
* CSA and ESCA,i.e., both types of channel switch
* Applications like hostapd 2.6 will append CSA IE
* and ECSA IE and expect the driver to advertise 2
* in max_num_csa_counters to successfully issue a
* channel switch
*/
wiphy->max_num_csa_counters = MAX_CSA_COUNTERS_NUM;
#endif
wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA);
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
#endif
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
if (fw_info.fw_beacon_prot) {
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION);
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT);
}
#endif
/* Set struct moal_handle pointer in wiphy_priv */
wdev_priv = wiphy_priv(wiphy);
*(unsigned long *)wdev_priv = (unsigned long)priv->phandle;
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
set_wiphy_dev(wiphy, (struct device *)priv->phandle->hotplug_device);
#endif
/* Set phy name */
for (index = 0; index < MAX_MLAN_ADAPTER; index++) {
if (m_handle[index] == priv->phandle) {
dev_set_name(&wiphy->dev, mwiphy_name, index);
break;
}
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
if (moal_extflg_isset(priv->phandle, EXT_BEACON_HINTS)) {
/* REGULATORY_DISABLE_BEACON_HINTS: NO-IR flag won't be removed
* on chn where an AP is visible! */
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS;
}
if (moal_extflg_isset(priv->phandle, EXT_COUNTRY_IE_IGNORE)) {
PRINTM(MIOCTL, "Don't follow countryIE provided by AP.\n");
wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
} else {
PRINTM(MIOCTL, "Follow countryIE provided by AP.\n");
}
#endif
memset(&priv->phandle->country_code, 0,
sizeof(priv->phandle->country_code));
if (reg_alpha2 && !strncmp(reg_alpha2, "99", strlen("99"))) {
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG |
REGULATORY_DISABLE_BEACON_HINTS |
REGULATORY_COUNTRY_IE_IGNORE;
#else
wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
#endif
wiphy_apply_custom_regulatory(wiphy, &mrvl_regdom);
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
if (woal_request_extcap(priv, (t_u8 *)&priv->extended_capabilities,
sizeof(priv->extended_capabilities)) < 0)
PRINTM(MERROR,
"Failed to get driver extended capability, use default\n");
DBG_HEXDUMP(MCMD_D, "wiphy ext cap",
(t_u8 *)&priv->extended_capabilities,
sizeof(priv->extended_capabilities));
wiphy->extended_capabilities = (t_u8 *)&priv->extended_capabilities;
wiphy->extended_capabilities_mask =
(t_u8 *)&priv->extended_capabilities;
wiphy->extended_capabilities_len = sizeof(priv->extended_capabilities);
#endif
woal_cfg80211_init_wiphy(priv, wiphy, MOAL_IOCTL_WAIT);
if (wiphy_register(wiphy) < 0) {
PRINTM(MERROR, "Wiphy device registration failed!\n");
ret = MLAN_STATUS_FAILURE;
goto err_wiphy;
}
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
if (fw_info.force_reg || (priv->phandle->params.txpwrlimit_cfg &&
priv->phandle->params.cntry_txpwr ==
CNTRY_RGPOWER_MODE)) {
PRINTM(MCMND, "FW region_code=%d force_reg=%d\n",
fw_info.region_code, fw_info.force_reg);
country = region_code_2_string(fw_info.region_code);
if (country) {
moal_memcpy_ext(priv->phandle,
priv->phandle->country_code, country, 2,
2);
queue_work(priv->phandle->evt_workqueue,
&priv->phandle->regulatory_work);
}
}
#endif
if ((!reg_alpha2 || strncmp(reg_alpha2, "99", strlen("99")))
) {
/** we will try driver parameter first */
if (reg_alpha2 && woal_is_valid_alpha2(reg_alpha2)) {
PRINTM(MIOCTL, "Notify reg_alpha2 %c%c\n",
reg_alpha2[0], reg_alpha2[1]);
if (!moal_extflg_isset(priv->phandle,
EXT_DISABLE_REGD_BY_DRIVER))
regulatory_hint(wiphy, reg_alpha2);
} else {
country = region_code_2_string(fw_info.region_code);
if (country) {
if (fw_info.region_code != 0) {
PRINTM(MIOCTL,
"Notify hw region code=%d %c%c\n",
fw_info.region_code, country[0],
country[1]);
if (!moal_extflg_isset(priv->phandle,
EXT_DISABLE_REGD_BY_DRIVER))
regulatory_hint(wiphy, country);
}
} else
PRINTM(MCMND,
"hw region code=%d not supported\n",
fw_info.region_code);
}
}
priv->phandle->wiphy = wiphy;
return ret;
err_wiphy:
if (wiphy)
wiphy_free(wiphy);
LEAVE();
return ret;
}