rtl8188eu/ioctl_cfg80211.c
Larry Finger 7ba3df1bcb rtl8188eu: Fix lockup on start
Kernel commit 51d62f2f2c50 ("cfg80211: Save the regulatory domain with a lock")
caused a lock dependency to the cfg80211 version of this driver. It is fixed by
moving the regulatory domain initialization call so that it is outside the lock
in the probe routine.

See https://github.com/lwfinger/rtl8188eu/issues/386 for further discussion.
Signed-off-by: Georg Mueller
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
2022-01-15 11:42:34 -06:00

7113 lines
206 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2007 - 2016 Realtek Corporation. All rights reserved. */
#define _IOCTL_CFG80211_C_
#include <drv_types.h>
#include <hal_data.h>
#ifdef CONFIG_IOCTL_CFG80211
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
#define STATION_INFO_SIGNAL BIT(NL80211_STA_INFO_SIGNAL)
#define STATION_INFO_TX_BITRATE BIT(NL80211_STA_INFO_TX_BITRATE)
#define STATION_INFO_RX_PACKETS BIT(NL80211_STA_INFO_RX_PACKETS)
#define STATION_INFO_TX_PACKETS BIT(NL80211_STA_INFO_TX_PACKETS)
#define STATION_INFO_TX_FAILED BIT(NL80211_STA_INFO_TX_FAILED)
#define STATION_INFO_ASSOC_REQ_IES 0
#endif /* Linux kernel >= 4.0.0 */
#include <rtw_wifi_regd.h>
#define RTW_MAX_MGMT_TX_CNT (8)
#define RTW_MAX_MGMT_TX_MS_GAS (500)
#define RTW_SCAN_IE_LEN_MAX 2304
#define RTW_MAX_REMAIN_ON_CHANNEL_DURATION 5000 /* ms */
#define RTW_MAX_NUM_PMKIDS 4
#define RTW_CH_MAX_2G_CHANNEL 14 /* Max channel in 2G band */
#ifdef CONFIG_WAPI_SUPPORT
#ifndef WLAN_CIPHER_SUITE_SMS4
#define WLAN_CIPHER_SUITE_SMS4 0x00147201
#endif
#ifndef WLAN_AKM_SUITE_WAPI_PSK
#define WLAN_AKM_SUITE_WAPI_PSK 0x000FAC04
#endif
#ifndef WLAN_AKM_SUITE_WAPI_CERT
#define WLAN_AKM_SUITE_WAPI_CERT 0x000FAC12
#endif
#ifndef NL80211_WAPI_VERSION_1
#define NL80211_WAPI_VERSION_1 (1 << 2)
#endif
#endif /* CONFIG_WAPI_SUPPORT */
#ifdef CONFIG_RTW_80211R
#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03
#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04
#endif
static const u32 rtw_cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
WLAN_CIPHER_SUITE_TKIP,
WLAN_CIPHER_SUITE_CCMP,
#ifdef CONFIG_WAPI_SUPPORT
WLAN_CIPHER_SUITE_SMS4,
#endif /* CONFIG_WAPI_SUPPORT */
#ifdef CONFIG_IEEE80211W
WLAN_CIPHER_SUITE_AES_CMAC,
#endif /* CONFIG_IEEE80211W */
};
#define RATETAB_ENT(_rate, _rateid, _flags) \
{ \
.bitrate = (_rate), \
.hw_value = (_rateid), \
.flags = (_flags), \
}
#define CHAN2G(_channel, _freq, _flags) { \
.band = NL80211_BAND_2GHZ, \
.center_freq = (_freq), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
#define CHAN5G(_channel, _flags) { \
.band = NL80211_BAND_5GHZ, \
.center_freq = 5000 + (5 * (_channel)), \
.hw_value = (_channel), \
.flags = (_flags), \
.max_antenna_gain = 0, \
.max_power = 30, \
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
/* if wowlan is not supported, kernel generate a disconnect at each suspend
* cf: /net/wireless/sysfs.c, so register a stub wowlan.
* Moreover wowlan has to be enabled via a the nl80211_set_wowlan callback.
* (from user space, e.g. iw phy0 wowlan enable)
*/
static const struct wiphy_wowlan_support wowlan_stub = {
.flags = WIPHY_WOWLAN_ANY,
.n_patterns = 0,
.pattern_max_len = 0,
.pattern_min_len = 0,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
.max_pkt_offset = 0,
#endif
};
#endif
static struct ieee80211_rate rtw_rates[] = {
RATETAB_ENT(10, 0x1, 0),
RATETAB_ENT(20, 0x2, 0),
RATETAB_ENT(55, 0x4, 0),
RATETAB_ENT(110, 0x8, 0),
RATETAB_ENT(60, 0x10, 0),
RATETAB_ENT(90, 0x20, 0),
RATETAB_ENT(120, 0x40, 0),
RATETAB_ENT(180, 0x80, 0),
RATETAB_ENT(240, 0x100, 0),
RATETAB_ENT(360, 0x200, 0),
RATETAB_ENT(480, 0x400, 0),
RATETAB_ENT(540, 0x800, 0),
};
#define rtw_a_rates (rtw_rates + 4)
#define RTW_A_RATES_NUM 8
#define rtw_g_rates (rtw_rates + 0)
#define RTW_G_RATES_NUM 12
#define RTW_2G_CHANNELS_NUM 14
#define RTW_5G_CHANNELS_NUM 37
static struct ieee80211_channel rtw_2ghz_channels[] = {
CHAN2G(1, 2412, 0),
CHAN2G(2, 2417, 0),
CHAN2G(3, 2422, 0),
CHAN2G(4, 2427, 0),
CHAN2G(5, 2432, 0),
CHAN2G(6, 2437, 0),
CHAN2G(7, 2442, 0),
CHAN2G(8, 2447, 0),
CHAN2G(9, 2452, 0),
CHAN2G(10, 2457, 0),
CHAN2G(11, 2462, 0),
CHAN2G(12, 2467, 0),
CHAN2G(13, 2472, 0),
CHAN2G(14, 2484, 0),
};
static struct ieee80211_channel rtw_5ghz_a_channels[] = {
CHAN5G(34, 0), CHAN5G(36, 0),
CHAN5G(38, 0), CHAN5G(40, 0),
CHAN5G(42, 0), CHAN5G(44, 0),
CHAN5G(46, 0), CHAN5G(48, 0),
CHAN5G(52, 0), CHAN5G(56, 0),
CHAN5G(60, 0), CHAN5G(64, 0),
CHAN5G(100, 0), CHAN5G(104, 0),
CHAN5G(108, 0), CHAN5G(112, 0),
CHAN5G(116, 0), CHAN5G(120, 0),
CHAN5G(124, 0), CHAN5G(128, 0),
CHAN5G(132, 0), CHAN5G(136, 0),
CHAN5G(140, 0), CHAN5G(149, 0),
CHAN5G(153, 0), CHAN5G(157, 0),
CHAN5G(161, 0), CHAN5G(165, 0),
CHAN5G(184, 0), CHAN5G(188, 0),
CHAN5G(192, 0), CHAN5G(196, 0),
CHAN5G(200, 0), CHAN5G(204, 0),
CHAN5G(208, 0), CHAN5G(212, 0),
CHAN5G(216, 0),
};
static void rtw_2g_channels_init(struct ieee80211_channel *channels)
{
memcpy((void *)channels, (void *)rtw_2ghz_channels,
sizeof(struct ieee80211_channel) * RTW_2G_CHANNELS_NUM
);
}
static void rtw_5g_channels_init(struct ieee80211_channel *channels)
{
memcpy((void *)channels, (void *)rtw_5ghz_a_channels,
sizeof(struct ieee80211_channel) * RTW_5G_CHANNELS_NUM
);
}
static void rtw_2g_rates_init(struct ieee80211_rate *rates)
{
memcpy(rates, rtw_g_rates,
sizeof(struct ieee80211_rate) * RTW_G_RATES_NUM
);
}
static void rtw_5g_rates_init(struct ieee80211_rate *rates)
{
memcpy(rates, rtw_a_rates,
sizeof(struct ieee80211_rate) * RTW_A_RATES_NUM
);
}
static struct ieee80211_supported_band *rtw_spt_band_alloc(
enum nl80211_band band
)
{
struct ieee80211_supported_band *spt_band = NULL;
int n_channels, n_bitrates;
if (band == NL80211_BAND_2GHZ) {
n_channels = RTW_2G_CHANNELS_NUM;
n_bitrates = RTW_G_RATES_NUM;
} else if (band == NL80211_BAND_5GHZ) {
n_channels = RTW_5G_CHANNELS_NUM;
n_bitrates = RTW_A_RATES_NUM;
} else
goto exit;
spt_band = (struct ieee80211_supported_band *)rtw_zmalloc(
sizeof(struct ieee80211_supported_band)
+ sizeof(struct ieee80211_channel) * n_channels
+ sizeof(struct ieee80211_rate) * n_bitrates
);
if (!spt_band)
goto exit;
spt_band->channels = (struct ieee80211_channel *)(((u8 *)spt_band) + sizeof(struct ieee80211_supported_band));
spt_band->bitrates = (struct ieee80211_rate *)(((u8 *)spt_band->channels) + sizeof(struct ieee80211_channel) * n_channels);
spt_band->band = band;
spt_band->n_channels = n_channels;
spt_band->n_bitrates = n_bitrates;
if (band == NL80211_BAND_2GHZ) {
rtw_2g_channels_init(spt_band->channels);
rtw_2g_rates_init(spt_band->bitrates);
} else if (band == NL80211_BAND_5GHZ) {
rtw_5g_channels_init(spt_band->channels);
rtw_5g_rates_init(spt_band->bitrates);
}
/* spt_band.ht_cap */
exit:
return spt_band;
}
static void rtw_spt_band_free(struct ieee80211_supported_band *spt_band)
{
u32 size = 0;
if (!spt_band)
return;
#if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE)
if (spt_band->band == NL80211_BAND_2GHZ) {
#else
if (spt_band->band == IEEE80211_BAND_2GHZ) {
#endif
size = sizeof(struct ieee80211_supported_band)
+ sizeof(struct ieee80211_channel) * RTW_2G_CHANNELS_NUM
+ sizeof(struct ieee80211_rate) * RTW_G_RATES_NUM;
#if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE)
} else if (spt_band->band == NL80211_BAND_5GHZ) {
#else
} else if (spt_band->band == IEEE80211_BAND_5GHZ) {
#endif
size = sizeof(struct ieee80211_supported_band)
+ sizeof(struct ieee80211_channel) * RTW_5G_CHANNELS_NUM
+ sizeof(struct ieee80211_rate) * RTW_A_RATES_NUM;
} else {
}
rtw_mfree((u8 *)spt_band, size);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
static const struct ieee80211_txrx_stypes
rtw_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_ADHOC] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4)
},
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_AP] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4)
},
[NL80211_IFTYPE_AP_VLAN] = {
/* copy AP */
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4)
},
[NL80211_IFTYPE_P2P_CLIENT] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
[NL80211_IFTYPE_P2P_GO] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
BIT(IEEE80211_STYPE_DISASSOC >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
BIT(IEEE80211_STYPE_DEAUTH >> 4) |
BIT(IEEE80211_STYPE_ACTION >> 4)
},
#if defined(RTW_DEDICATED_P2P_DEVICE)
[NL80211_IFTYPE_P2P_DEVICE] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
},
#endif
};
#endif
static u64 rtw_get_systime_us(void)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0))
u64 ts;
ts = ktime_get_boottime();
return do_div(ts, 1000);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
struct timespec ts;
get_monotonic_boottime(&ts);
return ((u64)ts.tv_sec * 1000000) + ts.tv_nsec / 1000;
#else
struct timeval tv;
do_gettimeofday(&tv);
return ((u64)tv.tv_sec * 1000000) + tv.tv_usec;
#endif
}
/* Try to remove non target BSS's SR to reduce PBC overlap rate */
static int rtw_cfg80211_clear_wps_sr_of_non_target_bss(_adapter *padapter, struct wlan_network *pnetwork, struct cfg80211_ssid *req_ssid)
{
struct rtw_wdev_priv *wdev_data = adapter_wdev_data(padapter);
int ret = 0;
u8 *psr = NULL, sr = 0;
NDIS_802_11_SSID *pssid = &pnetwork->network.Ssid;
u32 wpsielen = 0;
u8 *wpsie = NULL;
if (pssid->SsidLength == req_ssid->ssid_len
&& !memcmp(pssid->Ssid, req_ssid->ssid, req_ssid->ssid_len) )
goto exit;
wpsie = rtw_get_wps_ie(pnetwork->network.IEs + _FIXED_IE_LENGTH_
, pnetwork->network.IELength - _FIXED_IE_LENGTH_, NULL, &wpsielen);
if (wpsie && wpsielen > 0)
psr = rtw_get_wps_attr_content(wpsie, wpsielen, WPS_ATTR_SELECTED_REGISTRAR, &sr, NULL);
if (psr && sr) {
if (0)
RTW_INFO("clear sr of non target bss:%s("MAC_FMT")\n"
, pssid->Ssid, MAC_ARG(pnetwork->network.MacAddress));
*psr = 0; /* clear sr */
ret = 1;
}
exit:
return ret;
}
#define MAX_BSSINFO_LEN 1000
struct cfg80211_bss *rtw_cfg80211_inform_bss(_adapter *padapter, struct wlan_network *pnetwork)
{
struct ieee80211_channel *notify_channel;
struct cfg80211_bss *bss = NULL;
/* struct ieee80211_supported_band *band; */
u16 channel;
u32 freq;
u64 notify_timestamp;
u16 notify_capability;
u16 notify_interval;
u8 *notify_ie;
size_t notify_ielen;
s32 notify_signal;
/* u8 buf[MAX_BSSINFO_LEN]; */
u8 *pbuf;
size_t buf_size = MAX_BSSINFO_LEN;
size_t len, bssinf_len = 0;
struct rtw_ieee80211_hdr *pwlanhdr;
__le16 *fctrl;
u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
struct wireless_dev *wdev = padapter->rtw_wdev;
struct wiphy *wiphy = wdev->wiphy;
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
pbuf = rtw_zmalloc(buf_size);
if (pbuf == NULL) {
RTW_INFO("%s pbuf allocate failed !!\n", __func__);
return bss;
}
/* RTW_INFO("%s\n", __func__); */
bssinf_len = pnetwork->network.IELength + sizeof(struct rtw_ieee80211_hdr_3addr);
if (bssinf_len > buf_size) {
RTW_INFO("%s IE Length too long > %zu byte\n", __func__, buf_size);
goto exit;
}
#ifndef CONFIG_WAPI_SUPPORT
{
u16 wapi_len = 0;
if (rtw_get_wapi_ie(pnetwork->network.IEs, pnetwork->network.IELength, NULL, &wapi_len) > 0) {
if (wapi_len > 0) {
RTW_INFO("%s, no support wapi!\n", __func__);
goto exit;
}
}
}
#endif /* !CONFIG_WAPI_SUPPORT */
channel = pnetwork->network.Configuration.DSConfig;
freq = rtw_ch2freq(channel);
notify_channel = ieee80211_get_channel(wiphy, freq);
notify_timestamp = rtw_get_systime_us();
notify_interval = le16_to_cpu(*(__le16 *)rtw_get_beacon_interval_from_ie(pnetwork->network.IEs));
notify_capability = le16_to_cpu(*(__le16 *)rtw_get_capability_from_ie(pnetwork->network.IEs));
notify_ie = pnetwork->network.IEs + _FIXED_IE_LENGTH_;
notify_ielen = pnetwork->network.IELength - _FIXED_IE_LENGTH_;
/* We've set wiphy's signal_type as CFG80211_SIGNAL_TYPE_MBM: signal strength in mBm (100*dBm) */
if (check_fwstate(pmlmepriv, _FW_LINKED) &&
is_same_network(&pmlmepriv->cur_network.network, &pnetwork->network, 0)) {
notify_signal = 100 * translate_percentage_to_dbm(padapter->recvpriv.signal_strength); /* dbm */
} else {
notify_signal = 100 * translate_percentage_to_dbm(pnetwork->network.PhyInfo.SignalStrength); /* dbm */
}
/* pbuf = buf; */
pwlanhdr = (struct rtw_ieee80211_hdr *)pbuf;
fctrl = &(pwlanhdr->frame_ctl);
*(fctrl) = 0;
SetSeqNum(pwlanhdr, 0/*pmlmeext->mgnt_seq*/);
/* pmlmeext->mgnt_seq++; */
if (pnetwork->network.Reserved[0] == 1) { /* WIFI_BEACON */
memcpy(pwlanhdr->addr1, bc_addr, ETH_ALEN);
set_frame_sub_type(pbuf, WIFI_BEACON);
} else {
memcpy(pwlanhdr->addr1, adapter_mac_addr(padapter), ETH_ALEN);
set_frame_sub_type(pbuf, WIFI_PROBERSP);
}
memcpy(pwlanhdr->addr2, pnetwork->network.MacAddress, ETH_ALEN);
memcpy(pwlanhdr->addr3, pnetwork->network.MacAddress, ETH_ALEN);
/* pbuf += sizeof(struct rtw_ieee80211_hdr_3addr); */
len = sizeof(struct rtw_ieee80211_hdr_3addr);
memcpy((pbuf + len), pnetwork->network.IEs, pnetwork->network.IELength);
*((__le64 *)(pbuf + len)) = cpu_to_le64(notify_timestamp);
len += pnetwork->network.IELength;
#if defined(CONFIG_P2P) && 0
if(rtw_get_p2p_ie(pnetwork->network.IEs+12, pnetwork->network.IELength-12, NULL, NULL))
RTW_INFO("%s, got p2p_ie\n", __func__);
#endif
bss = cfg80211_inform_bss_frame(wiphy, notify_channel, (struct ieee80211_mgmt *)pbuf,
len, notify_signal, GFP_ATOMIC);
if (unlikely(!bss)) {
RTW_INFO(FUNC_ADPT_FMT" bss NULL\n", FUNC_ADPT_ARG(padapter));
goto exit;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38))
#ifndef COMPAT_KERNEL_RELEASE
/* patch for cfg80211, update beacon ies to information_elements */
if (pnetwork->network.Reserved[0] == 1) { /* WIFI_BEACON */
if (bss->len_information_elements != bss->len_beacon_ies) {
bss->information_elements = bss->beacon_ies;
bss->len_information_elements = bss->len_beacon_ies;
}
}
#endif /* COMPAT_KERNEL_RELEASE */
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(wiphy, bss);
#else
cfg80211_put_bss(bss);
#endif
exit:
if (pbuf)
rtw_mfree(pbuf, buf_size);
return bss;
}
/*
Check the given bss is valid by kernel API cfg80211_get_bss()
@padapter : the given adapter
return true if bss is valid, false for not found.
*/
int rtw_cfg80211_check_bss(_adapter *padapter)
{
WLAN_BSSID_EX *pnetwork = &(padapter->mlmeextpriv.mlmext_info.network);
struct cfg80211_bss *bss = NULL;
struct ieee80211_channel *notify_channel = NULL;
u32 freq;
if (!(pnetwork) || !(padapter->rtw_wdev))
return false;
freq = rtw_ch2freq(pnetwork->Configuration.DSConfig);
notify_channel = ieee80211_get_channel(padapter->rtw_wdev->wiphy, freq);
bss = cfg80211_get_bss(padapter->rtw_wdev->wiphy, notify_channel,
pnetwork->MacAddress, pnetwork->Ssid.Ssid,
pnetwork->Ssid.SsidLength,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
pnetwork->InfrastructureMode == Ndis802_11Infrastructure?IEEE80211_BSS_TYPE_ESS:IEEE80211_BSS_TYPE_IBSS,
IEEE80211_PRIVACY(pnetwork->Privacy));
#else
pnetwork->InfrastructureMode == Ndis802_11Infrastructure?WLAN_CAPABILITY_ESS:WLAN_CAPABILITY_IBSS, pnetwork->InfrastructureMode == Ndis802_11Infrastructure?WLAN_CAPABILITY_ESS:WLAN_CAPABILITY_IBSS);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(padapter->rtw_wdev->wiphy, bss);
#else
cfg80211_put_bss(bss);
#endif
return bss != NULL;
}
void rtw_cfg80211_ibss_indicate_connect(_adapter *padapter)
{
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct wlan_network *cur_network = &(pmlmepriv->cur_network);
struct wireless_dev *pwdev = padapter->rtw_wdev;
struct cfg80211_bss *bss = NULL;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
struct wiphy *wiphy = pwdev->wiphy;
int freq = 2412;
struct ieee80211_channel *notify_channel;
#endif
RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
freq = rtw_ch2freq(cur_network->network.Configuration.DSConfig);
if (0)
RTW_INFO("chan: %d, freq: %d\n", cur_network->network.Configuration.DSConfig, freq);
#endif
if (pwdev->iftype != NL80211_IFTYPE_ADHOC)
return;
if (!rtw_cfg80211_check_bss(padapter)) {
WLAN_BSSID_EX *pnetwork = &(padapter->mlmeextpriv.mlmext_info.network);
struct wlan_network *scanned = pmlmepriv->cur_network_scanned;
if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) ) {
memcpy(&cur_network->network, pnetwork, sizeof(WLAN_BSSID_EX));
if (cur_network) {
if (!rtw_cfg80211_inform_bss(padapter, cur_network))
RTW_INFO(FUNC_ADPT_FMT" inform fail !!\n", FUNC_ADPT_ARG(padapter));
else
RTW_INFO(FUNC_ADPT_FMT" inform success !!\n", FUNC_ADPT_ARG(padapter));
} else {
RTW_INFO("cur_network is not exist!!!\n");
return ;
}
} else {
if (scanned == NULL)
rtw_warn_on(1);
if (!memcmp(&(scanned->network.Ssid), &(pnetwork->Ssid), sizeof(NDIS_802_11_SSID))
&& !memcmp(scanned->network.MacAddress, pnetwork->MacAddress, sizeof(NDIS_802_11_MAC_ADDRESS))
) {
if (!rtw_cfg80211_inform_bss(padapter, scanned))
RTW_INFO(FUNC_ADPT_FMT" inform fail !!\n", FUNC_ADPT_ARG(padapter));
else {
/* RTW_INFO(FUNC_ADPT_FMT" inform success !!\n", FUNC_ADPT_ARG(padapter)); */
}
} else {
RTW_INFO("scanned & pnetwork compare fail\n");
rtw_warn_on(1);
}
}
if (!rtw_cfg80211_check_bss(padapter))
RTW_INFO(FUNC_ADPT_FMT" BSS not found !!\n", FUNC_ADPT_ARG(padapter));
}
/* notify cfg80211 that device joined an IBSS */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
notify_channel = ieee80211_get_channel(wiphy, freq);
cfg80211_ibss_joined(padapter->pnetdev, cur_network->network.MacAddress, notify_channel, GFP_ATOMIC);
#else
cfg80211_ibss_joined(padapter->pnetdev, cur_network->network.MacAddress, GFP_ATOMIC);
#endif
}
void rtw_cfg80211_indicate_connect(_adapter *padapter)
{
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct wlan_network *cur_network = &(pmlmepriv->cur_network);
struct wireless_dev *pwdev = padapter->rtw_wdev;
#ifdef CONFIG_P2P
struct wifidirect_info *pwdinfo = &(padapter->wdinfo);
#endif
struct cfg80211_bss *bss = NULL;
RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter));
if (pwdev->iftype != NL80211_IFTYPE_STATION
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
&& pwdev->iftype != NL80211_IFTYPE_P2P_CLIENT
#endif
)
return;
if (check_fwstate(pmlmepriv, WIFI_AP_STATE) )
return;
#ifdef CONFIG_P2P
if (pwdinfo->driver_interface == DRIVER_CFG80211) {
#if !RTW_P2P_GROUP_INTERFACE
if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) {
rtw_p2p_set_pre_state(pwdinfo, rtw_p2p_state(pwdinfo));
rtw_p2p_set_role(pwdinfo, P2P_ROLE_CLIENT);
rtw_p2p_set_state(pwdinfo, P2P_STATE_GONEGO_OK);
RTW_INFO("%s, role=%d, p2p_state=%d, pre_p2p_state=%d\n", __func__, rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo), rtw_p2p_pre_state(pwdinfo));
}
#endif
}
#endif /* CONFIG_P2P */
if (check_fwstate(pmlmepriv, WIFI_MONITOR_STATE) != true) {
WLAN_BSSID_EX *pnetwork = &(padapter->mlmeextpriv.mlmext_info.network);
struct wlan_network *scanned = pmlmepriv->cur_network_scanned;
/* RTW_INFO(FUNC_ADPT_FMT" BSS not found\n", FUNC_ADPT_ARG(padapter)); */
if (scanned == NULL) {
rtw_warn_on(1);
goto check_bss;
}
if (!memcmp(scanned->network.MacAddress, pnetwork->MacAddress, sizeof(NDIS_802_11_MAC_ADDRESS))
&& !memcmp(&(scanned->network.Ssid), &(pnetwork->Ssid), sizeof(NDIS_802_11_SSID))
) {
if (!rtw_cfg80211_inform_bss(padapter, scanned))
RTW_INFO(FUNC_ADPT_FMT" inform fail !!\n", FUNC_ADPT_ARG(padapter));
else {
/* RTW_INFO(FUNC_ADPT_FMT" inform success !!\n", FUNC_ADPT_ARG(padapter)); */
}
} else {
RTW_INFO("scanned: %s("MAC_FMT"), cur: %s("MAC_FMT")\n",
scanned->network.Ssid.Ssid, MAC_ARG(scanned->network.MacAddress),
pnetwork->Ssid.Ssid, MAC_ARG(pnetwork->MacAddress)
);
rtw_warn_on(1);
}
}
check_bss:
if (!rtw_cfg80211_check_bss(padapter))
RTW_INFO(FUNC_ADPT_FMT" BSS not found !!\n", FUNC_ADPT_ARG(padapter));
if (rtw_to_roam(padapter) > 0) {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) || defined(COMPAT_KERNEL_RELEASE)
struct wiphy *wiphy = pwdev->wiphy;
struct ieee80211_channel *notify_channel;
u32 freq;
u16 channel = cur_network->network.Configuration.DSConfig;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
struct cfg80211_roam_info roam_info = {};
#endif
freq = rtw_ch2freq(channel);
notify_channel = ieee80211_get_channel(wiphy, freq);
#endif
RTW_INFO(FUNC_ADPT_FMT" call cfg80211_roamed\n", FUNC_ADPT_ARG(padapter));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
roam_info.channel = notify_channel;
roam_info.bssid = cur_network->network.MacAddress;
roam_info.req_ie =
pmlmepriv->assoc_req+sizeof(struct rtw_ieee80211_hdr_3addr)+2;
roam_info.req_ie_len =
pmlmepriv->assoc_req_len-sizeof(struct rtw_ieee80211_hdr_3addr)-2;
roam_info.resp_ie =
pmlmepriv->assoc_rsp+sizeof(struct rtw_ieee80211_hdr_3addr)+6;
roam_info.resp_ie_len =
pmlmepriv->assoc_rsp_len-sizeof(struct rtw_ieee80211_hdr_3addr)-6;
cfg80211_roamed(padapter->pnetdev, &roam_info, GFP_ATOMIC);
#else
cfg80211_roamed(padapter->pnetdev
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) || defined(COMPAT_KERNEL_RELEASE)
, notify_channel
#endif
, cur_network->network.MacAddress
, pmlmepriv->assoc_req + sizeof(struct rtw_ieee80211_hdr_3addr) + 2
, pmlmepriv->assoc_req_len - sizeof(struct rtw_ieee80211_hdr_3addr) - 2
, pmlmepriv->assoc_rsp + sizeof(struct rtw_ieee80211_hdr_3addr) + 6
, pmlmepriv->assoc_rsp_len - sizeof(struct rtw_ieee80211_hdr_3addr) - 6
, GFP_ATOMIC);
#endif
#ifdef CONFIG_RTW_80211R
if ((rtw_to_roam(padapter) > 0) && rtw_chk_ft_flags(padapter, RTW_FT_SUPPORTED))
rtw_set_ft_status(padapter, RTW_FT_ASSOCIATED_STA);
#endif
} else {
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) || defined(COMPAT_KERNEL_RELEASE)
RTW_INFO("pwdev->sme_state(b)=%d\n", pwdev->sme_state);
#endif
cfg80211_connect_result(padapter->pnetdev, cur_network->network.MacAddress
, pmlmepriv->assoc_req + sizeof(struct rtw_ieee80211_hdr_3addr) + 2
, pmlmepriv->assoc_req_len - sizeof(struct rtw_ieee80211_hdr_3addr) - 2
, pmlmepriv->assoc_rsp + sizeof(struct rtw_ieee80211_hdr_3addr) + 6
, pmlmepriv->assoc_rsp_len - sizeof(struct rtw_ieee80211_hdr_3addr) - 6
, WLAN_STATUS_SUCCESS, GFP_ATOMIC);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) || defined(COMPAT_KERNEL_RELEASE)
RTW_INFO("pwdev->sme_state(a)=%d\n", pwdev->sme_state);
#endif
}
}
void rtw_cfg80211_indicate_disconnect(_adapter *padapter, u16 reason, u8 locally_generated)
{
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct wireless_dev *pwdev = padapter->rtw_wdev;
#ifdef CONFIG_P2P
struct wifidirect_info *pwdinfo = &(padapter->wdinfo);
#endif
RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter));
/*always replace privated definitions with wifi reserved value 0*/
if ((reason == WLAN_REASON_ACTIVE_ROAM) || (reason == WLAN_REASON_JOIN_WRONG_CHANNEL) || (reason == WLAN_REASON_EXPIRATION_CHK))
reason = 0;
if (pwdev->iftype != NL80211_IFTYPE_STATION
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
&& pwdev->iftype != NL80211_IFTYPE_P2P_CLIENT
#endif
)
return;
if (check_fwstate(pmlmepriv, WIFI_AP_STATE) )
return;
#ifdef CONFIG_P2P
if (pwdinfo->driver_interface == DRIVER_CFG80211) {
if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) {
rtw_p2p_set_state(pwdinfo, rtw_p2p_pre_state(pwdinfo));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
if (pwdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
#endif
rtw_p2p_set_role(pwdinfo, P2P_ROLE_DEVICE);
RTW_INFO("%s, role=%d, p2p_state=%d, pre_p2p_state=%d\n", __func__, rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo), rtw_p2p_pre_state(pwdinfo));
}
}
#endif /* CONFIG_P2P */
#ifdef SUPPLICANT_RTK_VERSION_LOWER_THAN_JB42
if (!padapter->mlmepriv.not_indic_disco || padapter->ndev_unregistering) {
#else
{
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) || defined(COMPAT_KERNEL_RELEASE)
RTW_INFO("pwdev->sme_state(b)=%d\n", pwdev->sme_state);
if (pwdev->sme_state == CFG80211_SME_CONNECTING)
cfg80211_connect_result(padapter->pnetdev, NULL, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_ATOMIC/*GFP_KERNEL*/);
else if (pwdev->sme_state == CFG80211_SME_CONNECTED) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
cfg80211_disconnected(padapter->pnetdev, reason, NULL, 0, locally_generated, GFP_ATOMIC);
#else
cfg80211_disconnected(padapter->pnetdev, 0, NULL, 0, GFP_ATOMIC);
#endif
}
RTW_INFO("pwdev->sme_state(a)=%d\n", pwdev->sme_state);
#else
if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
RTW_INFO(FUNC_ADPT_FMT" call cfg80211_disconnected\n", FUNC_ADPT_ARG(padapter));
cfg80211_disconnected(padapter->pnetdev, reason, NULL, 0, locally_generated, GFP_ATOMIC);
#else
RTW_INFO(FUNC_ADPT_FMT" call cfg80211_disconnected\n", FUNC_ADPT_ARG(padapter));
cfg80211_disconnected(padapter->pnetdev, 0, NULL, 0, GFP_ATOMIC);
#endif
} else {
RTW_INFO(FUNC_ADPT_FMT" call cfg80211_connect_result\n", FUNC_ADPT_ARG(padapter));
cfg80211_connect_result(padapter->pnetdev, NULL, NULL, 0, NULL, 0,
WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_ATOMIC);
}
#endif
}
}
#ifdef CONFIG_AP_MODE
static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_param *param, u32 param_len)
{
int ret = 0;
u32 wep_key_idx, wep_key_len, wep_total_len;
struct sta_info *psta = NULL, *pbcmc_sta = NULL;
_adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct security_priv *psecuritypriv = &(padapter->securitypriv);
struct sta_priv *pstapriv = &padapter->stapriv;
RTW_INFO("%s\n", __func__);
param->u.crypt.err = 0;
param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
/* sizeof(struct ieee_param) = 64 bytes; */
/* if (param_len != (u32) ((u8 *) param->u.crypt.key - (u8 *) param) + param->u.crypt.key_len) */
if (param_len != sizeof(struct ieee_param) + param->u.crypt.key_len) {
ret = -EINVAL;
goto exit;
}
if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
if (param->u.crypt.idx >= WEP_KEYS
#ifdef CONFIG_IEEE80211W
&& param->u.crypt.idx > BIP_MAX_KEYID
#endif /* CONFIG_IEEE80211W */
) {
ret = -EINVAL;
goto exit;
}
} else {
psta = rtw_get_stainfo(pstapriv, param->sta_addr);
if (!psta) {
/* ret = -EINVAL; */
RTW_INFO("rtw_set_encryption(), sta has already been removed or never been added\n");
goto exit;
}
}
if (strcmp(param->u.crypt.alg, "none") == 0 && (psta == NULL)) {
/* todo:clear default encryption keys */
RTW_INFO("clear default encryption keys, keyid=%d\n", param->u.crypt.idx);
goto exit;
}
if (strcmp(param->u.crypt.alg, "WEP") == 0 && (psta == NULL)) {
RTW_INFO("r871x_set_encryption, crypt.alg = WEP\n");
wep_key_idx = param->u.crypt.idx;
wep_key_len = param->u.crypt.key_len;
RTW_INFO("r871x_set_encryption, wep_key_idx=%d, len=%d\n", wep_key_idx, wep_key_len);
if ((wep_key_idx >= WEP_KEYS) || (wep_key_len <= 0)) {
ret = -EINVAL;
goto exit;
}
if (wep_key_len > 0)
wep_key_len = wep_key_len <= 5 ? 5 : 13;
if (psecuritypriv->bWepDefaultKeyIdxSet == 0) {
/* wep default key has not been set, so use this key index as default key. */
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Auto;
psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled;
psecuritypriv->dot11PrivacyAlgrthm = _WEP40_;
psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
if (wep_key_len == 13) {
psecuritypriv->dot11PrivacyAlgrthm = _WEP104_;
psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
}
psecuritypriv->dot11PrivacyKeyIndex = wep_key_idx;
}
memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), param->u.crypt.key, wep_key_len);
psecuritypriv->dot11DefKeylen[wep_key_idx] = wep_key_len;
rtw_ap_set_wep_key(padapter, param->u.crypt.key, wep_key_len, wep_key_idx, 1);
goto exit;
}
if (!psta && check_fwstate(pmlmepriv, WIFI_AP_STATE)) { /* group key */
if (param->u.crypt.set_tx == 0) { /* group key */
if (strcmp(param->u.crypt.alg, "WEP") == 0) {
RTW_INFO("%s, set group_key, WEP\n", __func__);
memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
if (param->u.crypt.key_len == 13)
psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
} else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
RTW_INFO("%s, set group_key, TKIP\n", __func__);
psecuritypriv->dot118021XGrpPrivacy = _TKIP_;
memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
/* DEBUG_ERR("set key length :param->u.crypt.key_len=%d\n", param->u.crypt.key_len); */
/* set mic key */
memcpy(psecuritypriv->dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8);
memcpy(psecuritypriv->dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8);
psecuritypriv->busetkipkey = true;
} else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
RTW_INFO("%s, set group_key, CCMP\n", __func__);
psecuritypriv->dot118021XGrpPrivacy = _AES_;
memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
}
#ifdef CONFIG_IEEE80211W
else if (strcmp(param->u.crypt.alg, "BIP") == 0) {
int no;
RTW_INFO("BIP key_len=%d , index=%d\n", param->u.crypt.key_len, param->u.crypt.idx);
/* save the IGTK key, length 16 bytes */
memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
/* RTW_INFO("IGTK key below:\n");
for(no=0;no<16;no++)
printk(" %02x ", padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey[no]);
RTW_INFO("\n"); */
padapter->securitypriv.dot11wBIPKeyid = param->u.crypt.idx;
padapter->securitypriv.binstallBIPkey = true;
RTW_INFO(" ~~~~set sta key:IGKT\n");
goto exit;
}
#endif /* CONFIG_IEEE80211W */
else {
RTW_INFO("%s, set group_key, none\n", __func__);
psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_;
}
psecuritypriv->dot118021XGrpKeyid = param->u.crypt.idx;
psecuritypriv->binstallGrpkey = true;
psecuritypriv->dot11PrivacyAlgrthm = psecuritypriv->dot118021XGrpPrivacy;/* !!! */
rtw_ap_set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx);
pbcmc_sta = rtw_get_bcmc_stainfo(padapter);
if (pbcmc_sta) {
pbcmc_sta->ieee8021x_blocked = false;
pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy; /* rx will use bmc_sta's dot118021XPrivacy */
}
}
goto exit;
}
if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X && psta) { /* psk/802_1x */
if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
if (param->u.crypt.set_tx == 1) { /* pairwise key */
memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
if (strcmp(param->u.crypt.alg, "WEP") == 0) {
RTW_INFO("%s, set pairwise key, WEP\n", __func__);
psta->dot118021XPrivacy = _WEP40_;
if (param->u.crypt.key_len == 13)
psta->dot118021XPrivacy = _WEP104_;
} else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
RTW_INFO("%s, set pairwise key, TKIP\n", __func__);
psta->dot118021XPrivacy = _TKIP_;
/* DEBUG_ERR("set key length :param->u.crypt.key_len=%d\n", param->u.crypt.key_len); */
/* set mic key */
memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8);
memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8);
psecuritypriv->busetkipkey = true;
} else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
RTW_INFO("%s, set pairwise key, CCMP\n", __func__);
psta->dot118021XPrivacy = _AES_;
} else {
RTW_INFO("%s, set pairwise key, none\n", __func__);
psta->dot118021XPrivacy = _NO_PRIVACY_;
}
rtw_ap_set_pairwise_key(padapter, psta);
psta->ieee8021x_blocked = false;
psta->bpairwise_key_installed = true;
} else { /* group key??? */
if (strcmp(param->u.crypt.alg, "WEP") == 0) {
memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
if (param->u.crypt.key_len == 13)
psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
} else if (strcmp(param->u.crypt.alg, "TKIP") == 0) {
psecuritypriv->dot118021XGrpPrivacy = _TKIP_;
memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
/* DEBUG_ERR("set key length :param->u.crypt.key_len=%d\n", param->u.crypt.key_len); */
/* set mic key */
memcpy(psecuritypriv->dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8);
memcpy(psecuritypriv->dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8);
psecuritypriv->busetkipkey = true;
} else if (strcmp(param->u.crypt.alg, "CCMP") == 0) {
psecuritypriv->dot118021XGrpPrivacy = _AES_;
memcpy(psecuritypriv->dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
} else
psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_;
psecuritypriv->dot118021XGrpKeyid = param->u.crypt.idx;
psecuritypriv->binstallGrpkey = true;
psecuritypriv->dot11PrivacyAlgrthm = psecuritypriv->dot118021XGrpPrivacy;/* !!! */
rtw_ap_set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx);
pbcmc_sta = rtw_get_bcmc_stainfo(padapter);
if (pbcmc_sta) {
pbcmc_sta->ieee8021x_blocked = false;
pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy; /* rx will use bmc_sta's dot118021XPrivacy */
}
}
}
}
exit:
return ret;
}
#endif
static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param *param, u32 param_len)
{
int ret = 0;
u32 wep_key_idx, wep_key_len, wep_total_len;
_adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct security_priv *psecuritypriv = &padapter->securitypriv;
#ifdef CONFIG_P2P
struct wifidirect_info *pwdinfo = &padapter->wdinfo;
#endif /* CONFIG_P2P */
RTW_INFO("%s\n", __func__);
param->u.crypt.err = 0;
param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0';
if (param_len < (u32)((u8 *) param->u.crypt.key - (u8 *) param) + param->u.crypt.key_len) {
ret = -EINVAL;
goto exit;
}
if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
if (param->u.crypt.idx >= WEP_KEYS
#ifdef CONFIG_IEEE80211W
&& param->u.crypt.idx > BIP_MAX_KEYID
#endif /* CONFIG_IEEE80211W */
) {
ret = -EINVAL;
goto exit;
}
} else {
#ifdef CONFIG_WAPI_SUPPORT
if (strcmp(param->u.crypt.alg, "SMS4"))
#endif
{
ret = -EINVAL;
goto exit;
}
}
if (strcmp(param->u.crypt.alg, "WEP") == 0) {
RTW_INFO("wpa_set_encryption, crypt.alg = WEP\n");
wep_key_idx = param->u.crypt.idx;
wep_key_len = param->u.crypt.key_len;
if ((wep_key_idx > WEP_KEYS) || (wep_key_len <= 0)) {
ret = -EINVAL;
goto exit;
}
if (psecuritypriv->bWepDefaultKeyIdxSet == 0) {
/* wep default key has not been set, so use this key index as default key. */
wep_key_len = wep_key_len <= 5 ? 5 : 13;
psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled;
psecuritypriv->dot11PrivacyAlgrthm = _WEP40_;
psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
if (wep_key_len == 13) {
psecuritypriv->dot11PrivacyAlgrthm = _WEP104_;
psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
}
psecuritypriv->dot11PrivacyKeyIndex = wep_key_idx;
}
memcpy(&(psecuritypriv->dot11DefKey[wep_key_idx].skey[0]), param->u.crypt.key, wep_key_len);
psecuritypriv->dot11DefKeylen[wep_key_idx] = wep_key_len;
rtw_set_key(padapter, psecuritypriv, wep_key_idx, 0, true);
goto exit;
}
if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) { /* 802_1x */
struct sta_info *psta, *pbcmc_sta;
struct sta_priv *pstapriv = &padapter->stapriv;
/* RTW_INFO("%s, : dot11AuthAlgrthm == dot11AuthAlgrthm_8021X\n", __func__); */
if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE) ) { /* sta mode */
#ifdef CONFIG_RTW_80211R
if ((rtw_to_roam(padapter) > 0) && rtw_chk_ft_flags(padapter, RTW_FT_SUPPORTED))
psta = rtw_get_stainfo(pstapriv, pmlmepriv->assoc_bssid);
else
#endif
psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv));
if (psta == NULL) {
/* DEBUG_ERR( ("Set wpa_set_encryption: Obtain Sta_info fail\n")); */
RTW_INFO("%s, : Obtain Sta_info fail\n", __func__);
} else {
/* Jeff: don't disable ieee8021x_blocked while clearing key */
if (strcmp(param->u.crypt.alg, "none") != 0)
psta->ieee8021x_blocked = false;
if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) ||
(padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled))
psta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm;
if (param->u.crypt.set_tx == 1) { /* pairwise key */
RTW_INFO("%s, : param->u.crypt.set_tx ==1\n", __func__);
memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */
/* DEBUG_ERR(("\nset key length :param->u.crypt.key_len=%d\n", param->u.crypt.key_len)); */
memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8);
memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8);
padapter->securitypriv.busetkipkey = false;
/* _set_timer(&padapter->securitypriv.tkip_timer, 50); */
}
psta->bpairwise_key_installed = true;
#ifdef CONFIG_RTW_80211R
psta->ft_pairwise_key_installed = true;
#endif
/* DEBUG_ERR((" param->u.crypt.key_len=%d\n", param->u.crypt.key_len)); */
RTW_INFO(" ~~~~set sta key:unicastkey\n");
rtw_setstakey_cmd(padapter, psta, UNICAST_KEY, true);
} else { /* group key */
if (strcmp(param->u.crypt.alg, "TKIP") == 0 || strcmp(param->u.crypt.alg, "CCMP") == 0) {
memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key,
(param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8);
memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8);
padapter->securitypriv.binstallGrpkey = true;
if (param->u.crypt.idx < 4)
memcpy(padapter->securitypriv.iv_seq[param->u.crypt.idx], param->u.crypt.seq, 8);
/* DEBUG_ERR((" param->u.crypt.key_len=%d\n", param->u.crypt.key_len)); */
RTW_INFO(" ~~~~set sta key:groupkey\n");
padapter->securitypriv.dot118021XGrpKeyid = param->u.crypt.idx;
rtw_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx, 1, true);
}
#ifdef CONFIG_IEEE80211W
else if (strcmp(param->u.crypt.alg, "BIP") == 0) {
int no;
/* RTW_INFO("BIP key_len=%d , index=%d @@@@@@@@@@@@@@@@@@\n", param->u.crypt.key_len, param->u.crypt.idx); */
/* save the IGTK key, length 16 bytes */
memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key,
(param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len));
/*RTW_INFO("IGTK key below:\n");
for(no=0;no<16;no++)
printk(" %02x ", padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey[no]);
RTW_INFO("\n");*/
padapter->securitypriv.dot11wBIPKeyid = param->u.crypt.idx;
padapter->securitypriv.binstallBIPkey = true;
RTW_INFO(" ~~~~set sta key:IGKT\n");
}
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_P2P
if (pwdinfo->driver_interface == DRIVER_CFG80211) {
if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_PROVISIONING_ING))
rtw_p2p_set_state(pwdinfo, P2P_STATE_PROVISIONING_DONE);
}
#endif /* CONFIG_P2P */
}
}
pbcmc_sta = rtw_get_bcmc_stainfo(padapter);
if (pbcmc_sta == NULL) {
/* DEBUG_ERR( ("Set OID_802_11_ADD_KEY: bcmc stainfo is null\n")); */
} else {
/* Jeff: don't disable ieee8021x_blocked while clearing key */
if (strcmp(param->u.crypt.alg, "none") != 0)
pbcmc_sta->ieee8021x_blocked = false;
if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) ||
(padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled))
pbcmc_sta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm;
}
} else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { /* adhoc mode */
}
}
#ifdef CONFIG_WAPI_SUPPORT
if (strcmp(param->u.crypt.alg, "SMS4") == 0) {
PRT_WAPI_T pWapiInfo = &padapter->wapiInfo;
PRT_WAPI_STA_INFO pWapiSta;
u8 WapiASUEPNInitialValueSrc[16] = {0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C} ;
u8 WapiAEPNInitialValueSrc[16] = {0x37, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C} ;
u8 WapiAEMultiCastPNInitialValueSrc[16] = {0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C, 0x36, 0x5C} ;
if (param->u.crypt.set_tx == 1) {
list_for_each_entry(pWapiSta, &pWapiInfo->wapiSTAUsedList, list) {
if (!memcmp(pWapiSta->PeerMacAddr, param->sta_addr, 6)) {
memcpy(pWapiSta->lastTxUnicastPN, WapiASUEPNInitialValueSrc, 16);
pWapiSta->wapiUsk.bSet = true;
memcpy(pWapiSta->wapiUsk.dataKey, param->u.crypt.key, 16);
memcpy(pWapiSta->wapiUsk.micKey, param->u.crypt.key + 16, 16);
pWapiSta->wapiUsk.keyId = param->u.crypt.idx ;
pWapiSta->wapiUsk.bTxEnable = true;
memcpy(pWapiSta->lastRxUnicastPNBEQueue, WapiAEPNInitialValueSrc, 16);
memcpy(pWapiSta->lastRxUnicastPNBKQueue, WapiAEPNInitialValueSrc, 16);
memcpy(pWapiSta->lastRxUnicastPNVIQueue, WapiAEPNInitialValueSrc, 16);
memcpy(pWapiSta->lastRxUnicastPNVOQueue, WapiAEPNInitialValueSrc, 16);
memcpy(pWapiSta->lastRxUnicastPN, WapiAEPNInitialValueSrc, 16);
pWapiSta->wapiUskUpdate.bTxEnable = false;
pWapiSta->wapiUskUpdate.bSet = false;
if (psecuritypriv->sw_encrypt == false || psecuritypriv->sw_decrypt == false) {
/* set unicast key for ASUE */
rtw_wapi_set_key(padapter, &pWapiSta->wapiUsk, pWapiSta, false, false);
}
}
}
} else {
list_for_each_entry(pWapiSta, &pWapiInfo->wapiSTAUsedList, list) {
if (!memcmp(pWapiSta->PeerMacAddr, get_bssid(pmlmepriv), 6)) {
pWapiSta->wapiMsk.bSet = true;
memcpy(pWapiSta->wapiMsk.dataKey, param->u.crypt.key, 16);
memcpy(pWapiSta->wapiMsk.micKey, param->u.crypt.key + 16, 16);
pWapiSta->wapiMsk.keyId = param->u.crypt.idx ;
pWapiSta->wapiMsk.bTxEnable = false;
if (!pWapiSta->bSetkeyOk)
pWapiSta->bSetkeyOk = true;
pWapiSta->bAuthenticateInProgress = false;
memcpy(pWapiSta->lastRxMulticastPN, WapiAEMultiCastPNInitialValueSrc, 16);
if (psecuritypriv->sw_decrypt == false) {
/* set rx broadcast key for ASUE */
rtw_wapi_set_key(padapter, &pWapiSta->wapiMsk, pWapiSta, true, false);
}
}
}
}
}
#endif
exit:
RTW_INFO("%s, ret=%d\n", __func__, ret);
return ret;
}
static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct net_device *ndev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
u8 key_index, bool pairwise, const u8 *mac_addr,
#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) */
u8 key_index, const u8 *mac_addr,
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) */
struct key_params *params)
{
char *alg_name;
u32 param_len;
struct ieee_param *param = NULL;
int ret = 0;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct wireless_dev *rtw_wdev = padapter->rtw_wdev;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
#ifdef CONFIG_TDLS
struct sta_info *ptdls_sta;
#endif /* CONFIG_TDLS */
RTW_INFO(FUNC_NDEV_FMT" adding key for %pM\n", FUNC_NDEV_ARG(ndev), mac_addr);
RTW_INFO("cipher=0x%x\n", params->cipher);
RTW_INFO("key_len=0x%x\n", params->key_len);
RTW_INFO("seq_len=0x%x\n", params->seq_len);
RTW_INFO("key_index=%d\n", key_index);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
RTW_INFO("pairwise=%d\n", pairwise);
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) */
param_len = sizeof(struct ieee_param) + params->key_len;
param = (struct ieee_param *)rtw_malloc(param_len);
if (param == NULL)
return -1;
memset(param, 0, param_len);
param->cmd = IEEE_CMD_SET_ENCRYPTION;
memset(param->sta_addr, 0xff, ETH_ALEN);
switch (params->cipher) {
case IW_AUTH_CIPHER_NONE:
/* todo: remove key */
/* remove = 1; */
alg_name = "none";
break;
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
alg_name = "WEP";
break;
case WLAN_CIPHER_SUITE_TKIP:
alg_name = "TKIP";
break;
case WLAN_CIPHER_SUITE_CCMP:
alg_name = "CCMP";
break;
#ifdef CONFIG_IEEE80211W
case WLAN_CIPHER_SUITE_AES_CMAC:
alg_name = "BIP";
break;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WAPI_SUPPORT
case WLAN_CIPHER_SUITE_SMS4:
alg_name = "SMS4";
if (pairwise == NL80211_KEYTYPE_PAIRWISE) {
if (key_index != 0 && key_index != 1) {
ret = -ENOTSUPP;
goto addkey_end;
}
memcpy((void *)param->sta_addr, (void *)mac_addr, ETH_ALEN);
} else
RTW_INFO("mac_addr is null\n");
RTW_INFO("rtw_wx_set_enc_ext: SMS4 case\n");
break;
#endif
default:
ret = -ENOTSUPP;
goto addkey_end;
}
strncpy((char *)param->u.crypt.alg, alg_name, IEEE_CRYPT_ALG_NAME_LEN);
if (!mac_addr || is_broadcast_ether_addr(mac_addr)) {
param->u.crypt.set_tx = 0; /* for wpa/wpa2 group key */
} else {
param->u.crypt.set_tx = 1; /* for wpa/wpa2 pairwise key */
}
/* param->u.crypt.idx = key_index - 1; */
param->u.crypt.idx = key_index;
if (params->seq_len && params->seq)
memcpy(param->u.crypt.seq, (u8 *)params->seq, params->seq_len);
if (params->key_len && params->key) {
param->u.crypt.key_len = params->key_len;
memcpy(param->u.crypt.key, (u8 *)params->key, params->key_len);
}
if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) ) {
#ifdef CONFIG_TDLS
if (rtw_tdls_is_driver_setup(padapter) == false && mac_addr) {
ptdls_sta = rtw_get_stainfo(&padapter->stapriv, (void *)mac_addr);
if (ptdls_sta != NULL && ptdls_sta->tdls_sta_state) {
memcpy(ptdls_sta->tpk.tk, params->key, params->key_len);
rtw_tdls_set_key(padapter, ptdls_sta);
goto addkey_end;
}
}
#endif /* CONFIG_TDLS */
ret = rtw_cfg80211_set_encryption(ndev, param, param_len);
} else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) ) {
#ifdef CONFIG_AP_MODE
if (mac_addr)
memcpy(param->sta_addr, (void *)mac_addr, ETH_ALEN);
ret = rtw_cfg80211_ap_set_encryption(ndev, param, param_len);
#endif
} else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)
|| check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)
) {
/* RTW_INFO("@@@@@@@@@@ fw_state=0x%x, iftype=%d\n", pmlmepriv->fw_state, rtw_wdev->iftype); */
ret = rtw_cfg80211_set_encryption(ndev, param, param_len);
} else
RTW_INFO("error! fw_state=0x%x, iftype=%d\n", pmlmepriv->fw_state, rtw_wdev->iftype);
addkey_end:
if (param)
rtw_mfree((u8 *)param, param_len);
return ret;
}
static int cfg80211_rtw_get_key(struct wiphy *wiphy, struct net_device *ndev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
u8 key_index, bool pairwise, const u8 *mac_addr,
#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) */
u8 key_index, const u8 *mac_addr,
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) */
void *cookie,
void (*callback)(void *cookie, struct key_params *))
{
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
return 0;
}
static int cfg80211_rtw_del_key(struct wiphy *wiphy, struct net_device *ndev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
u8 key_index, bool pairwise, const u8 *mac_addr)
#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) */
u8 key_index, const u8 *mac_addr)
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) */
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct security_priv *psecuritypriv = &padapter->securitypriv;
RTW_INFO(FUNC_NDEV_FMT" key_index=%d\n", FUNC_NDEV_ARG(ndev), key_index);
if (key_index == psecuritypriv->dot11PrivacyKeyIndex) {
/* clear the flag of wep default key set. */
psecuritypriv->bWepDefaultKeyIdxSet = 0;
}
return 0;
}
static int cfg80211_rtw_set_default_key(struct wiphy *wiphy,
struct net_device *ndev, u8 key_index
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) || defined(COMPAT_KERNEL_RELEASE)
, bool unicast, bool multicast
#endif
)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct security_priv *psecuritypriv = &padapter->securitypriv;
#define SET_DEF_KEY_PARAM_FMT " key_index=%d"
#define SET_DEF_KEY_PARAM_ARG , key_index
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) || defined(COMPAT_KERNEL_RELEASE)
#define SET_DEF_KEY_PARAM_FMT_2_6_38 ", unicast=%d, multicast=%d"
#define SET_DEF_KEY_PARAM_ARG_2_6_38 , unicast, multicast
#else
#define SET_DEF_KEY_PARAM_FMT_2_6_38 ""
#define SET_DEF_KEY_PARAM_ARG_2_6_38
#endif
RTW_INFO(FUNC_NDEV_FMT
SET_DEF_KEY_PARAM_FMT
SET_DEF_KEY_PARAM_FMT_2_6_38
"\n", FUNC_NDEV_ARG(ndev)
SET_DEF_KEY_PARAM_ARG
SET_DEF_KEY_PARAM_ARG_2_6_38
);
if ((key_index < WEP_KEYS) && ((psecuritypriv->dot11PrivacyAlgrthm == _WEP40_) || (psecuritypriv->dot11PrivacyAlgrthm == _WEP104_))) { /* set wep default key */
psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled;
psecuritypriv->dot11PrivacyKeyIndex = key_index;
psecuritypriv->dot11PrivacyAlgrthm = _WEP40_;
psecuritypriv->dot118021XGrpPrivacy = _WEP40_;
if (psecuritypriv->dot11DefKeylen[key_index] == 13) {
psecuritypriv->dot11PrivacyAlgrthm = _WEP104_;
psecuritypriv->dot118021XGrpPrivacy = _WEP104_;
}
psecuritypriv->bWepDefaultKeyIdxSet = 1; /* set the flag to represent that wep default key has been set */
}
return 0;
}
#if defined(CONFIG_GTK_OL) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0))
static int cfg80211_rtw_set_rekey_data(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_gtk_rekey_data *data)
{
/*int i;*/
struct sta_info *psta;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct sta_priv *pstapriv = &padapter->stapriv;
struct security_priv *psecuritypriv = &(padapter->securitypriv);
psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv));
if (psta == NULL) {
RTW_INFO("%s, : Obtain Sta_info fail\n", __func__);
return -1;
}
memcpy(psta->kek, data->kek, NL80211_KEK_LEN);
/*printk("\ncfg80211_rtw_set_rekey_data KEK:");
for(i=0;i<NL80211_KEK_LEN; i++)
printk(" %02x ", psta->kek[i]);*/
memcpy(psta->kck, data->kck, NL80211_KCK_LEN);
/*printk("\ncfg80211_rtw_set_rekey_data KCK:");
for(i=0;i<NL80211_KCK_LEN; i++)
printk(" %02x ", psta->kck[i]);*/
memcpy(psta->replay_ctr, data->replay_ctr, NL80211_REPLAY_CTR_LEN);
psecuritypriv->binstallKCK_KEK = true;
/*printk("\nREPLAY_CTR: ");
for(i=0;i<RTW_REPLAY_CTR_LEN; i++)
printk(" %02x ", psta->replay_ctr[i]);*/
return 0;
}
#endif /*CONFIG_GTK_OL*/
static int cfg80211_rtw_get_station(struct wiphy *wiphy,
struct net_device *ndev,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
u8 *mac,
#else
const u8 *mac,
#endif
struct station_info *sinfo)
{
int ret = 0;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct sta_info *psta = NULL;
struct sta_priv *pstapriv = &padapter->stapriv;
sinfo->filled = 0;
if (!mac) {
RTW_INFO(FUNC_NDEV_FMT" mac==%p\n", FUNC_NDEV_ARG(ndev), mac);
ret = -ENOENT;
goto exit;
}
psta = rtw_get_stainfo(pstapriv, (u8 *)mac);
if (psta == NULL) {
RTW_INFO("%s, sta_info is null\n", __func__);
ret = -ENOENT;
goto exit;
}
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO(FUNC_NDEV_FMT" mac="MAC_FMT"\n", FUNC_NDEV_ARG(ndev), MAC_ARG(mac));
#endif
/* for infra./P2PClient mode */
if (check_fwstate(pmlmepriv, WIFI_STATION_STATE)
&& check_fwstate(pmlmepriv, _FW_LINKED)
) {
struct wlan_network *cur_network = &(pmlmepriv->cur_network);
if (!memcmp((u8 *)mac, cur_network->network.MacAddress, ETH_ALEN) == false) {
RTW_INFO("%s, mismatch bssid="MAC_FMT"\n", __func__, MAC_ARG(cur_network->network.MacAddress));
ret = -ENOENT;
goto exit;
}
sinfo->filled |= STATION_INFO_SIGNAL;
sinfo->signal = translate_percentage_to_dbm(padapter->recvpriv.signal_strength);
sinfo->filled |= STATION_INFO_TX_BITRATE;
sinfo->txrate.legacy = rtw_get_cur_max_rate(padapter);
sinfo->filled |= STATION_INFO_RX_PACKETS;
sinfo->rx_packets = sta_rx_data_pkts(psta);
sinfo->filled |= STATION_INFO_TX_PACKETS;
sinfo->tx_packets = psta->sta_stats.tx_pkts;
sinfo->filled |= STATION_INFO_TX_FAILED;
sinfo->tx_failed = psta->sta_stats.tx_fail_cnt;
}
/* for Ad-Hoc/AP mode */
if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)
|| check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE)
|| check_fwstate(pmlmepriv, WIFI_AP_STATE))
&& check_fwstate(pmlmepriv, _FW_LINKED)
) {
/* TODO: should acquire station info... */
}
exit:
return ret;
}
extern int netdev_open(struct net_device *pnetdev);
static int cfg80211_rtw_change_iface(struct wiphy *wiphy,
struct net_device *ndev,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
enum nl80211_iftype type, u32 *flags,
#else
enum nl80211_iftype type,
#endif
struct vif_params *params)
{
enum nl80211_iftype old_type;
NDIS_802_11_NETWORK_INFRASTRUCTURE networkType;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct wireless_dev *rtw_wdev = padapter->rtw_wdev;
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
#ifdef CONFIG_P2P
struct wifidirect_info *pwdinfo = &(padapter->wdinfo);
u8 is_p2p = false;
#endif
int ret = 0;
u8 change = false;
RTW_INFO(FUNC_NDEV_FMT" type=%d, hw_port:%d\n", FUNC_NDEV_ARG(ndev), type, padapter->hw_port);
if (adapter_to_dvobj(padapter)->processing_dev_remove ) {
ret = -EPERM;
goto exit;
}
RTW_INFO(FUNC_NDEV_FMT" call netdev_open\n", FUNC_NDEV_ARG(ndev));
if (netdev_open(ndev) != 0) {
RTW_INFO(FUNC_NDEV_FMT" call netdev_open fail\n", FUNC_NDEV_ARG(ndev));
ret = -EPERM;
goto exit;
}
if (_FAIL == rtw_pwr_wakeup(padapter)) {
RTW_INFO(FUNC_NDEV_FMT" call rtw_pwr_wakeup fail\n", FUNC_NDEV_ARG(ndev));
ret = -EPERM;
goto exit;
}
old_type = rtw_wdev->iftype;
RTW_INFO(FUNC_NDEV_FMT" old_iftype=%d, new_iftype=%d\n",
FUNC_NDEV_ARG(ndev), old_type, type);
if (old_type != type) {
change = true;
pmlmeext->action_public_rxseq = 0xffff;
pmlmeext->action_public_dialog_token = 0xff;
}
/* initial default type */
ndev->type = ARPHRD_ETHER;
/*
* Disable Power Save in moniter mode,
* and enable it after leaving moniter mode.
*/
if (type == NL80211_IFTYPE_MONITOR) {
rtw_ps_deny(padapter, PS_DENY_MONITOR_MODE);
LeaveAllPowerSaveMode(padapter);
} else if (old_type == NL80211_IFTYPE_MONITOR) {
/* driver in moniter mode in last time */
rtw_ps_deny_cancel(padapter, PS_DENY_MONITOR_MODE);
}
switch (type) {
case NL80211_IFTYPE_ADHOC:
networkType = Ndis802_11IBSS;
break;
#if defined(CONFIG_P2P) && ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE))
case NL80211_IFTYPE_P2P_CLIENT:
is_p2p = true;
__attribute__ ((__fallthrough__));/* FALL THRU */
#endif
case NL80211_IFTYPE_STATION:
networkType = Ndis802_11Infrastructure;
#ifdef CONFIG_P2P
if (change && pwdinfo->driver_interface == DRIVER_CFG80211) {
if (is_p2p )
rtw_p2p_enable(padapter, P2P_ROLE_CLIENT);
#if !RTW_P2P_GROUP_INTERFACE
else if (rtw_p2p_chk_role(pwdinfo, P2P_ROLE_CLIENT)
|| rtw_p2p_chk_role(pwdinfo, P2P_ROLE_GO)
) {
/* it means remove GC/GO and change mode from GC/GO to station(P2P DEVICE) */
rtw_p2p_set_role(pwdinfo, P2P_ROLE_DEVICE);
}
#endif
}
#endif /* CONFIG_P2P */
break;
#if defined(CONFIG_P2P) && ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE))
case NL80211_IFTYPE_P2P_GO:
is_p2p = true;
__attribute__ ((__fallthrough__));/* FALL THRU */
#endif
case NL80211_IFTYPE_AP:
networkType = Ndis802_11APMode;
#ifdef CONFIG_P2P
if (change && pwdinfo->driver_interface == DRIVER_CFG80211) {
if (is_p2p )
rtw_p2p_enable(padapter, P2P_ROLE_GO);
#if !RTW_P2P_GROUP_INTERFACE
else if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) {
/* it means P2P Group created, we will be GO and change mode from P2P DEVICE to AP(GO) */
rtw_p2p_set_role(pwdinfo, P2P_ROLE_GO);
}
#endif
}
#endif /* CONFIG_P2P */
break;
case NL80211_IFTYPE_MONITOR:
networkType = Ndis802_11Monitor;
ndev->type = ARPHRD_IEEE80211_RADIOTAP; /* IEEE 802.11 + radiotap header : 803 */
break;
default:
ret = -EOPNOTSUPP;
goto exit;
}
rtw_wdev->iftype = type;
if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) {
rtw_wdev->iftype = old_type;
ret = -EPERM;
goto exit;
}
rtw_setopmode_cmd(padapter, networkType, true);
exit:
RTW_INFO(FUNC_NDEV_FMT" ret:%d\n", FUNC_NDEV_ARG(ndev), ret);
return ret;
}
void rtw_cfg80211_indicate_scan_done(_adapter *adapter, bool aborted)
{
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(adapter);
unsigned long irqL;
#if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE)
struct cfg80211_scan_info info;
memset(&info, 0, sizeof(info));
info.aborted = aborted;
#endif
_enter_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
if (pwdev_priv->scan_request != NULL) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s with scan req\n", __func__);
#endif
/* avoid WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); */
if (pwdev_priv->scan_request->wiphy != pwdev_priv->rtw_wdev->wiphy)
RTW_INFO("error wiphy compare\n");
else
#if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE)
cfg80211_scan_done(pwdev_priv->scan_request, &info);
#else
cfg80211_scan_done(pwdev_priv->scan_request, aborted);
#endif
pwdev_priv->scan_request = NULL;
} else {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s without scan req\n", __func__);
#endif
}
_exit_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
}
u32 rtw_cfg80211_wait_scan_req_empty(_adapter *adapter, u32 timeout_ms)
{
struct rtw_wdev_priv *wdev_priv = adapter_wdev_data(adapter);
u8 empty = false;
u32 start;
u32 pass_ms;
start = jiffies;
while (rtw_get_passing_time_ms(start) <= timeout_ms) {
if (RTW_CANNOT_RUN(adapter))
break;
if (!wdev_priv->scan_request) {
empty = true;
break;
}
rtw_msleep_os(10);
}
pass_ms = rtw_get_passing_time_ms(start);
if (empty == false && pass_ms > timeout_ms)
RTW_INFO(FUNC_ADPT_FMT" pass_ms:%u, timeout\n"
, FUNC_ADPT_ARG(adapter), pass_ms);
return pass_ms;
}
void rtw_cfg80211_unlink_bss(_adapter *padapter, struct wlan_network *pnetwork)
{
struct wireless_dev *pwdev = padapter->rtw_wdev;
struct wiphy *wiphy = pwdev->wiphy;
struct cfg80211_bss *bss = NULL;
WLAN_BSSID_EX select_network = pnetwork->network;
bss = cfg80211_get_bss(wiphy, NULL/*notify_channel*/,
select_network.MacAddress, select_network.Ssid.Ssid,
select_network.Ssid.SsidLength,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
select_network.InfrastructureMode == Ndis802_11Infrastructure?IEEE80211_BSS_TYPE_ESS:IEEE80211_BSS_TYPE_IBSS,
IEEE80211_PRIVACY(select_network.Privacy));
#else
select_network.InfrastructureMode == Ndis802_11Infrastructure?WLAN_CAPABILITY_ESS:WLAN_CAPABILITY_IBSS,
select_network.InfrastructureMode == Ndis802_11Infrastructure?WLAN_CAPABILITY_ESS:WLAN_CAPABILITY_IBSS);
#endif
if (bss) {
cfg80211_unlink_bss(wiphy, bss);
RTW_INFO("%s(): cfg80211_unlink %s!! () ", __func__, select_network.Ssid.Ssid);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
cfg80211_put_bss(padapter->rtw_wdev->wiphy, bss);
#else
cfg80211_put_bss(bss);
#endif
}
return;
}
/* if target wps scan ongoing, target_ssid is filled */
static int rtw_cfg80211_is_target_wps_scan(struct cfg80211_scan_request *scan_req, struct cfg80211_ssid *target_ssid)
{
int ret = 0;
if (scan_req->n_ssids != 1
|| scan_req->ssids[0].ssid_len == 0
|| scan_req->n_channels != 1
)
goto exit;
/* under target WPS scan */
memcpy(target_ssid, scan_req->ssids, sizeof(struct cfg80211_ssid));
ret = 1;
exit:
return ret;
}
static void _rtw_cfg80211_surveydone_event_callback(_adapter *padapter, struct cfg80211_scan_request *scan_req)
{
unsigned long irqL;
_list *plist, *phead;
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
_queue *queue = &(pmlmepriv->scanned_queue);
struct wlan_network *pnetwork = NULL;
u32 cnt = 0;
u32 wait_for_surveydone;
sint wait_status;
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter);
struct cfg80211_ssid target_ssid;
u8 target_wps_scan = 0;
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s\n", __func__);
#endif
if (scan_req)
target_wps_scan = rtw_cfg80211_is_target_wps_scan(scan_req, &target_ssid);
else {
_enter_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
if (pwdev_priv->scan_request != NULL)
target_wps_scan = rtw_cfg80211_is_target_wps_scan(pwdev_priv->scan_request, &target_ssid);
_exit_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
}
_enter_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL);
phead = get_list_head(queue);
plist = get_next(phead);
while (1) {
if (rtw_end_of_queue_search(phead, plist) )
break;
pnetwork = LIST_CONTAINOR(plist, struct wlan_network, list);
/* report network only if the current channel set contains the channel to which this network belongs */
if (rtw_ch_set_search_ch(padapter->mlmeextpriv.channel_set, pnetwork->network.Configuration.DSConfig) >= 0
&& rtw_mlme_band_check(padapter, pnetwork->network.Configuration.DSConfig)
&& rtw_validate_ssid(&(pnetwork->network.Ssid))
) {
if (target_wps_scan)
rtw_cfg80211_clear_wps_sr_of_non_target_bss(padapter, pnetwork, &target_ssid);
rtw_cfg80211_inform_bss(padapter, pnetwork);
}
plist = get_next(plist);
}
_exit_critical_bh(&(pmlmepriv->scanned_queue.lock), &irqL);
}
inline void rtw_cfg80211_surveydone_event_callback(_adapter *padapter)
{
_rtw_cfg80211_surveydone_event_callback(padapter, NULL);
}
static int rtw_cfg80211_set_probe_req_wpsp2pie(_adapter *padapter, char *buf, int len)
{
int ret = 0;
uint wps_ielen = 0;
u8 *wps_ie;
u32 p2p_ielen = 0;
u8 *p2p_ie;
u32 wfd_ielen = 0;
u8 *wfd_ie;
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, ielen=%d\n", __func__, len);
#endif
if (len > 0) {
wps_ie = rtw_get_wps_ie(buf, len, NULL, &wps_ielen);
if (wps_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("probe_req_wps_ielen=%d\n", wps_ielen);
#endif
if (pmlmepriv->wps_probe_req_ie) {
u32 free_len = pmlmepriv->wps_probe_req_ie_len;
pmlmepriv->wps_probe_req_ie_len = 0;
rtw_mfree(pmlmepriv->wps_probe_req_ie, free_len);
pmlmepriv->wps_probe_req_ie = NULL;
}
pmlmepriv->wps_probe_req_ie = rtw_malloc(wps_ielen);
if (pmlmepriv->wps_probe_req_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->wps_probe_req_ie, wps_ie, wps_ielen);
pmlmepriv->wps_probe_req_ie_len = wps_ielen;
}
/* buf += wps_ielen; */
/* len -= wps_ielen; */
#ifdef CONFIG_P2P
p2p_ie = rtw_get_p2p_ie(buf, len, NULL, &p2p_ielen);
if (p2p_ie) {
struct wifidirect_info *wdinfo = &padapter->wdinfo;
u32 attr_contentlen = 0;
u8 listen_ch_attr[5];
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("probe_req_p2p_ielen=%d\n", p2p_ielen);
#endif
if (pmlmepriv->p2p_probe_req_ie) {
u32 free_len = pmlmepriv->p2p_probe_req_ie_len;
pmlmepriv->p2p_probe_req_ie_len = 0;
rtw_mfree(pmlmepriv->p2p_probe_req_ie, free_len);
pmlmepriv->p2p_probe_req_ie = NULL;
}
pmlmepriv->p2p_probe_req_ie = rtw_malloc(p2p_ielen);
if (pmlmepriv->p2p_probe_req_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->p2p_probe_req_ie, p2p_ie, p2p_ielen);
pmlmepriv->p2p_probe_req_ie_len = p2p_ielen;
if (rtw_get_p2p_attr_content(p2p_ie, p2p_ielen, P2P_ATTR_LISTEN_CH, (u8 *)listen_ch_attr, (uint *) &attr_contentlen)
&& attr_contentlen == 5) {
if (wdinfo->listen_channel != listen_ch_attr[4]) {
RTW_INFO(FUNC_ADPT_FMT" listen channel - country:%c%c%c, class:%u, ch:%u\n",
FUNC_ADPT_ARG(padapter), listen_ch_attr[0], listen_ch_attr[1], listen_ch_attr[2],
listen_ch_attr[3], listen_ch_attr[4]);
wdinfo->listen_channel = listen_ch_attr[4];
}
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WFD
wfd_ie = rtw_get_wfd_ie(buf, len, NULL, &wfd_ielen);
if (wfd_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("probe_req_wfd_ielen=%d\n", wfd_ielen);
#endif
if (rtw_mlme_update_wfd_ie_data(pmlmepriv, MLME_PROBE_REQ_IE, wfd_ie, wfd_ielen) != _SUCCESS)
return -EINVAL;
}
#endif /* CONFIG_WFD */
}
return ret;
}
#ifdef CONFIG_CONCURRENT_MODE
u8 rtw_cfg80211_scan_via_buddy(_adapter *padapter, struct cfg80211_scan_request *request)
{
int i;
u8 ret = false;
_adapter *iface = NULL;
unsigned long irqL;
struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter);
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
for (i = 0; i < dvobj->iface_nums; i++) {
struct mlme_priv *buddy_mlmepriv;
struct rtw_wdev_priv *buddy_wdev_priv;
iface = dvobj->padapters[i];
if (iface == NULL)
continue;
if (iface == padapter)
continue;
if (rtw_is_adapter_up(iface) == false)
continue;
buddy_mlmepriv = &iface->mlmepriv;
if (!check_fwstate(buddy_mlmepriv, _FW_UNDER_SURVEY))
continue;
buddy_wdev_priv = adapter_wdev_data(iface);
_enter_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
_enter_critical_bh(&buddy_wdev_priv->scan_req_lock, &irqL);
if (buddy_wdev_priv->scan_request) {
pmlmepriv->scanning_via_buddy_intf = true;
_enter_critical_bh(&pmlmepriv->lock, &irqL);
set_fwstate(pmlmepriv, _FW_UNDER_SURVEY);
_exit_critical_bh(&pmlmepriv->lock, &irqL);
pwdev_priv->scan_request = request;
ret = true;
}
_exit_critical_bh(&buddy_wdev_priv->scan_req_lock, &irqL);
_exit_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
if (ret )
goto exit;
}
exit:
return ret;
}
void rtw_cfg80211_indicate_scan_done_for_buddy(_adapter *padapter, bool bscan_aborted)
{
int i;
u8 ret = 0;
_adapter *iface = NULL;
unsigned long irqL;
struct dvobj_priv *dvobj = adapter_to_dvobj(padapter);
struct mlme_priv *mlmepriv;
struct rtw_wdev_priv *wdev_priv;
bool indicate_buddy_scan;
for (i = 0; i < dvobj->iface_nums; i++) {
iface = dvobj->padapters[i];
if ((iface) && rtw_is_adapter_up(iface)) {
if (iface == padapter)
continue;
mlmepriv = &(iface->mlmepriv);
wdev_priv = adapter_wdev_data(iface);
indicate_buddy_scan = false;
_enter_critical_bh(&wdev_priv->scan_req_lock, &irqL);
if (wdev_priv->scan_request && mlmepriv->scanning_via_buddy_intf ) {
mlmepriv->scanning_via_buddy_intf = false;
clr_fwstate(mlmepriv, _FW_UNDER_SURVEY);
indicate_buddy_scan = true;
}
_exit_critical_bh(&wdev_priv->scan_req_lock, &irqL);
if (indicate_buddy_scan ) {
rtw_cfg80211_surveydone_event_callback(iface);
rtw_indicate_scan_done(iface, bscan_aborted);
}
}
}
}
#endif /* CONFIG_CONCURRENT_MODE */
static int cfg80211_rtw_scan(struct wiphy *wiphy
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
, struct net_device *ndev
#endif
, struct cfg80211_scan_request *request)
{
int i, chan_num = 0;
u8 _status = false;
int ret = 0;
NDIS_802_11_SSID ssid[RTW_SSID_SCAN_AMOUNT];
struct rtw_ieee80211_channel ch[RTW_CHANNEL_SCAN_AMOUNT];
struct rtw_ieee80211_channel *pch;
unsigned long irqL;
u8 *wps_ie = NULL;
uint wps_ielen = 0;
u8 *p2p_ie = NULL;
uint p2p_ielen = 0;
u8 survey_times = 3;
u8 survey_times_for_one_ch = 6;
struct cfg80211_ssid *ssids = request->ssids;
int social_channel = 0, j = 0;
bool need_indicate_scan_done = false;
bool ps_denied = false;
_adapter *padapter;
struct wireless_dev *wdev;
struct rtw_wdev_priv *pwdev_priv;
struct mlme_priv *pmlmepriv;
#ifdef CONFIG_P2P
struct wifidirect_info *pwdinfo;
#endif /* CONFIG_P2P */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
wdev = request->wdev;
#if defined(RTW_DEDICATED_P2P_DEVICE)
if (wdev == wiphy_to_pd_wdev(wiphy))
padapter = wiphy_to_adapter(wiphy);
else
#endif
if (wdev_to_ndev(wdev))
padapter = (_adapter *)rtw_netdev_priv(wdev_to_ndev(wdev));
else {
ret = -EINVAL;
goto exit;
}
#else
if (ndev == NULL) {
ret = -EINVAL;
goto exit;
}
padapter = (_adapter *)rtw_netdev_priv(ndev);
wdev = ndev_to_wdev(ndev);
#endif
pwdev_priv = adapter_wdev_data(padapter);
pmlmepriv = &padapter->mlmepriv;
#ifdef CONFIG_P2P
pwdinfo = &(padapter->wdinfo);
#endif /* CONFIG_P2P */
RTW_INFO(FUNC_ADPT_FMT"%s\n", FUNC_ADPT_ARG(padapter)
, wdev == wiphy_to_pd_wdev(wiphy) ? " PD" : "");
#ifdef CONFIG_MP_INCLUDED
if (rtw_mi_mp_mode_check(padapter)) {
RTW_INFO("MP mode block Scan request\n");
ret = -EPERM;
goto exit;
}
#endif
if (adapter_wdev_data(padapter)->block_scan ) {
RTW_INFO(FUNC_ADPT_FMT" wdev_priv.block_scan is set\n", FUNC_ADPT_ARG(padapter));
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
}
rtw_ps_deny(padapter, PS_DENY_SCAN);
ps_denied = true;
if (_FAIL == rtw_pwr_wakeup(padapter)) {
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
}
#ifdef CONFIG_P2P
if (pwdinfo->driver_interface == DRIVER_CFG80211) {
if (ssids->ssid != NULL
&& !memcmp(ssids->ssid, "DIRECT-", 7)
&& rtw_get_p2p_ie((u8 *)request->ie, request->ie_len, NULL, NULL)
) {
if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE))
rtw_p2p_enable(padapter, P2P_ROLE_DEVICE);
else {
rtw_p2p_set_pre_state(pwdinfo, rtw_p2p_state(pwdinfo));
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, role=%d, p2p_state=%d\n", __func__, rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo));
#endif
}
rtw_p2p_set_state(pwdinfo, P2P_STATE_LISTEN);
if (request->n_channels == 3 &&
request->channels[0]->hw_value == 1 &&
request->channels[1]->hw_value == 6 &&
request->channels[2]->hw_value == 11
)
social_channel = 1;
}
}
#endif /*CONFIG_P2P*/
if (request->ie && request->ie_len > 0)
rtw_cfg80211_set_probe_req_wpsp2pie(padapter, (u8 *)request->ie, request->ie_len);
if (rtw_is_scan_deny(padapter)) {
RTW_INFO(FUNC_ADPT_FMT ": scan deny\n", FUNC_ADPT_ARG(padapter));
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
}
/* check fw state*/
if (check_fwstate(pmlmepriv, WIFI_AP_STATE) ) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO(FUNC_ADPT_FMT" under WIFI_AP_STATE\n", FUNC_ADPT_ARG(padapter));
#endif
if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS | _FW_UNDER_SURVEY | _FW_UNDER_LINKING) ) {
RTW_INFO("%s, fwstate=0x%x\n", __func__, pmlmepriv->fw_state);
if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS))
RTW_INFO("AP mode process WPS\n");
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
}
}
if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) ) {
RTW_INFO("%s, fwstate=0x%x\n", __func__, pmlmepriv->fw_state);
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
} else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) ) {
RTW_INFO("%s, fwstate=0x%x\n", __func__, pmlmepriv->fw_state);
ret = -EBUSY;
goto check_need_indicate_scan_done;
}
#ifdef CONFIG_CONCURRENT_MODE
if (rtw_mi_buddy_check_fwstate(padapter, _FW_UNDER_LINKING | WIFI_UNDER_WPS)) {
RTW_INFO("%s exit due to buddy_intf's mlme state under linking or wps\n", __func__);
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
} else if (rtw_mi_buddy_check_fwstate(padapter, _FW_UNDER_SURVEY)) {
bool scan_via_buddy = rtw_cfg80211_scan_via_buddy(padapter, request);
if (scan_via_buddy == false)
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
}
#endif /* CONFIG_CONCURRENT_MODE */
/* busy traffic check*/
if (rtw_mi_busy_traffic_check(padapter, true)) {
need_indicate_scan_done = true;
goto check_need_indicate_scan_done;
}
#ifdef CONFIG_P2P
if (!rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE) && !rtw_p2p_chk_state(pwdinfo, P2P_STATE_IDLE)) {
rtw_p2p_set_state(pwdinfo, P2P_STATE_FIND_PHASE_SEARCH);
rtw_free_network_queue(padapter, true);
if (social_channel == 0)
rtw_p2p_findphase_ex_set(pwdinfo, P2P_FINDPHASE_EX_NONE);
else
rtw_p2p_findphase_ex_set(pwdinfo, P2P_FINDPHASE_EX_SOCIAL_LAST);
}
#endif /* CONFIG_P2P */
memset(ssid, 0, sizeof(NDIS_802_11_SSID) * RTW_SSID_SCAN_AMOUNT);
/* parsing request ssids, n_ssids */
for (i = 0; i < request->n_ssids && i < RTW_SSID_SCAN_AMOUNT; i++) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("ssid=%s, len=%d\n", ssids[i].ssid, ssids[i].ssid_len);
#endif
memcpy(ssid[i].Ssid, ssids[i].ssid, ssids[i].ssid_len);
ssid[i].SsidLength = ssids[i].ssid_len;
}
/* parsing channels, n_channels */
memset(ch, 0, sizeof(struct rtw_ieee80211_channel) * RTW_CHANNEL_SCAN_AMOUNT);
for (i = 0; i < request->n_channels && i < RTW_CHANNEL_SCAN_AMOUNT; i++) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO(FUNC_ADPT_FMT CHAN_FMT"\n", FUNC_ADPT_ARG(padapter), CHAN_ARG(request->channels[i]));
#endif
ch[i].hw_value = request->channels[i]->hw_value;
ch[i].flags = request->channels[i]->flags;
}
if (request->n_channels == 1) {
for (i = 1; i < survey_times_for_one_ch; i++)
memcpy(&ch[i], &ch[0], sizeof(struct rtw_ieee80211_channel));
pch = ch;
chan_num = survey_times_for_one_ch;
} else if (request->n_channels <= 4) {
for (j = request->n_channels - 1; j >= 0; j--)
for (i = 0; i < survey_times; i++)
memcpy(&ch[j * survey_times + i], &ch[j], sizeof(struct rtw_ieee80211_channel));
pch = ch;
chan_num = survey_times * request->n_channels;
} else {
pch = ch;
chan_num = request->n_channels;
}
_enter_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
_enter_critical_bh(&pmlmepriv->lock, &irqL);
_status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, pch, chan_num);
if (_status == _SUCCESS)
pwdev_priv->scan_request = request;
else
ret = -1;
_exit_critical_bh(&pmlmepriv->lock, &irqL);
_exit_critical_bh(&pwdev_priv->scan_req_lock, &irqL);
check_need_indicate_scan_done:
if (need_indicate_scan_done) {
#if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE)
struct cfg80211_scan_info info;
memset(&info, 0, sizeof(info));
info.aborted = 0;
#endif
_rtw_cfg80211_surveydone_event_callback(padapter, request);
#if (KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE)
cfg80211_scan_done(request, &info);
#else
cfg80211_scan_done(request, 0);
#endif
}
cancel_ps_deny:
if (ps_denied )
rtw_ps_deny_cancel(padapter, PS_DENY_SCAN);
exit:
return ret;
}
static int cfg80211_rtw_set_wiphy_params(struct wiphy *wiphy, u32 changed)
{
RTW_INFO("%s\n", __func__);
return 0;
}
static int rtw_cfg80211_set_wpa_version(struct security_priv *psecuritypriv, u32 wpa_version)
{
RTW_INFO("%s, wpa_version=%d\n", __func__, wpa_version);
if (!wpa_version) {
psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen;
return 0;
}
if (wpa_version & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPAPSK;
#ifdef CONFIG_WAPI_SUPPORT
if (wpa_version & NL80211_WAPI_VERSION_1)
psecuritypriv->ndisauthtype = Ndis802_11AuthModeWAPI;
#endif
return 0;
}
static int rtw_cfg80211_set_auth_type(struct security_priv *psecuritypriv,
enum nl80211_auth_type sme_auth_type)
{
RTW_INFO("%s, nl80211_auth_type=%d\n", __func__, sme_auth_type);
switch (sme_auth_type) {
case NL80211_AUTHTYPE_AUTOMATIC:
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Auto;
break;
case NL80211_AUTHTYPE_OPEN_SYSTEM:
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
if (psecuritypriv->ndisauthtype > Ndis802_11AuthModeWPA)
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
#ifdef CONFIG_WAPI_SUPPORT
if (psecuritypriv->ndisauthtype == Ndis802_11AuthModeWAPI)
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_WAPI;
#endif
break;
case NL80211_AUTHTYPE_SHARED_KEY:
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Shared;
psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled;
break;
default:
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open;
/* return -ENOTSUPP; */
}
return 0;
}
static int rtw_cfg80211_set_cipher(struct security_priv *psecuritypriv, u32 cipher, bool ucast)
{
u32 ndisencryptstatus = Ndis802_11EncryptionDisabled;
u32 *profile_cipher = ucast ? &psecuritypriv->dot11PrivacyAlgrthm :
&psecuritypriv->dot118021XGrpPrivacy;
RTW_INFO("%s, ucast=%d, cipher=0x%x\n", __func__, ucast, cipher);
if (!cipher) {
*profile_cipher = _NO_PRIVACY_;
psecuritypriv->ndisencryptstatus = ndisencryptstatus;
return 0;
}
switch (cipher) {
case IW_AUTH_CIPHER_NONE:
*profile_cipher = _NO_PRIVACY_;
ndisencryptstatus = Ndis802_11EncryptionDisabled;
#ifdef CONFIG_WAPI_SUPPORT
if (psecuritypriv->dot11PrivacyAlgrthm == _SMS4_)
*profile_cipher = _SMS4_;
#endif
break;
case WLAN_CIPHER_SUITE_WEP40:
*profile_cipher = _WEP40_;
ndisencryptstatus = Ndis802_11Encryption1Enabled;
break;
case WLAN_CIPHER_SUITE_WEP104:
*profile_cipher = _WEP104_;
ndisencryptstatus = Ndis802_11Encryption1Enabled;
break;
case WLAN_CIPHER_SUITE_TKIP:
*profile_cipher = _TKIP_;
ndisencryptstatus = Ndis802_11Encryption2Enabled;
break;
case WLAN_CIPHER_SUITE_CCMP:
*profile_cipher = _AES_;
ndisencryptstatus = Ndis802_11Encryption3Enabled;
break;
#ifdef CONFIG_WAPI_SUPPORT
case WLAN_CIPHER_SUITE_SMS4:
*profile_cipher = _SMS4_;
ndisencryptstatus = Ndis802_11_EncrypteionWAPI;
break;
#endif
default:
RTW_INFO("Unsupported cipher: 0x%x\n", cipher);
return -ENOTSUPP;
}
if (ucast) {
psecuritypriv->ndisencryptstatus = ndisencryptstatus;
/* if(psecuritypriv->dot11PrivacyAlgrthm >= _AES_) */
/* psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPA2PSK; */
}
return 0;
}
static int rtw_cfg80211_set_key_mgt(struct security_priv *psecuritypriv, u32 key_mgt)
{
RTW_INFO("%s, key_mgt=0x%x\n", __func__, key_mgt);
if (key_mgt == WLAN_AKM_SUITE_8021X) {
/* *auth_type = UMAC_AUTH_TYPE_8021X; */
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
psecuritypriv->rsn_akm_suite_type = 1;
} else if (key_mgt == WLAN_AKM_SUITE_PSK) {
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
psecuritypriv->rsn_akm_suite_type = 2;
}
#ifdef CONFIG_WAPI_SUPPORT
else if (key_mgt == WLAN_AKM_SUITE_WAPI_PSK)
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_WAPI;
else if (key_mgt == WLAN_AKM_SUITE_WAPI_CERT)
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_WAPI;
#endif
#ifdef CONFIG_RTW_80211R
else if (key_mgt == WLAN_AKM_SUITE_FT_8021X) {
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
psecuritypriv->rsn_akm_suite_type = 3;
} else if (key_mgt == WLAN_AKM_SUITE_FT_PSK) {
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
psecuritypriv->rsn_akm_suite_type = 4;
}
#endif
else {
RTW_INFO("Invalid key mgt: 0x%x\n", key_mgt);
/* return -EINVAL; */
}
return 0;
}
static int rtw_cfg80211_set_wpa_ie(_adapter *padapter, u8 *pie, size_t ielen)
{
u8 *buf = NULL, *pos = NULL;
u32 left;
int group_cipher = 0, pairwise_cipher = 0;
int ret = 0;
int wpa_ielen = 0;
int wpa2_ielen = 0;
u8 *pwpa, *pwpa2;
u8 null_addr[] = {0, 0, 0, 0, 0, 0};
if (pie == NULL || !ielen) {
/* Treat this as normal case, but need to clear WIFI_UNDER_WPS */
_clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
goto exit;
}
if (ielen > MAX_WPA_IE_LEN + MAX_WPS_IE_LEN + MAX_P2P_IE_LEN) {
ret = -EINVAL;
goto exit;
}
buf = rtw_zmalloc(ielen);
if (buf == NULL) {
ret = -ENOMEM;
goto exit;
}
memcpy(buf, pie , ielen);
/* dump */
{
int i;
RTW_INFO("set wpa_ie(length:%zu):\n", ielen);
for (i = 0; i < ielen; i = i + 8)
RTW_INFO("0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n", buf[i], buf[i + 1], buf[i + 2], buf[i + 3], buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7]);
}
pos = buf;
if (ielen < RSN_HEADER_LEN) {
ret = -1;
goto exit;
}
pwpa = rtw_get_wpa_ie(buf, &wpa_ielen, ielen);
if (pwpa && wpa_ielen > 0) {
if (rtw_parse_wpa_ie(pwpa, wpa_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPAPSK;
memcpy(padapter->securitypriv.supplicant_ie, &pwpa[0], wpa_ielen + 2);
RTW_INFO("got wpa_ie, wpa_ielen:%u\n", wpa_ielen);
}
}
pwpa2 = rtw_get_wpa2_ie(buf, &wpa2_ielen, ielen);
if (pwpa2 && wpa2_ielen > 0) {
if (rtw_parse_wpa2_ie(pwpa2, wpa2_ielen + 2, &group_cipher, &pairwise_cipher, NULL) == _SUCCESS) {
padapter->securitypriv.dot11AuthAlgrthm = dot11AuthAlgrthm_8021X;
padapter->securitypriv.ndisauthtype = Ndis802_11AuthModeWPA2PSK;
memcpy(padapter->securitypriv.supplicant_ie, &pwpa2[0], wpa2_ielen + 2);
RTW_INFO("got wpa2_ie, wpa2_ielen:%u\n", wpa2_ielen);
}
}
if (group_cipher == 0)
group_cipher = WPA_CIPHER_NONE;
if (pairwise_cipher == 0)
pairwise_cipher = WPA_CIPHER_NONE;
switch (group_cipher) {
case WPA_CIPHER_NONE:
padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
break;
case WPA_CIPHER_WEP40:
padapter->securitypriv.dot118021XGrpPrivacy = _WEP40_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
break;
case WPA_CIPHER_TKIP:
padapter->securitypriv.dot118021XGrpPrivacy = _TKIP_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled;
break;
case WPA_CIPHER_CCMP:
padapter->securitypriv.dot118021XGrpPrivacy = _AES_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled;
break;
case WPA_CIPHER_WEP104:
padapter->securitypriv.dot118021XGrpPrivacy = _WEP104_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
break;
}
switch (pairwise_cipher) {
case WPA_CIPHER_NONE:
padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled;
break;
case WPA_CIPHER_WEP40:
padapter->securitypriv.dot11PrivacyAlgrthm = _WEP40_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
break;
case WPA_CIPHER_TKIP:
padapter->securitypriv.dot11PrivacyAlgrthm = _TKIP_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption2Enabled;
break;
case WPA_CIPHER_CCMP:
padapter->securitypriv.dot11PrivacyAlgrthm = _AES_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption3Enabled;
break;
case WPA_CIPHER_WEP104:
padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_;
padapter->securitypriv.ndisencryptstatus = Ndis802_11Encryption1Enabled;
break;
}
{/* handle wps_ie */
uint wps_ielen;
u8 *wps_ie;
wps_ie = rtw_get_wps_ie(buf, ielen, NULL, &wps_ielen);
if (wps_ie && wps_ielen > 0) {
RTW_INFO("got wps_ie, wps_ielen:%u\n", wps_ielen);
padapter->securitypriv.wps_ie_len = wps_ielen < MAX_WPS_IE_LEN ? wps_ielen : MAX_WPS_IE_LEN;
memcpy(padapter->securitypriv.wps_ie, wps_ie, padapter->securitypriv.wps_ie_len);
set_fwstate(&padapter->mlmepriv, WIFI_UNDER_WPS);
} else
_clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
}
#ifdef CONFIG_P2P
{/* check p2p_ie for assoc req; */
uint p2p_ielen = 0;
u8 *p2p_ie;
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
p2p_ie = rtw_get_p2p_ie(buf, ielen, NULL, &p2p_ielen);
if (p2p_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s p2p_assoc_req_ielen=%d\n", __func__, p2p_ielen);
#endif
if (pmlmepriv->p2p_assoc_req_ie) {
u32 free_len = pmlmepriv->p2p_assoc_req_ie_len;
pmlmepriv->p2p_assoc_req_ie_len = 0;
rtw_mfree(pmlmepriv->p2p_assoc_req_ie, free_len);
pmlmepriv->p2p_assoc_req_ie = NULL;
}
pmlmepriv->p2p_assoc_req_ie = rtw_malloc(p2p_ielen);
if (pmlmepriv->p2p_assoc_req_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
goto exit;
}
memcpy(pmlmepriv->p2p_assoc_req_ie, p2p_ie, p2p_ielen);
pmlmepriv->p2p_assoc_req_ie_len = p2p_ielen;
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WFD
{
uint wfd_ielen = 0;
u8 *wfd_ie;
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
wfd_ie = rtw_get_wfd_ie(buf, ielen, NULL, &wfd_ielen);
if (wfd_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s wfd_assoc_req_ielen=%d\n", __func__, wfd_ielen);
#endif
if (rtw_mlme_update_wfd_ie_data(pmlmepriv, MLME_ASSOC_REQ_IE, wfd_ie, wfd_ielen) != _SUCCESS)
goto exit;
}
}
#endif /* CONFIG_WFD */
/* TKIP and AES disallow multicast packets until installing group key */
if (padapter->securitypriv.dot11PrivacyAlgrthm == _TKIP_
|| padapter->securitypriv.dot11PrivacyAlgrthm == _TKIP_WTMIC_
|| padapter->securitypriv.dot11PrivacyAlgrthm == _AES_)
/* WPS open need to enable multicast */
/* || check_fwstate(&padapter->mlmepriv, WIFI_UNDER_WPS) ) */
rtw_hal_set_hwreg(padapter, HW_VAR_OFF_RCR_AM, null_addr);
exit:
if (buf)
rtw_mfree(buf, ielen);
if (ret)
_clr_fwstate_(&padapter->mlmepriv, WIFI_UNDER_WPS);
return ret;
}
static int cfg80211_rtw_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_ibss_params *params)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
NDIS_802_11_SSID ndis_ssid;
struct security_priv *psecuritypriv = &padapter->securitypriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
WLAN_BSSID_EX *pnetwork = (WLAN_BSSID_EX *)(&(pmlmeinfo->network));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
struct cfg80211_chan_def *pch_def;
#endif
struct ieee80211_channel *pch;
int ret = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
pch_def = (struct cfg80211_chan_def *)(&params->chandef);
pch = (struct ieee80211_channel *) pch_def->chan;
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31))
pch = (struct ieee80211_channel *)(params->channel);
#endif
if (!params->ssid || !params->ssid_len) {
ret = -EINVAL;
goto exit;
}
if (params->ssid_len > IW_ESSID_MAX_SIZE) {
ret = -E2BIG;
goto exit;
}
if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
ret = -EPERM;
goto exit;
}
rtw_ps_deny(padapter, PS_DENY_JOIN);
if (_FAIL == rtw_pwr_wakeup(padapter)) {
ret = -EPERM;
goto cancel_ps_deny;
}
#ifdef CONFIG_CONCURRENT_MODE
if (rtw_mi_buddy_check_fwstate(padapter, _FW_UNDER_LINKING)) {
RTW_INFO("%s, but buddy_intf is under linking\n", __func__);
ret = -EINVAL;
goto cancel_ps_deny;
}
rtw_mi_buddy_scan_abort(padapter, true); /* OR rtw_mi_scan_abort(padapter, true);*/
#endif /*CONFIG_CONCURRENT_MODE*/
memset(&ndis_ssid, 0, sizeof(NDIS_802_11_SSID));
ndis_ssid.SsidLength = params->ssid_len;
memcpy(ndis_ssid.Ssid, (u8 *)params->ssid, params->ssid_len);
/* RTW_INFO("ssid=%s, len=%zu\n", ndis_ssid.Ssid, params->ssid_len); */
psecuritypriv->ndisencryptstatus = Ndis802_11EncryptionDisabled;
psecuritypriv->dot11PrivacyAlgrthm = _NO_PRIVACY_;
psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_;
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen;
ret = rtw_cfg80211_set_auth_type(psecuritypriv, NL80211_AUTHTYPE_OPEN_SYSTEM);
rtw_set_802_11_authentication_mode(padapter, psecuritypriv->ndisauthtype);
RTW_INFO("%s: center_freq = %d\n", __func__, pch->center_freq);
pmlmeext->cur_channel = rtw_freq2ch(pch->center_freq);
if (rtw_set_802_11_ssid(padapter, &ndis_ssid) == false) {
ret = -1;
goto cancel_ps_deny;
}
cancel_ps_deny:
rtw_ps_deny_cancel(padapter, PS_DENY_JOIN);
exit:
return ret;
}
static int cfg80211_rtw_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct wireless_dev *rtw_wdev = padapter->rtw_wdev;
enum nl80211_iftype old_type;
int ret = 0;
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
#ifdef SUPPLICANT_RTK_VERSION_LOWER_THAN_JB42
padapter->mlmepriv.not_indic_disco = true;
#endif
old_type = rtw_wdev->iftype;
rtw_set_to_roam(padapter, 0);
if (check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
rtw_scan_abort(padapter);
LeaveAllPowerSaveMode(padapter);
rtw_wdev->iftype = NL80211_IFTYPE_STATION;
if (rtw_set_802_11_infrastructure_mode(padapter, Ndis802_11Infrastructure) == false) {
rtw_wdev->iftype = old_type;
ret = -EPERM;
goto leave_ibss;
}
rtw_setopmode_cmd(padapter, Ndis802_11Infrastructure, true);
}
leave_ibss:
#ifdef SUPPLICANT_RTK_VERSION_LOWER_THAN_JB42
padapter->mlmepriv.not_indic_disco = false;
#endif
return 0;
}
static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_connect_params *sme)
{
int ret = 0;
unsigned long irqL;
_list *phead;
struct wlan_network *pnetwork = NULL;
NDIS_802_11_AUTHENTICATION_MODE authmode;
NDIS_802_11_SSID ndis_ssid;
u8 *dst_ssid, *src_ssid;
u8 *dst_bssid, *src_bssid;
/* u8 matched_by_bssid=false; */
/* u8 matched_by_ssid=false; */
u8 matched = false;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct security_priv *psecuritypriv = &padapter->securitypriv;
_queue *queue = &pmlmepriv->scanned_queue;
#ifdef SUPPLICANT_RTK_VERSION_LOWER_THAN_JB42
padapter->mlmepriv.not_indic_disco = true;
#endif
RTW_INFO("=>"FUNC_NDEV_FMT" - Start to Connection\n", FUNC_NDEV_ARG(ndev));
RTW_INFO("privacy=%d, key=%p, key_len=%d, key_idx=%d, auth_type=%d\n",
sme->privacy, sme->key, sme->key_len, sme->key_idx, sme->auth_type);
if (adapter_wdev_data(padapter)->block ) {
ret = -EBUSY;
RTW_INFO("%s wdev_priv.block is set\n", __func__);
goto exit;
}
#ifdef CONFIG_PLATFORM_MSTAR_SCAN_BEFORE_CONNECT
printk("MStar Android!\n");
if (adapter_wdev_data(padapter)->bandroid_scan == false) {
#ifdef CONFIG_P2P
struct wifidirect_info *pwdinfo = &(padapter->wdinfo);
if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE))
#endif /* CONFIG_P2P */
{
ret = -EBUSY;
printk("Android hasn't attached yet!\n");
goto exit;
}
}
#endif
if (!sme->ssid || !sme->ssid_len) {
ret = -EINVAL;
goto exit;
}
if (sme->ssid_len > IW_ESSID_MAX_SIZE) {
ret = -E2BIG;
goto exit;
}
rtw_ps_deny(padapter, PS_DENY_JOIN);
if (_FAIL == rtw_pwr_wakeup(padapter)) {
ret = -EPERM;
goto cancel_ps_deny;
}
if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) {
ret = -EPERM;
goto cancel_ps_deny;
}
if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) ) {
ret = -EBUSY;
RTW_INFO("%s, fw_state=0x%x, goto exit\n", __func__, pmlmepriv->fw_state);
goto cancel_ps_deny;
}
#ifdef CONFIG_CONCURRENT_MODE
if (rtw_mi_buddy_check_fwstate(padapter, _FW_UNDER_LINKING)) {
ret = -EINVAL;
goto cancel_ps_deny;
}
#endif
rtw_mi_scan_abort(padapter, true);
memset(&ndis_ssid, 0, sizeof(NDIS_802_11_SSID));
ndis_ssid.SsidLength = sme->ssid_len;
memcpy(ndis_ssid.Ssid, (u8 *)sme->ssid, sme->ssid_len);
RTW_INFO("ssid=%s, len=%zu\n", ndis_ssid.Ssid, sme->ssid_len);
if (sme->bssid)
RTW_INFO("bssid="MAC_FMT"\n", MAC_ARG(sme->bssid));
psecuritypriv->ndisencryptstatus = Ndis802_11EncryptionDisabled;
psecuritypriv->dot11PrivacyAlgrthm = _NO_PRIVACY_;
psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_;
psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_Open; /* open system */
psecuritypriv->ndisauthtype = Ndis802_11AuthModeOpen;
#ifdef CONFIG_WAPI_SUPPORT
padapter->wapiInfo.bWapiEnable = false;
#endif
ret = rtw_cfg80211_set_wpa_version(psecuritypriv, sme->crypto.wpa_versions);
if (ret < 0)
goto cancel_ps_deny;
#ifdef CONFIG_WAPI_SUPPORT
if (sme->crypto.wpa_versions & NL80211_WAPI_VERSION_1) {
padapter->wapiInfo.bWapiEnable = true;
padapter->wapiInfo.extra_prefix_len = WAPI_EXT_LEN;
padapter->wapiInfo.extra_postfix_len = SMS4_MIC_LEN;
}
#endif
ret = rtw_cfg80211_set_auth_type(psecuritypriv, sme->auth_type);
#ifdef CONFIG_WAPI_SUPPORT
if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_WAPI)
padapter->mlmeextpriv.mlmext_info.auth_algo = psecuritypriv->dot11AuthAlgrthm;
#endif
if (ret < 0)
goto cancel_ps_deny;
RTW_INFO("%s, ie_len=%zu\n", __func__, sme->ie_len);
ret = rtw_cfg80211_set_wpa_ie(padapter, (u8 *)sme->ie, sme->ie_len);
if (ret < 0)
goto cancel_ps_deny;
if (sme->crypto.n_ciphers_pairwise) {
ret = rtw_cfg80211_set_cipher(psecuritypriv, sme->crypto.ciphers_pairwise[0], true);
if (ret < 0)
goto cancel_ps_deny;
}
/* For WEP Shared auth */
if (sme->key_len > 0 && sme->key) {
u32 wep_key_idx, wep_key_len, wep_total_len;
NDIS_802_11_WEP *pwep = NULL;
RTW_INFO("%s(): Shared/Auto WEP\n", __func__);
wep_key_idx = sme->key_idx;
wep_key_len = sme->key_len;
if (sme->key_idx > WEP_KEYS) {
ret = -EINVAL;
goto cancel_ps_deny;
}
if (wep_key_len > 0) {
wep_key_len = wep_key_len <= 5 ? 5 : 13;
wep_total_len = wep_key_len + FIELD_OFFSET(NDIS_802_11_WEP, KeyMaterial);
pwep = (NDIS_802_11_WEP *) rtw_malloc(wep_total_len);
if (pwep == NULL) {
RTW_INFO(" wpa_set_encryption: pwep allocate fail !!!\n");
ret = -ENOMEM;
goto cancel_ps_deny;
}
memset(pwep, 0, wep_total_len);
pwep->KeyLength = wep_key_len;
pwep->Length = wep_total_len;
if (wep_key_len == 13) {
padapter->securitypriv.dot11PrivacyAlgrthm = _WEP104_;
padapter->securitypriv.dot118021XGrpPrivacy = _WEP104_;
}
} else {
ret = -EINVAL;
goto cancel_ps_deny;
}
pwep->KeyIndex = wep_key_idx;
pwep->KeyIndex |= 0x80000000;
memcpy(pwep->KeyMaterial, (void *)sme->key, pwep->KeyLength);
if (rtw_set_802_11_add_wep(padapter, pwep) == (u8)_FAIL)
ret = -EOPNOTSUPP ;
if (pwep)
rtw_mfree((u8 *)pwep, wep_total_len);
if (ret < 0)
goto cancel_ps_deny;
}
ret = rtw_cfg80211_set_cipher(psecuritypriv, sme->crypto.cipher_group, false);
if (ret < 0)
return ret;
if (sme->crypto.n_akm_suites) {
ret = rtw_cfg80211_set_key_mgt(psecuritypriv, sme->crypto.akm_suites[0]);
if (ret < 0)
goto cancel_ps_deny;
}
#ifdef CONFIG_8011R
else {
/*It could be a connection without RSN IEs*/
psecuritypriv->rsn_akm_suite_type = 0;
}
#endif
#ifdef CONFIG_WAPI_SUPPORT
if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_WAPI_PSK)
padapter->wapiInfo.bWapiPSK = true;
else if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_WAPI_CERT)
padapter->wapiInfo.bWapiPSK = false;
#endif
authmode = psecuritypriv->ndisauthtype;
rtw_set_802_11_authentication_mode(padapter, authmode);
/* rtw_set_802_11_encryption_mode(padapter, padapter->securitypriv.ndisencryptstatus); */
if (rtw_set_802_11_connect(padapter, (u8 *)sme->bssid, &ndis_ssid) == false) {
ret = -1;
goto cancel_ps_deny;
}
RTW_INFO("set ssid:dot11AuthAlgrthm=%d, dot11PrivacyAlgrthm=%d, dot118021XGrpPrivacy=%d\n", psecuritypriv->dot11AuthAlgrthm, psecuritypriv->dot11PrivacyAlgrthm,
psecuritypriv->dot118021XGrpPrivacy);
cancel_ps_deny:
rtw_ps_deny_cancel(padapter, PS_DENY_JOIN);
exit:
RTW_INFO("<=%s, ret %d\n", __func__, ret);
#ifdef SUPPLICANT_RTK_VERSION_LOWER_THAN_JB42
padapter->mlmepriv.not_indic_disco = false;
#endif
return ret;
}
static int cfg80211_rtw_disconnect(struct wiphy *wiphy, struct net_device *ndev,
u16 reason_code)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
RTW_INFO(FUNC_NDEV_FMT" - Start to Disconnect\n", FUNC_NDEV_ARG(ndev));
#ifdef SUPPLICANT_RTK_VERSION_LOWER_THAN_JB42
padapter->mlmepriv.not_indic_disco = true;
#endif
rtw_set_to_roam(padapter, 0);
/* if(check_fwstate(&padapter->mlmepriv, _FW_LINKED)) */
{
rtw_scan_abort(padapter);
LeaveAllPowerSaveMode(padapter);
rtw_disassoc_cmd(padapter, 500, false);
rtw_sta_mstatus_report(padapter);
RTW_INFO("%s...call rtw_indicate_disconnect\n", __func__);
rtw_free_assoc_resources(padapter, 1);
rtw_indicate_disconnect(padapter, 0, true);
rtw_pwr_wakeup(padapter);
}
#ifdef SUPPLICANT_RTK_VERSION_LOWER_THAN_JB42
padapter->mlmepriv.not_indic_disco = false;
#endif
RTW_INFO(FUNC_NDEV_FMT" return 0\n", FUNC_NDEV_ARG(ndev));
return 0;
}
static int cfg80211_rtw_set_txpower(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
struct wireless_dev *wdev,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)) || defined(COMPAT_KERNEL_RELEASE)
enum nl80211_tx_power_setting type, int mbm)
#else
enum tx_power_setting type, int dbm)
#endif
{
RTW_INFO("%s\n", __func__);
return 0;
}
static int cfg80211_rtw_get_txpower(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
struct wireless_dev *wdev,
#endif
int *dbm)
{
RTW_INFO("%s\n", __func__);
*dbm = (12);
return 0;
}
inline bool rtw_cfg80211_pwr_mgmt(_adapter *adapter)
{
struct rtw_wdev_priv *rtw_wdev_priv = adapter_wdev_data(adapter);
return rtw_wdev_priv->power_mgmt;
}
static int cfg80211_rtw_set_power_mgmt(struct wiphy *wiphy,
struct net_device *ndev,
bool enabled, int timeout)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct rtw_wdev_priv *rtw_wdev_priv = adapter_wdev_data(padapter);
RTW_INFO(FUNC_NDEV_FMT" enabled:%u, timeout:%d\n", FUNC_NDEV_ARG(ndev),
enabled, timeout);
rtw_wdev_priv->power_mgmt = enabled;
#ifdef CONFIG_LPS
if (!enabled)
rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_LEAVE_CFG80211_PWRMGMT, 1);
#endif
return 0;
}
static int cfg80211_rtw_set_pmksa(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_pmksa *pmksa)
{
u8 index, blInserted = false;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_priv *mlme = &padapter->mlmepriv;
struct security_priv *psecuritypriv = &padapter->securitypriv;
u8 strZeroMacAddress[ETH_ALEN] = { 0x00 };
RTW_INFO(FUNC_NDEV_FMT" "MAC_FMT" "KEY_FMT"\n", FUNC_NDEV_ARG(ndev)
, MAC_ARG(pmksa->bssid), KEY_ARG(pmksa->pmkid));
if (!memcmp((u8 *)pmksa->bssid, strZeroMacAddress, ETH_ALEN) )
return -EINVAL;
if (check_fwstate(mlme, _FW_LINKED) == false) {
RTW_INFO(FUNC_NDEV_FMT" not set pmksa cause not in linked state\n", FUNC_NDEV_ARG(ndev));
return -EINVAL;
}
blInserted = false;
/* overwrite PMKID */
for (index = 0 ; index < NUM_PMKID_CACHE; index++) {
if (!memcmp(psecuritypriv->PMKIDList[index].Bssid, (u8 *)pmksa->bssid, ETH_ALEN) ) {
/* BSSID is matched, the same AP => rewrite with new PMKID. */
RTW_INFO(FUNC_NDEV_FMT" BSSID exists in the PMKList.\n", FUNC_NDEV_ARG(ndev));
memcpy(psecuritypriv->PMKIDList[index].PMKID, (u8 *)pmksa->pmkid, WLAN_PMKID_LEN);
psecuritypriv->PMKIDList[index].bUsed = true;
psecuritypriv->PMKIDIndex = index + 1;
blInserted = true;
break;
}
}
if (!blInserted) {
/* Find a new entry */
RTW_INFO(FUNC_NDEV_FMT" Use the new entry index = %d for this PMKID.\n",
FUNC_NDEV_ARG(ndev), psecuritypriv->PMKIDIndex);
memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].Bssid, (u8 *)pmksa->bssid, ETH_ALEN);
memcpy(psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].PMKID, (u8 *)pmksa->pmkid, WLAN_PMKID_LEN);
psecuritypriv->PMKIDList[psecuritypriv->PMKIDIndex].bUsed = true;
psecuritypriv->PMKIDIndex++ ;
if (psecuritypriv->PMKIDIndex == 16)
psecuritypriv->PMKIDIndex = 0;
}
return 0;
}
static int cfg80211_rtw_del_pmksa(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_pmksa *pmksa)
{
u8 index, bMatched = false;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct security_priv *psecuritypriv = &padapter->securitypriv;
RTW_INFO(FUNC_NDEV_FMT" "MAC_FMT" "KEY_FMT"\n", FUNC_NDEV_ARG(ndev)
, MAC_ARG(pmksa->bssid), KEY_ARG(pmksa->pmkid));
for (index = 0 ; index < NUM_PMKID_CACHE; index++) {
if (!memcmp(psecuritypriv->PMKIDList[index].Bssid, (u8 *)pmksa->bssid, ETH_ALEN) ) {
/* BSSID is matched, the same AP => Remove this PMKID information and reset it. */
memset(psecuritypriv->PMKIDList[index].Bssid, 0x00, ETH_ALEN);
memset(psecuritypriv->PMKIDList[index].PMKID, 0x00, WLAN_PMKID_LEN);
psecuritypriv->PMKIDList[index].bUsed = false;
bMatched = true;
RTW_INFO(FUNC_NDEV_FMT" clear id:%hhu\n", FUNC_NDEV_ARG(ndev), index);
break;
}
}
if (false == bMatched) {
RTW_INFO(FUNC_NDEV_FMT" do not have matched BSSID\n"
, FUNC_NDEV_ARG(ndev));
return -EINVAL;
}
return 0;
}
static int cfg80211_rtw_flush_pmksa(struct wiphy *wiphy,
struct net_device *ndev)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct security_priv *psecuritypriv = &padapter->securitypriv;
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
memset(&psecuritypriv->PMKIDList[0], 0x00, sizeof(RT_PMKID_LIST) * NUM_PMKID_CACHE);
psecuritypriv->PMKIDIndex = 0;
return 0;
}
#ifdef CONFIG_AP_MODE
void rtw_cfg80211_indicate_sta_assoc(_adapter *padapter, u8 *pmgmt_frame, uint frame_len)
{
s32 freq;
int channel;
struct wireless_dev *pwdev = padapter->rtw_wdev;
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
struct net_device *ndev = padapter->pnetdev;
RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter));
#if defined(RTW_USE_CFG80211_STA_EVENT) || defined(COMPAT_KERNEL_RELEASE)
{
struct station_info sinfo;
u8 ie_offset;
if (get_frame_sub_type(pmgmt_frame) == WIFI_ASSOCREQ)
ie_offset = _ASOCREQ_IE_OFFSET_;
else /* WIFI_REASSOCREQ */
ie_offset = _REASOCREQ_IE_OFFSET_;
memset(&sinfo, 0, sizeof(sinfo));
sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
sinfo.assoc_req_ies = pmgmt_frame + WLAN_HDR_A3_LEN + ie_offset;
sinfo.assoc_req_ies_len = frame_len - WLAN_HDR_A3_LEN - ie_offset;
cfg80211_new_sta(ndev, get_addr2_ptr(pmgmt_frame), &sinfo, GFP_ATOMIC);
}
#else /* defined(RTW_USE_CFG80211_STA_EVENT) */
channel = pmlmeext->cur_channel;
freq = rtw_ch2freq(channel);
#ifdef COMPAT_KERNEL_RELEASE
rtw_cfg80211_rx_mgmt(pwdev, freq, 0, pmgmt_frame, frame_len, GFP_ATOMIC);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) && !defined(CONFIG_CFG80211_FORCE_COMPATIBLE_2_6_37_UNDER)
rtw_cfg80211_rx_mgmt(pwdev, freq, 0, pmgmt_frame, frame_len, GFP_ATOMIC);
#else /* COMPAT_KERNEL_RELEASE */
{
/* to avoid WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION) when calling cfg80211_send_rx_assoc() */
#ifndef CONFIG_PLATFORM_MSTAR
pwdev->iftype = NL80211_IFTYPE_STATION;
#endif /* CONFIG_PLATFORM_MSTAR */
RTW_INFO("iftype=%d before call cfg80211_send_rx_assoc()\n", pwdev->iftype);
rtw_cfg80211_send_rx_assoc(padapter, NULL, pmgmt_frame, frame_len);
RTW_INFO("iftype=%d after call cfg80211_send_rx_assoc()\n", pwdev->iftype);
pwdev->iftype = NL80211_IFTYPE_AP;
/* cfg80211_rx_action(padapter->pnetdev, freq, pmgmt_frame, frame_len, GFP_ATOMIC); */
}
#endif /* COMPAT_KERNEL_RELEASE */
#endif /* defined(RTW_USE_CFG80211_STA_EVENT) */
}
void rtw_cfg80211_indicate_sta_disassoc(_adapter *padapter, unsigned char *da, unsigned short reason)
{
s32 freq;
int channel;
u8 *pmgmt_frame;
uint frame_len;
struct rtw_ieee80211_hdr *pwlanhdr;
unsigned short *fctrl;
u8 mgmt_buf[128] = {0};
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
struct wireless_dev *wdev = padapter->rtw_wdev;
struct net_device *ndev = padapter->pnetdev;
RTW_INFO(FUNC_ADPT_FMT"\n", FUNC_ADPT_ARG(padapter));
#if defined(RTW_USE_CFG80211_STA_EVENT) || defined(COMPAT_KERNEL_RELEASE)
cfg80211_del_sta(ndev, da, GFP_ATOMIC);
#else /* defined(RTW_USE_CFG80211_STA_EVENT) */
channel = pmlmeext->cur_channel;
freq = rtw_ch2freq(channel);
pmgmt_frame = mgmt_buf;
pwlanhdr = (struct rtw_ieee80211_hdr *)pmgmt_frame;
fctrl = &(pwlanhdr->frame_ctl);
*(fctrl) = 0;
memcpy(pwlanhdr->addr1, adapter_mac_addr(padapter), ETH_ALEN);
memcpy(pwlanhdr->addr2, da, ETH_ALEN);
memcpy(pwlanhdr->addr3, get_my_bssid(&(pmlmeinfo->network)), ETH_ALEN);
SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
pmlmeext->mgnt_seq++;
set_frame_sub_type(pmgmt_frame, WIFI_DEAUTH);
pmgmt_frame += sizeof(struct rtw_ieee80211_hdr_3addr);
frame_len = sizeof(struct rtw_ieee80211_hdr_3addr);
reason = cpu_to_le16(reason);
pmgmt_frame = rtw_set_fixed_ie(pmgmt_frame, _RSON_CODE_ , (unsigned char *)&reason, &frame_len);
#ifdef COMPAT_KERNEL_RELEASE
rtw_cfg80211_rx_mgmt(wdev, freq, 0, mgmt_buf, frame_len, GFP_ATOMIC);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) && !defined(CONFIG_CFG80211_FORCE_COMPATIBLE_2_6_37_UNDER)
rtw_cfg80211_rx_mgmt(wdev, freq, 0, mgmt_buf, frame_len, GFP_ATOMIC);
#else /* COMPAT_KERNEL_RELEASE */
cfg80211_send_disassoc(padapter->pnetdev, mgmt_buf, frame_len);
/* cfg80211_rx_action(padapter->pnetdev, freq, mgmt_buf, frame_len, GFP_ATOMIC); */
#endif /* COMPAT_KERNEL_RELEASE */
#endif /* defined(RTW_USE_CFG80211_STA_EVENT) */
}
static int rtw_cfg80211_monitor_if_open(struct net_device *ndev)
{
int ret = 0;
RTW_INFO("%s\n", __func__);
return ret;
}
static int rtw_cfg80211_monitor_if_close(struct net_device *ndev)
{
int ret = 0;
RTW_INFO("%s\n", __func__);
return ret;
}
static int rtw_cfg80211_monitor_if_xmit_entry(struct sk_buff *skb, struct net_device *ndev)
{
int ret = 0;
int rtap_len;
int qos_len = 0;
int dot11_hdr_len = 24;
int snap_len = 6;
unsigned char *pdata;
u16 frame_ctl;
unsigned char src_mac_addr[6];
unsigned char dst_mac_addr[6];
struct rtw_ieee80211_hdr *dot11_hdr;
struct ieee80211_radiotap_header *rtap_hdr;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
if (skb)
rtw_mstat_update(MSTAT_TYPE_SKB, MSTAT_ALLOC_SUCCESS, skb->truesize);
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
goto fail;
rtap_hdr = (struct ieee80211_radiotap_header *)skb->data;
if (unlikely(rtap_hdr->it_version))
goto fail;
rtap_len = ieee80211_get_radiotap_len(skb->data);
if (unlikely(skb->len < rtap_len))
goto fail;
if (rtap_len != 14) {
RTW_INFO("radiotap len (should be 14): %d\n", rtap_len);
goto fail;
}
/* Skip the ratio tap header */
skb_pull(skb, rtap_len);
dot11_hdr = (struct rtw_ieee80211_hdr *)skb->data;
frame_ctl = le16_to_cpu(dot11_hdr->frame_ctl);
/* Check if the QoS bit is set */
if ((frame_ctl & RTW_IEEE80211_FCTL_FTYPE) == RTW_IEEE80211_FTYPE_DATA) {
/* Check if this ia a Wireless Distribution System (WDS) frame
* which has 4 MAC addresses
*/
if (le16_to_cpu(dot11_hdr->frame_ctl) & 0x0080)
qos_len = 2;
if ((le16_to_cpu(dot11_hdr->frame_ctl) & 0x0300) == 0x0300)
dot11_hdr_len += 6;
memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr));
memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr));
/* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for
* for two MAC addresses
*/
skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2);
pdata = (unsigned char *)skb->data;
memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr));
memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr));
RTW_INFO("should be eapol packet\n");
/* Use the real net device to transmit the packet */
ret = _rtw_xmit_entry(skb, padapter->pnetdev);
return ret;
} else if ((frame_ctl & (RTW_IEEE80211_FCTL_FTYPE | RTW_IEEE80211_FCTL_STYPE))
== (RTW_IEEE80211_FTYPE_MGMT | RTW_IEEE80211_STYPE_ACTION)
) {
/* only for action frames */
struct xmit_frame *pmgntframe;
struct pkt_attrib *pattrib;
unsigned char *pframe;
/* u8 category, action, OUI_Subtype, dialogToken=0; */
/* unsigned char *frame_body; */
struct rtw_ieee80211_hdr *pwlanhdr;
struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
u8 *buf = skb->data;
u32 len = skb->len;
u8 category, action;
int type = -1;
if (rtw_action_frame_parse(buf, len, &category, &action) == false) {
RTW_INFO(FUNC_NDEV_FMT" frame_control:0x%x\n", FUNC_NDEV_ARG(ndev),
le16_to_cpu(((struct rtw_ieee80211_hdr_3addr *)buf)->frame_ctl));
goto fail;
}
RTW_INFO("RTW_Tx:da="MAC_FMT" via "FUNC_NDEV_FMT"\n",
MAC_ARG(GetAddr1Ptr(buf)), FUNC_NDEV_ARG(ndev));
#ifdef CONFIG_P2P
type = rtw_p2p_check_frames(padapter, buf, len, true);
if (type >= 0)
goto dump;
#endif
if (category == RTW_WLAN_CATEGORY_PUBLIC)
RTW_INFO("RTW_Tx:%s\n", action_public_str(action));
else
RTW_INFO("RTW_Tx:category(%u), action(%u)\n", category, action);
dump:
/* starting alloc mgmt frame to dump it */
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
if (pmgntframe == NULL)
goto fail;
/* update attribute */
pattrib = &pmgntframe->attrib;
update_mgntframe_attrib(padapter, pattrib);
pattrib->retry_ctrl = false;
memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
memcpy(pframe, (void *)buf, len);
pattrib->pktlen = len;
#ifdef CONFIG_P2P
if (type >= 0)
rtw_xframe_chk_wfd_ie(pmgntframe);
#endif /* CONFIG_P2P */
pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
/* update seq number */
pmlmeext->mgnt_seq = GetSequence(pwlanhdr);
pattrib->seqnum = pmlmeext->mgnt_seq;
pmlmeext->mgnt_seq++;
pattrib->last_txcmdsz = pattrib->pktlen;
dump_mgntframe(padapter, pmgntframe);
} else
RTW_INFO("frame_ctl=0x%x\n", frame_ctl & (RTW_IEEE80211_FCTL_FTYPE | RTW_IEEE80211_FCTL_STYPE));
fail:
rtw_skb_free(skb);
return 0;
}
static void rtw_cfg80211_monitor_if_set_multicast_list(struct net_device *ndev)
{
RTW_INFO("%s\n", __func__);
}
static int rtw_cfg80211_monitor_if_set_mac_address(struct net_device *ndev, void *addr)
{
int ret = 0;
RTW_INFO("%s\n", __func__);
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
static const struct net_device_ops rtw_cfg80211_monitor_if_ops = {
.ndo_open = rtw_cfg80211_monitor_if_open,
.ndo_stop = rtw_cfg80211_monitor_if_close,
.ndo_start_xmit = rtw_cfg80211_monitor_if_xmit_entry,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
.ndo_set_multicast_list = rtw_cfg80211_monitor_if_set_multicast_list,
#endif
.ndo_set_mac_address = rtw_cfg80211_monitor_if_set_mac_address,
};
#endif
static int rtw_cfg80211_add_monitor_if(_adapter *padapter, char *name, struct net_device **ndev)
{
int ret = 0;
struct net_device *mon_ndev = NULL;
struct wireless_dev *mon_wdev = NULL;
struct rtw_netdev_priv_indicator *pnpi;
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter);
if (!name) {
RTW_INFO(FUNC_ADPT_FMT" without specific name\n", FUNC_ADPT_ARG(padapter));
ret = -EINVAL;
goto out;
}
if (pwdev_priv->pmon_ndev) {
RTW_INFO(FUNC_ADPT_FMT" monitor interface exist: "NDEV_FMT"\n",
FUNC_ADPT_ARG(padapter), NDEV_ARG(pwdev_priv->pmon_ndev));
ret = -EBUSY;
goto out;
}
mon_ndev = alloc_etherdev(sizeof(struct rtw_netdev_priv_indicator));
if (!mon_ndev) {
RTW_INFO(FUNC_ADPT_FMT" allocate ndev fail\n", FUNC_ADPT_ARG(padapter));
ret = -ENOMEM;
goto out;
}
mon_ndev->type = ARPHRD_IEEE80211_RADIOTAP;
strncpy(mon_ndev->name, name, IFNAMSIZ);
mon_ndev->name[IFNAMSIZ - 1] = 0;
#if (LINUX_VERSION_CODE>=KERNEL_VERSION(4, 11, 9))
mon_ndev->needs_free_netdev = false;
mon_ndev->priv_destructor = rtw_ndev_destructor;
#else
mon_ndev->destructor = rtw_ndev_destructor;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29))
mon_ndev->netdev_ops = &rtw_cfg80211_monitor_if_ops;
#else
mon_ndev->open = rtw_cfg80211_monitor_if_open;
mon_ndev->stop = rtw_cfg80211_monitor_if_close;
mon_ndev->hard_start_xmit = rtw_cfg80211_monitor_if_xmit_entry;
mon_ndev->set_mac_address = rtw_cfg80211_monitor_if_set_mac_address;
#endif
pnpi = netdev_priv(mon_ndev);
pnpi->priv = padapter;
pnpi->sizeof_priv = sizeof(_adapter);
/* wdev */
mon_wdev = (struct wireless_dev *)rtw_zmalloc(sizeof(struct wireless_dev));
if (!mon_wdev) {
RTW_INFO(FUNC_ADPT_FMT" allocate mon_wdev fail\n", FUNC_ADPT_ARG(padapter));
ret = -ENOMEM;
goto out;
}
mon_wdev->wiphy = padapter->rtw_wdev->wiphy;
mon_wdev->netdev = mon_ndev;
mon_wdev->iftype = NL80211_IFTYPE_MONITOR;
mon_ndev->ieee80211_ptr = mon_wdev;
ret = register_netdevice(mon_ndev);
if (ret)
goto out;
*ndev = pwdev_priv->pmon_ndev = mon_ndev;
memcpy(pwdev_priv->ifname_mon, name, IFNAMSIZ + 1);
out:
if (ret && mon_wdev) {
rtw_mfree((u8 *)mon_wdev, sizeof(struct wireless_dev));
mon_wdev = NULL;
}
if (ret && mon_ndev) {
free_netdev(mon_ndev);
*ndev = mon_ndev = NULL;
}
return ret;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
static struct wireless_dev *
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) || defined(COMPAT_KERNEL_RELEASE)
static struct net_device *
#else
static int
#endif
cfg80211_rtw_add_virtual_intf(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
const char *name,
#else
char *name,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
unsigned char name_assign_type,
#endif
enum nl80211_iftype type,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
u32 *flags,
#endif
struct vif_params *params)
{
int ret = 0;
struct wireless_dev *wdev = NULL;
struct net_device *ndev = NULL;
_adapter *padapter;
struct dvobj_priv *dvobj = wiphy_to_dvobj(wiphy);
rtw_set_rtnl_lock_holder(dvobj, current);
RTW_INFO(FUNC_WIPHY_FMT" name:%s, type:%d\n", FUNC_WIPHY_ARG(wiphy), name, type);
switch (type) {
case NL80211_IFTYPE_MONITOR:
padapter = wiphy_to_adapter(wiphy); /* TODO: get ap iface ? */
ret = rtw_cfg80211_add_monitor_if(padapter, (char *)name, &ndev);
if (ret == 0)
wdev = ndev->ieee80211_ptr;
break;
#if defined(CONFIG_P2P) && ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE))
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
#endif
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_AP:
padapter = dvobj_get_unregisterd_adapter(dvobj);
if (!padapter) {
RTW_WARN("adapter pool empty!\n");
ret = -ENODEV;
break;
}
if (rtw_os_ndev_init(padapter, name) != _SUCCESS) {
RTW_WARN("ndev init fail!\n");
ret = -ENODEV;
break;
}
#if defined(CONFIG_P2P) && ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE))
if (type == NL80211_IFTYPE_P2P_CLIENT || type == NL80211_IFTYPE_P2P_GO)
rtw_p2p_enable(padapter, P2P_ROLE_DEVICE);
#endif
ndev = padapter->pnetdev;
wdev = ndev->ieee80211_ptr;
break;
#if defined(CONFIG_P2P) && defined(RTW_DEDICATED_P2P_DEVICE)
case NL80211_IFTYPE_P2P_DEVICE:
ret = rtw_pd_iface_alloc(wiphy, name, &wdev);
break;
#endif
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
default:
ret = -ENODEV;
RTW_INFO("Unsupported interface type\n");
break;
}
if (ndev)
RTW_INFO(FUNC_WIPHY_FMT" ndev:%p, ret:%d\n", FUNC_WIPHY_ARG(wiphy), ndev, ret);
else
RTW_INFO(FUNC_WIPHY_FMT" wdev:%p, ret:%d\n", FUNC_WIPHY_ARG(wiphy), wdev, ret);
rtw_set_rtnl_lock_holder(dvobj, NULL);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
return wdev ? wdev : ERR_PTR(ret);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) || defined(COMPAT_KERNEL_RELEASE)
return ndev ? ndev : ERR_PTR(ret);
#else
return ret;
#endif
}
static int cfg80211_rtw_del_virtual_intf(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
struct wireless_dev *wdev
#else
struct net_device *ndev
#endif
)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
struct net_device *ndev = wdev_to_ndev(wdev);
#endif
int ret = 0;
struct dvobj_priv *dvobj = wiphy_to_dvobj(wiphy);
_adapter *adapter;
struct rtw_wdev_priv *pwdev_priv;
rtw_set_rtnl_lock_holder(dvobj, current);
if (ndev) {
adapter = (_adapter *)rtw_netdev_priv(ndev);
pwdev_priv = adapter_wdev_data(adapter);
if (ndev == pwdev_priv->pmon_ndev) {
unregister_netdevice(ndev);
pwdev_priv->pmon_ndev = NULL;
pwdev_priv->ifname_mon[0] = '\0';
RTW_INFO(FUNC_NDEV_FMT" remove monitor ndev\n", FUNC_NDEV_ARG(ndev));
} else {
RTW_INFO(FUNC_NDEV_FMT" unregister ndev\n", FUNC_NDEV_ARG(ndev));
rtw_os_ndev_unregister(adapter);
}
} else
#if defined(CONFIG_P2P) && defined(RTW_DEDICATED_P2P_DEVICE)
if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE) {
if (wdev == wiphy_to_pd_wdev(wiphy))
rtw_pd_iface_free(wiphy);
else {
RTW_ERR(FUNC_WIPHY_FMT" unknown P2P Device wdev:%p\n", FUNC_WIPHY_ARG(wiphy), wdev);
rtw_warn_on(1);
}
} else
#endif
{
ret = -EINVAL;
goto exit;
}
exit:
rtw_set_rtnl_lock_holder(dvobj, NULL);
return ret;
}
static int rtw_add_beacon(_adapter *adapter, const u8 *head, size_t head_len, const u8 *tail, size_t tail_len)
{
int ret = 0;
u8 *pbuf = NULL;
uint len, wps_ielen = 0;
uint p2p_ielen = 0;
u8 *p2p_ie;
u8 got_p2p_ie = false;
struct mlme_priv *pmlmepriv = &(adapter->mlmepriv);
/* struct sta_priv *pstapriv = &padapter->stapriv; */
RTW_INFO("%s beacon_head_len=%zu, beacon_tail_len=%zu\n", __func__, head_len, tail_len);
if (check_fwstate(pmlmepriv, WIFI_AP_STATE) != true)
return -EINVAL;
if (head_len < 24)
return -EINVAL;
pbuf = rtw_zmalloc(head_len + tail_len);
if (!pbuf)
return -ENOMEM;
/* memcpy(&pstapriv->max_num_sta, param->u.bcn_ie.reserved, 2); */
/* if((pstapriv->max_num_sta>NUM_STA) || (pstapriv->max_num_sta<=0)) */
/* pstapriv->max_num_sta = NUM_STA; */
memcpy(pbuf, (void *)head + 24, head_len - 24); /* 24=beacon header len. */
memcpy(pbuf + head_len - 24, (void *)tail, tail_len);
len = head_len + tail_len - 24;
/* check wps ie if inclued */
if (rtw_get_wps_ie(pbuf + _FIXED_IE_LENGTH_, len - _FIXED_IE_LENGTH_, NULL, &wps_ielen))
RTW_INFO("add bcn, wps_ielen=%d\n", wps_ielen);
#ifdef CONFIG_P2P
if (adapter->wdinfo.driver_interface == DRIVER_CFG80211) {
/* check p2p if enable */
if (rtw_get_p2p_ie(pbuf + _FIXED_IE_LENGTH_, len - _FIXED_IE_LENGTH_, NULL, &p2p_ielen)) {
struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
struct wifidirect_info *pwdinfo = &(adapter->wdinfo);
RTW_INFO("got p2p_ie, len=%d\n", p2p_ielen);
got_p2p_ie = true;
if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) {
RTW_INFO("Enable P2P function for the first time\n");
rtw_p2p_enable(adapter, P2P_ROLE_GO);
adapter->stapriv.expire_to = 3; /* 3x2 = 6 sec in p2p mode */
} else {
RTW_INFO("enter GO Mode, p2p_ielen=%d\n", p2p_ielen);
rtw_p2p_set_role(pwdinfo, P2P_ROLE_GO);
rtw_p2p_set_state(pwdinfo, P2P_STATE_GONEGO_OK);
pwdinfo->intent = 15;
}
}
}
#endif /* CONFIG_P2P */
/* pbss_network->IEs will not include p2p_ie, wfd ie */
rtw_ies_remove_ie(pbuf, &len, _BEACON_IE_OFFSET_, _VENDOR_SPECIFIC_IE_, P2P_OUI, 4);
rtw_ies_remove_ie(pbuf, &len, _BEACON_IE_OFFSET_, _VENDOR_SPECIFIC_IE_, WFD_OUI, 4);
if (rtw_check_beacon_data(adapter, pbuf, len) == _SUCCESS) {
#ifdef CONFIG_P2P
/* check p2p if enable */
if (got_p2p_ie ) {
struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv;
struct wifidirect_info *pwdinfo = &(adapter->wdinfo);
pwdinfo->operating_channel = pmlmeext->cur_channel;
}
#endif /* CONFIG_P2P */
ret = 0;
} else
ret = -EINVAL;
rtw_mfree(pbuf, head_len + tail_len);
return ret;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) && !defined(COMPAT_KERNEL_RELEASE)
static int cfg80211_rtw_add_beacon(struct wiphy *wiphy, struct net_device *ndev,
struct beacon_parameters *info)
{
int ret = 0;
_adapter *adapter = (_adapter *)rtw_netdev_priv(ndev);
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
ret = rtw_add_beacon(adapter, info->head, info->head_len, info->tail, info->tail_len);
return ret;
}
static int cfg80211_rtw_set_beacon(struct wiphy *wiphy, struct net_device *ndev,
struct beacon_parameters *info)
{
_adapter *adapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_ext_priv *pmlmeext = &(adapter->mlmeextpriv);
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
pmlmeext->bstart_bss = true;
cfg80211_rtw_add_beacon(wiphy, ndev, info);
return 0;
}
static int cfg80211_rtw_del_beacon(struct wiphy *wiphy, struct net_device *ndev)
{
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
return 0;
}
#else
static int cfg80211_rtw_start_ap(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_ap_settings *settings)
{
int ret = 0;
_adapter *adapter = (_adapter *)rtw_netdev_priv(ndev);
RTW_INFO(FUNC_NDEV_FMT" hidden_ssid:%d, auth_type:%d\n", FUNC_NDEV_ARG(ndev),
settings->hidden_ssid, settings->auth_type);
ret = rtw_add_beacon(adapter, settings->beacon.head, settings->beacon.head_len,
settings->beacon.tail, settings->beacon.tail_len);
adapter->mlmeextpriv.mlmext_info.hidden_ssid_mode = settings->hidden_ssid;
if (settings->ssid && settings->ssid_len) {
WLAN_BSSID_EX *pbss_network = &adapter->mlmepriv.cur_network.network;
WLAN_BSSID_EX *pbss_network_ext = &adapter->mlmeextpriv.mlmext_info.network;
if (0)
RTW_INFO(FUNC_ADPT_FMT" ssid:(%s,%zu), from ie:(%s,%d)\n", FUNC_ADPT_ARG(adapter),
settings->ssid, settings->ssid_len,
pbss_network->Ssid.Ssid, pbss_network->Ssid.SsidLength);
memcpy(pbss_network->Ssid.Ssid, (void *)settings->ssid, settings->ssid_len);
pbss_network->Ssid.SsidLength = settings->ssid_len;
memcpy(pbss_network_ext->Ssid.Ssid, (void *)settings->ssid, settings->ssid_len);
pbss_network_ext->Ssid.SsidLength = settings->ssid_len;
if (0)
RTW_INFO(FUNC_ADPT_FMT" after ssid:(%s,%d), (%s,%d)\n", FUNC_ADPT_ARG(adapter),
pbss_network->Ssid.Ssid, pbss_network->Ssid.SsidLength,
pbss_network_ext->Ssid.Ssid, pbss_network_ext->Ssid.SsidLength);
}
return ret;
}
static int cfg80211_rtw_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_beacon_data *info)
{
int ret = 0;
_adapter *adapter = (_adapter *)rtw_netdev_priv(ndev);
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
ret = rtw_add_beacon(adapter, info->head, info->head_len, info->tail, info->tail_len);
return ret;
}
static int cfg80211_rtw_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
{
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
return 0;
}
#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) */
#if CONFIG_RTW_MACADDR_ACL && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
static int cfg80211_rtw_set_mac_acl(struct wiphy *wiphy, struct net_device *ndev,
const struct cfg80211_acl_data *params)
{
_adapter *adapter = (_adapter *)rtw_netdev_priv(ndev);
u8 acl_mode = RTW_ACL_MODE_DISABLED;
int ret = -1;
int i;
if (!params) {
RTW_WARN(FUNC_ADPT_FMT" params NULL\n", FUNC_ADPT_ARG(adapter));
goto exit;
}
RTW_INFO(FUNC_ADPT_FMT" acl_policy:%d, entry_num:%d\n"
, FUNC_ADPT_ARG(adapter), params->acl_policy, params->n_acl_entries);
if (params->acl_policy == NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED)
acl_mode = RTW_ACL_MODE_ACCEPT_UNLESS_LISTED;
else if (params->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED)
acl_mode = RTW_ACL_MODE_DENY_UNLESS_LISTED;
if (!params->n_acl_entries) {
if (acl_mode != RTW_ACL_MODE_DISABLED)
RTW_WARN(FUNC_ADPT_FMT" acl_policy:%d with no entry\n"
, FUNC_ADPT_ARG(adapter), params->acl_policy);
acl_mode = RTW_ACL_MODE_DISABLED;
goto exit;
}
for (i = 0; i < params->n_acl_entries; i++)
rtw_acl_add_sta(adapter, params->mac_addrs[i].addr);
ret = 0;
exit:
rtw_set_macaddr_acl(adapter, acl_mode);
return ret;
}
#endif /* CONFIG_RTW_MACADDR_ACL && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)) */
static int cfg80211_rtw_add_station(struct wiphy *wiphy, struct net_device *ndev,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
u8 *mac,
#else
const u8 *mac,
#endif
struct station_parameters *params)
{
int ret = 0;
#ifdef CONFIG_TDLS
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct sta_priv *pstapriv = &padapter->stapriv;
struct sta_info *psta;
#endif /* CONFIG_TDLS */
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
#ifdef CONFIG_TDLS
psta = rtw_get_stainfo(pstapriv, (u8 *)mac);
if (psta == NULL) {
psta = rtw_alloc_stainfo(pstapriv, (u8 *)mac);
if (psta == NULL) {
RTW_INFO("[%s] Alloc station for "MAC_FMT" fail\n", __func__, MAC_ARG(mac));
ret = -EOPNOTSUPP;
goto exit;
}
}
#endif /* CONFIG_TDLS */
exit:
return ret;
}
static int cfg80211_rtw_del_station(struct wiphy *wiphy, struct net_device *ndev,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
u8 *mac
#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0))
const u8 *mac
#else
struct station_del_parameters *params
#endif
)
{
int ret = 0;
unsigned long irqL;
_list *phead, *plist;
u8 updated = false;
const u8 *target_mac;
struct sta_info *psta = NULL;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
struct sta_priv *pstapriv = &padapter->stapriv;
RTW_INFO("+"FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0))
target_mac = mac;
#else
target_mac = params->mac;
#endif
if (check_fwstate(pmlmepriv, (_FW_LINKED | WIFI_AP_STATE)) != true) {
RTW_INFO("%s, fw_state != FW_LINKED|WIFI_AP_STATE\n", __func__);
return -EINVAL;
}
if (!target_mac) {
RTW_INFO("flush all sta, and cam_entry\n");
flush_all_cam_entry(padapter); /* clear CAM */
ret = rtw_sta_flush(padapter, true);
return ret;
}
RTW_INFO("free sta macaddr =" MAC_FMT "\n", MAC_ARG(target_mac));
if (target_mac[0] == 0xff && target_mac[1] == 0xff &&
target_mac[2] == 0xff && target_mac[3] == 0xff &&
target_mac[4] == 0xff && target_mac[5] == 0xff)
return -EINVAL;
_enter_critical_bh(&pstapriv->asoc_list_lock, &irqL);
phead = &pstapriv->asoc_list;
plist = get_next(phead);
/* check asoc_queue */
while ((rtw_end_of_queue_search(phead, plist)) == false) {
psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list);
plist = get_next(plist);
if (!memcmp((u8 *)target_mac, psta->hwaddr, ETH_ALEN)) {
if (psta->dot8021xalg == 1 && psta->bpairwise_key_installed == false)
RTW_INFO("%s, sta's dot8021xalg = 1 and key_installed = false\n", __func__);
else {
RTW_INFO("free psta=%p, aid=%d\n", psta, psta->aid);
list_del_init(&psta->asoc_list);
pstapriv->asoc_list_cnt--;
/* _exit_critical_bh(&pstapriv->asoc_list_lock, &irqL); */
if (check_fwstate(pmlmepriv, (WIFI_AP_STATE)) )
updated = ap_free_sta(padapter, psta, true, WLAN_REASON_PREV_AUTH_NOT_VALID, true);
else
updated = ap_free_sta(padapter, psta, true, WLAN_REASON_DEAUTH_LEAVING, true);
/* _enter_critical_bh(&pstapriv->asoc_list_lock, &irqL); */
psta = NULL;
break;
}
}
}
_exit_critical_bh(&pstapriv->asoc_list_lock, &irqL);
associated_clients_update(padapter, updated, STA_INFO_UPDATE_ALL);
RTW_INFO("-"FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
return ret;
}
static int cfg80211_rtw_change_station(struct wiphy *wiphy, struct net_device *ndev,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0))
u8 *mac,
#else
const u8 *mac,
#endif
struct station_parameters *params)
{
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
return 0;
}
static struct sta_info *rtw_sta_info_get_by_idx(const int idx, struct sta_priv *pstapriv)
{
_list *phead, *plist;
struct sta_info *psta = NULL;
int i = 0;
phead = &pstapriv->asoc_list;
plist = get_next(phead);
/* check asoc_queue */
while ((rtw_end_of_queue_search(phead, plist)) == false) {
if (idx == i)
psta = LIST_CONTAINOR(plist, struct sta_info, asoc_list);
plist = get_next(plist);
i++;
}
return psta;
}
static int cfg80211_rtw_dump_station(struct wiphy *wiphy, struct net_device *ndev,
int idx, u8 *mac, struct station_info *sinfo)
{
int ret = 0;
unsigned long irqL;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct sta_info *psta = NULL;
struct sta_priv *pstapriv = &padapter->stapriv;
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
_enter_critical_bh(&pstapriv->asoc_list_lock, &irqL);
psta = rtw_sta_info_get_by_idx(idx, pstapriv);
_exit_critical_bh(&pstapriv->asoc_list_lock, &irqL);
if (NULL == psta) {
RTW_INFO("Station is not found\n");
ret = -ENOENT;
goto exit;
}
memcpy(mac, psta->hwaddr, ETH_ALEN);
sinfo->filled = 0;
sinfo->filled |= STATION_INFO_SIGNAL;
sinfo->signal = psta->rssi;
exit:
return ret;
}
static int cfg80211_rtw_change_bss(struct wiphy *wiphy, struct net_device *ndev,
struct bss_parameters *params)
{
u8 i;
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
/*
RTW_INFO("use_cts_prot=%d\n", params->use_cts_prot);
RTW_INFO("use_short_preamble=%d\n", params->use_short_preamble);
RTW_INFO("use_short_slot_time=%d\n", params->use_short_slot_time);
RTW_INFO("ap_isolate=%d\n", params->ap_isolate);
RTW_INFO("basic_rates_len=%d\n", params->basic_rates_len);
for(i = 0; i < params->basic_rates_len; i++)
RTW_INFO("basic_rates=%d\n", params->basic_rates[i]);
*/
return 0;
}
static int cfg80211_rtw_set_channel(struct wiphy *wiphy
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
, struct net_device *ndev
#endif
, struct ieee80211_channel *chan, enum nl80211_channel_type channel_type)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
#else
_adapter *padapter = wiphy_to_adapter(wiphy);
#endif
int chan_target = (u8) ieee80211_frequency_to_channel(chan->center_freq);
int chan_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
int chan_width = CHANNEL_WIDTH_20;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35))
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
#endif
switch (channel_type) {
case NL80211_CHAN_NO_HT:
case NL80211_CHAN_HT20:
chan_width = CHANNEL_WIDTH_20;
chan_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
case NL80211_CHAN_HT40MINUS:
chan_width = CHANNEL_WIDTH_40;
chan_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
break;
case NL80211_CHAN_HT40PLUS:
chan_width = CHANNEL_WIDTH_40;
chan_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
break;
default:
chan_width = CHANNEL_WIDTH_20;
chan_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
}
set_channel_bwmode(padapter, chan_target, chan_offset, chan_width);
return 0;
}
static int cfg80211_rtw_set_monitor_channel(struct wiphy *wiphy
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
, struct cfg80211_chan_def *chandef
#else
, struct ieee80211_channel *chan
, enum nl80211_channel_type channel_type
#endif
)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
struct ieee80211_channel *chan = chandef->chan;
#endif
_adapter *padapter = wiphy_to_adapter(wiphy);
int target_channal = chan->hw_value;
int target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
int target_width = CHANNEL_WIDTH_20;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("center_freq %u Mhz ch %u width %u freq1 %u freq2 %u\n"
, chan->center_freq
, chan->hw_value
, chandef->width
, chandef->center_freq1
, chandef->center_freq2);
#endif /* CONFIG_DEBUG_CFG80211 */
switch (chandef->width) {
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
target_width = CHANNEL_WIDTH_20;
target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
case NL80211_CHAN_WIDTH_40:
target_width = CHANNEL_WIDTH_40;
if (chandef->center_freq1 > chan->center_freq)
target_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
else
target_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
break;
case NL80211_CHAN_WIDTH_80:
target_width = CHANNEL_WIDTH_80;
target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
case NL80211_CHAN_WIDTH_80P80:
target_width = CHANNEL_WIDTH_80_80;
target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
case NL80211_CHAN_WIDTH_160:
target_width = CHANNEL_WIDTH_160;
target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
#endif
default:
target_width = CHANNEL_WIDTH_20;
target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
}
#else
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("center_freq %u Mhz ch %u channel_type %u\n"
, chan->center_freq
, chan->hw_value
, channel_type);
#endif /* CONFIG_DEBUG_CFG80211 */
switch (channel_type) {
case NL80211_CHAN_NO_HT:
case NL80211_CHAN_HT20:
target_width = CHANNEL_WIDTH_20;
target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
case NL80211_CHAN_HT40MINUS:
target_width = CHANNEL_WIDTH_40;
target_offset = HAL_PRIME_CHNL_OFFSET_UPPER;
break;
case NL80211_CHAN_HT40PLUS:
target_width = CHANNEL_WIDTH_40;
target_offset = HAL_PRIME_CHNL_OFFSET_LOWER;
break;
default:
target_width = CHANNEL_WIDTH_20;
target_offset = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
break;
}
#endif
set_channel_bwmode(padapter, target_channal, target_offset, target_width);
return 0;
}
static int cfg80211_rtw_auth(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_auth_request *req)
{
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
return 0;
}
static int cfg80211_rtw_assoc(struct wiphy *wiphy, struct net_device *ndev,
struct cfg80211_assoc_request *req)
{
RTW_INFO(FUNC_NDEV_FMT"\n", FUNC_NDEV_ARG(ndev));
return 0;
}
#endif /* CONFIG_AP_MODE */
void rtw_cfg80211_rx_probe_request(_adapter *adapter, union recv_frame *rframe)
{
struct wireless_dev *wdev = adapter->rtw_wdev;
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(adapter);
u8 *frame = get_recvframe_data(rframe);
uint frame_len = rframe->u.hdr.len;
s32 freq;
u8 ch, sch = rtw_get_oper_ch(adapter);
ch = rframe->u.hdr.attrib.ch ? rframe->u.hdr.attrib.ch : sch;
freq = rtw_ch2freq(ch);
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("RTW_Rx: probe request, ch=%d(%d)\n", ch, sch);
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
rtw_cfg80211_rx_mgmt(wdev, freq, 0, frame, frame_len, GFP_ATOMIC);
#else
cfg80211_rx_action(adapter->pnetdev, freq, frame, frame_len, GFP_ATOMIC);
#endif
}
void rtw_cfg80211_rx_action_p2p(_adapter *adapter, union recv_frame *rframe)
{
struct wireless_dev *wdev = adapter->rtw_wdev;
u8 *frame = get_recvframe_data(rframe);
uint frame_len = rframe->u.hdr.len;
s32 freq;
u8 ch, sch = rtw_get_oper_ch(adapter);
u8 category, action;
int type;
ch = rframe->u.hdr.attrib.ch ? rframe->u.hdr.attrib.ch : sch;
freq = rtw_ch2freq(ch);
RTW_INFO("RTW_Rx:ch=%d(%d)\n", ch, sch);
#ifdef CONFIG_P2P
type = rtw_p2p_check_frames(adapter, frame, frame_len, false);
if (type >= 0)
goto indicate;
#endif
rtw_action_frame_parse(frame, frame_len, &category, &action);
RTW_INFO("RTW_Rx:category(%u), action(%u)\n", category, action);
indicate:
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
rtw_cfg80211_rx_mgmt(wdev, freq, 0, frame, frame_len, GFP_ATOMIC);
#else
cfg80211_rx_action(adapter->pnetdev, freq, frame, frame_len, GFP_ATOMIC);
#endif
}
void rtw_cfg80211_rx_p2p_action_public(_adapter *adapter, union recv_frame *rframe)
{
struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
struct wireless_dev *wdev = adapter->rtw_wdev;
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(adapter);
u8 *frame = get_recvframe_data(rframe);
uint frame_len = rframe->u.hdr.len;
s32 freq;
u8 ch, sch = rtw_get_oper_ch(adapter);
u8 category, action;
int type;
ch = rframe->u.hdr.attrib.ch ? rframe->u.hdr.attrib.ch : sch;
freq = rtw_ch2freq(ch);
RTW_INFO("RTW_Rx:ch=%d(%d)\n", ch, sch);
#ifdef CONFIG_P2P
type = rtw_p2p_check_frames(adapter, frame, frame_len, false);
if (type >= 0) {
switch (type) {
case P2P_GO_NEGO_CONF:
if (0) {
RTW_INFO(FUNC_ADPT_FMT" Nego confirm. state=%u, status=%u, iaddr="MAC_FMT"\n"
, FUNC_ADPT_ARG(adapter), pwdev_priv->nego_info.state, pwdev_priv->nego_info.status
, MAC_ARG(pwdev_priv->nego_info.iface_addr));
}
if (pwdev_priv->nego_info.state == 2
&& pwdev_priv->nego_info.status == 0
&& rtw_check_invalid_mac_address(pwdev_priv->nego_info.iface_addr, false) == false
) {
_adapter *intended_iface = dvobj_get_adapter_by_addr(dvobj, pwdev_priv->nego_info.iface_addr);
if (intended_iface) {
RTW_INFO(FUNC_ADPT_FMT" Nego confirm. Allow only "ADPT_FMT" to scan for 2000 ms\n"
, FUNC_ADPT_ARG(adapter), ADPT_ARG(intended_iface));
/* allow only intended_iface to do scan for 2000 ms */
rtw_mi_set_scan_deny(adapter, 2000);
rtw_clear_scan_deny(intended_iface);
}
}
break;
case P2P_PROVISION_DISC_RESP:
case P2P_INVIT_RESP:
#if !RTW_P2P_GROUP_INTERFACE
rtw_mi_buddy_set_scan_deny(adapter, 2000);
#endif
break;
}
goto indicate;
}
#endif
rtw_action_frame_parse(frame, frame_len, &category, &action);
RTW_INFO("RTW_Rx:category(%u), action(%u)\n", category, action);
indicate:
#if defined(RTW_DEDICATED_P2P_DEVICE)
if (rtw_cfg80211_redirect_pd_wdev(dvobj_to_wiphy(dvobj), get_ra(frame), &wdev))
if (0)
RTW_INFO("redirect to pd_wdev:%p\n", wdev);
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
rtw_cfg80211_rx_mgmt(wdev, freq, 0, frame, frame_len, GFP_ATOMIC);
#else
cfg80211_rx_action(adapter->pnetdev, freq, frame, frame_len, GFP_ATOMIC);
#endif
}
void rtw_cfg80211_rx_action(_adapter *adapter, union recv_frame *rframe, const char *msg)
{
struct wireless_dev *wdev = adapter->rtw_wdev;
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(adapter);
u8 *frame = get_recvframe_data(rframe);
uint frame_len = rframe->u.hdr.len;
s32 freq;
u8 ch, sch = rtw_get_oper_ch(adapter);
u8 category, action;
ch = rframe->u.hdr.attrib.ch ? rframe->u.hdr.attrib.ch : sch;
freq = rtw_ch2freq(ch);
rtw_action_frame_parse(frame, frame_len, &category, &action);
if (action == ACT_PUBLIC_GAS_INITIAL_REQ) {
rtw_mi_set_scan_deny(adapter, 200);
rtw_mi_scan_abort(adapter, false); /*rtw_scan_abort_no_wait*/
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
rtw_cfg80211_rx_mgmt(wdev, freq, 0, frame, frame_len, GFP_ATOMIC);
#else
cfg80211_rx_action(adapter->pnetdev, freq, frame, frame_len, GFP_ATOMIC);
#endif
RTW_INFO("RTW_Rx:ch=%d(%d)\n", ch, sch);
if (msg)
RTW_INFO("RTW_Rx:%s\n", msg);
else
RTW_INFO("RTW_Rx:category(%u), action(%u)\n", category, action);
}
#ifdef CONFIG_P2P
void rtw_cfg80211_issue_p2p_provision_request(_adapter *padapter, const u8 *buf, size_t len)
{
u16 wps_devicepassword_id = 0x0000;
uint wps_devicepassword_id_len = 0;
u8 wpsie[255] = { 0x00 }, p2p_ie[255] = { 0x00 };
uint p2p_ielen = 0;
uint wpsielen = 0;
u32 devinfo_contentlen = 0;
u8 devinfo_content[64] = { 0x00 };
u16 capability = 0;
uint capability_len = 0;
unsigned char category = RTW_WLAN_CATEGORY_PUBLIC;
u8 action = P2P_PUB_ACTION_ACTION;
u8 dialogToken = 1;
__be32 p2poui = cpu_to_be32(P2POUI);
u8 oui_subtype = P2P_PROVISION_DISC_REQ;
u32 p2pielen = 0;
#ifdef CONFIG_WFD
u32 wfdielen = 0;
#endif
struct xmit_frame *pmgntframe;
struct pkt_attrib *pattrib;
unsigned char *pframe;
struct rtw_ieee80211_hdr *pwlanhdr;
__le16 *fctrl;
struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
struct mlme_ext_info *pmlmeinfo = &(pmlmeext->mlmext_info);
struct wifidirect_info *pwdinfo = &(padapter->wdinfo);
u8 *frame_body = (unsigned char *)(buf + sizeof(struct rtw_ieee80211_hdr_3addr));
size_t frame_body_len = len - sizeof(struct rtw_ieee80211_hdr_3addr);
__be16 be_tmp;
RTW_INFO("[%s] In\n", __func__);
/* prepare for building provision_request frame */
memcpy(pwdinfo->tx_prov_disc_info.peerIFAddr, GetAddr1Ptr(buf), ETH_ALEN);
memcpy(pwdinfo->tx_prov_disc_info.peerDevAddr, GetAddr1Ptr(buf), ETH_ALEN);
pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_PUSH_BUTTON;
rtw_get_wps_ie(frame_body + _PUBLIC_ACTION_IE_OFFSET_, frame_body_len - _PUBLIC_ACTION_IE_OFFSET_, wpsie, &wpsielen);
rtw_get_wps_attr_content(wpsie, wpsielen, WPS_ATTR_DEVICE_PWID, (u8 *)&be_tmp, &wps_devicepassword_id_len);
wps_devicepassword_id = be16_to_cpu(be_tmp);
switch (wps_devicepassword_id) {
case WPS_DPID_PIN:
pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_LABEL;
break;
case WPS_DPID_USER_SPEC:
pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_DISPLYA;
break;
case WPS_DPID_MACHINE_SPEC:
break;
case WPS_DPID_REKEY:
break;
case WPS_DPID_PBC:
pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_PUSH_BUTTON;
break;
case WPS_DPID_REGISTRAR_SPEC:
pwdinfo->tx_prov_disc_info.wps_config_method_request = WPS_CM_KEYPAD;
break;
default:
break;
}
if (rtw_get_p2p_ie(frame_body + _PUBLIC_ACTION_IE_OFFSET_, frame_body_len - _PUBLIC_ACTION_IE_OFFSET_, p2p_ie, &p2p_ielen)) {
rtw_get_p2p_attr_content(p2p_ie, p2p_ielen, P2P_ATTR_DEVICE_INFO, devinfo_content, &devinfo_contentlen);
rtw_get_p2p_attr_content(p2p_ie, p2p_ielen, P2P_ATTR_CAPABILITY, (u8 *)&capability, &capability_len);
}
/* start to build provision_request frame */
memset(wpsie, 0, sizeof(wpsie));
memset(p2p_ie, 0, sizeof(p2p_ie));
p2p_ielen = 0;
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
if (pmgntframe == NULL)
return;
/* update attribute */
pattrib = &pmgntframe->attrib;
update_mgntframe_attrib(padapter, pattrib);
memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
fctrl = &(pwlanhdr->frame_ctl);
*(fctrl) = 0;
memcpy(pwlanhdr->addr1, pwdinfo->tx_prov_disc_info.peerDevAddr, ETH_ALEN);
memcpy(pwlanhdr->addr2, adapter_mac_addr(padapter), ETH_ALEN);
memcpy(pwlanhdr->addr3, pwdinfo->tx_prov_disc_info.peerDevAddr, ETH_ALEN);
SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq);
pmlmeext->mgnt_seq++;
set_frame_sub_type(pframe, WIFI_ACTION);
pframe += sizeof(struct rtw_ieee80211_hdr_3addr);
pattrib->pktlen = sizeof(struct rtw_ieee80211_hdr_3addr);
pframe = rtw_set_fixed_ie(pframe, 1, &(category), &(pattrib->pktlen));
pframe = rtw_set_fixed_ie(pframe, 1, &(action), &(pattrib->pktlen));
pframe = rtw_set_fixed_ie(pframe, 4, (unsigned char *) &(p2poui), &(pattrib->pktlen));
pframe = rtw_set_fixed_ie(pframe, 1, &(oui_subtype), &(pattrib->pktlen));
pframe = rtw_set_fixed_ie(pframe, 1, &(dialogToken), &(pattrib->pktlen));
/* build_prov_disc_request_p2p_ie */
/* P2P OUI */
p2pielen = 0;
p2p_ie[p2pielen++] = 0x50;
p2p_ie[p2pielen++] = 0x6F;
p2p_ie[p2pielen++] = 0x9A;
p2p_ie[p2pielen++] = 0x09; /* WFA P2P v1.0 */
/* Commented by Albert 20110301 */
/* According to the P2P Specification, the provision discovery request frame should contain 3 P2P attributes */
/* 1. P2P Capability */
/* 2. Device Info */
/* 3. Group ID ( When joining an operating P2P Group ) */
/* P2P Capability ATTR */
/* Type: */
p2p_ie[p2pielen++] = P2P_ATTR_CAPABILITY;
/* Length: */
/* *(u16*) ( p2pie + p2pielen ) = cpu_to_le16( 0x0002 ); */
RTW_PUT_LE16(p2p_ie + p2pielen, 0x0002);
p2pielen += 2;
/* Value: */
/* Device Capability Bitmap, 1 byte */
/* Group Capability Bitmap, 1 byte */
memcpy(p2p_ie + p2pielen, &capability, 2);
p2pielen += 2;
/* Device Info ATTR */
/* Type: */
p2p_ie[p2pielen++] = P2P_ATTR_DEVICE_INFO;
/* Length: */
/* 21->P2P Device Address (6bytes) + Config Methods (2bytes) + Primary Device Type (8bytes) */
/* + NumofSecondDevType (1byte) + WPS Device Name ID field (2bytes) + WPS Device Name Len field (2bytes) */
/* *(u16*) ( p2pie + p2pielen ) = cpu_to_le16( 21 + pwdinfo->device_name_len ); */
RTW_PUT_LE16(p2p_ie + p2pielen, devinfo_contentlen);
p2pielen += 2;
/* Value: */
memcpy(p2p_ie + p2pielen, devinfo_content, devinfo_contentlen);
p2pielen += devinfo_contentlen;
pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, p2pielen, (unsigned char *) p2p_ie, &p2p_ielen);
/* p2pielen = build_prov_disc_request_p2p_ie( pwdinfo, pframe, NULL, 0, pwdinfo->tx_prov_disc_info.peerDevAddr); */
/* pframe += p2pielen; */
pattrib->pktlen += p2p_ielen;
wpsielen = 0;
/* WPS OUI */
*(__be32 *)(wpsie) = cpu_to_be32(WPSOUI);
wpsielen += 4;
/* WPS version */
/* Type: */
*(__be16 *)(wpsie + wpsielen) = cpu_to_be16(WPS_ATTR_VER1);
wpsielen += 2;
/* Length: */
*(__be16 *)(wpsie + wpsielen) = cpu_to_be16(0x0001);
wpsielen += 2;
/* Value: */
wpsie[wpsielen++] = WPS_VERSION_1; /* Version 1.0 */
/* Config Method */
/* Type: */
*(__be16 *)(wpsie + wpsielen) = cpu_to_be16(WPS_ATTR_CONF_METHOD);
wpsielen += 2;
/* Length: */
*(__be16 *)(wpsie + wpsielen) = cpu_to_be16(0x0002);
wpsielen += 2;
/* Value: */
*(__be16 *)(wpsie + wpsielen) = cpu_to_be16(pwdinfo->tx_prov_disc_info.wps_config_method_request);
wpsielen += 2;
pframe = rtw_set_ie(pframe, _VENDOR_SPECIFIC_IE_, wpsielen, (unsigned char *) wpsie, &pattrib->pktlen);
#ifdef CONFIG_WFD
wfdielen = build_provdisc_req_wfd_ie(pwdinfo, pframe);
pframe += wfdielen;
pattrib->pktlen += wfdielen;
#endif
pattrib->last_txcmdsz = pattrib->pktlen;
/* dump_mgntframe(padapter, pmgntframe); */
if (dump_mgntframe_and_wait_ack(padapter, pmgntframe) != _SUCCESS)
RTW_INFO("%s, ack to\n", __func__);
}
#ifdef CONFIG_RTW_80211R
static s32 cfg80211_rtw_update_ft_ies(struct wiphy *wiphy,
struct net_device *ndev,
struct cfg80211_update_ft_ies_params *ftie)
{
_adapter *padapter = NULL;
struct mlme_priv *pmlmepriv = NULL;
ft_priv *pftpriv = NULL;
unsigned long irqL;
u8 *p;
u8 *pie = NULL;
u32 ie_len = 0;
if (ndev == NULL)
return -EINVAL;
padapter = (_adapter *)rtw_netdev_priv(ndev);
pmlmepriv = &(padapter->mlmepriv);
pftpriv = &pmlmepriv->ftpriv;
p = (u8 *)ftie->ie;
if (ftie->ie_len <= sizeof(pftpriv->updated_ft_ies)) {
_enter_critical_bh(&pmlmepriv->lock, &irqL);
memcpy(pftpriv->updated_ft_ies, ftie->ie, ftie->ie_len);
pftpriv->updated_ft_ies_len = ftie->ie_len;
_exit_critical_bh(&pmlmepriv->lock, &irqL);
} else {
RTW_ERR("FTIEs parsing fail!\n");
return -EINVAL;
}
if ((rtw_to_roam(padapter) > 0) && rtw_chk_ft_status(padapter, RTW_FT_AUTHENTICATED_STA)) {
RTW_INFO("auth success, start reassoc\n");
_enter_critical_bh(&pmlmepriv->lock, &irqL);
rtw_set_ft_status(padapter, RTW_FT_ASSOCIATING_STA);
_exit_critical_bh(&pmlmepriv->lock, &irqL);
start_clnt_assoc(padapter);
}
return 0;
}
#endif
inline void rtw_cfg80211_set_is_roch(_adapter *adapter, bool val)
{
adapter->cfg80211_wdinfo.is_ro_ch = val;
rtw_mi_update_iface_status(&(adapter->mlmepriv), 0);
}
inline bool rtw_cfg80211_get_is_roch(_adapter *adapter)
{
return adapter->cfg80211_wdinfo.is_ro_ch;
}
static s32 cfg80211_rtw_remain_on_channel(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
struct wireless_dev *wdev,
#else
struct net_device *ndev,
#endif
struct ieee80211_channel *channel,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
enum nl80211_channel_type channel_type,
#endif
unsigned int duration, u64 *cookie)
{
s32 err = 0;
u8 remain_ch = (u8) ieee80211_frequency_to_channel(channel->center_freq);
u8 union_ch = 0, union_bw = 0, union_offset = 0;
u8 i;
u8 ready_on_channel = false;
_adapter *padapter = NULL;
_adapter *iface;
struct dvobj_priv *dvobj;
struct rtw_wdev_priv *pwdev_priv;
struct mlme_ext_priv *pmlmeext;
struct wifidirect_info *pwdinfo;
struct cfg80211_wifidirect_info *pcfg80211_wdinfo;
u8 is_p2p_find = false;
#ifndef CONFIG_RADIO_WORK
#define RTW_ROCH_DURATION_ENLARGE
#define RTW_ROCH_BACK_OP
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
#if defined(RTW_DEDICATED_P2P_DEVICE)
if (wdev == wiphy_to_pd_wdev(wiphy))
padapter = wiphy_to_adapter(wiphy);
else
#endif
if (wdev_to_ndev(wdev))
padapter = (_adapter *)rtw_netdev_priv(wdev_to_ndev(wdev));
else {
err = -EINVAL;
goto exit;
}
#else
struct wireless_dev *wdev;
if (ndev == NULL) {
err = -EINVAL;
goto exit;
}
padapter = (_adapter *)rtw_netdev_priv(ndev);
wdev = ndev_to_wdev(ndev);
#endif
dvobj = adapter_to_dvobj(padapter);
pwdev_priv = adapter_wdev_data(padapter);
pmlmeext = &padapter->mlmeextpriv;
pwdinfo = &padapter->wdinfo;
pcfg80211_wdinfo = &padapter->cfg80211_wdinfo;
#ifdef CONFIG_CONCURRENT_MODE
is_p2p_find = (duration < (pwdinfo->ext_listen_interval)) ? true : false;
#endif
*cookie = ATOMIC_INC_RETURN(&pcfg80211_wdinfo->ro_ch_cookie_gen);
RTW_INFO(FUNC_ADPT_FMT"%s ch:%u duration:%d, cookie:0x%llx\n"
, FUNC_ADPT_ARG(padapter), wdev == wiphy_to_pd_wdev(wiphy) ? " PD" : ""
, remain_ch, duration, *cookie);
if (rtw_ch_set_search_ch(pmlmeext->channel_set, remain_ch) < 0) {
RTW_WARN(FUNC_ADPT_FMT" invalid ch:%u\n", FUNC_ADPT_ARG(padapter), remain_ch);
err = -EFAULT;
goto exit;
}
#ifdef CONFIG_MP_INCLUDED
if (rtw_mi_mp_mode_check(padapter)) {
RTW_INFO("MP mode block remain_on_channel request\n");
err = -EFAULT;
goto exit;
}
#endif
if (_FAIL == rtw_pwr_wakeup(padapter)) {
err = -EFAULT;
goto exit;
}
rtw_scan_abort(padapter);
#ifdef CONFIG_CONCURRENT_MODE
/*don't scan_abort during p2p_listen.*/
if (is_p2p_find)
rtw_mi_buddy_scan_abort(padapter, true);
#endif /*CONFIG_CONCURRENT_MODE*/
if (rtw_cfg80211_get_is_roch(padapter) ) {
_cancel_timer_ex(&padapter->cfg80211_wdinfo.remain_on_ch_timer);
p2p_cancel_roch_cmd(padapter, 0, NULL, RTW_CMDF_WAIT_ACK);
}
/* if(!rtw_p2p_chk_role(pwdinfo, P2P_ROLE_CLIENT) && !rtw_p2p_chk_role(pwdinfo, P2P_ROLE_GO)) */
if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) {
rtw_p2p_enable(padapter, P2P_ROLE_DEVICE);
padapter->wdinfo.listen_channel = remain_ch;
RTW_INFO(FUNC_ADPT_FMT" init listen_channel %u\n"
, FUNC_ADPT_ARG(padapter), padapter->wdinfo.listen_channel);
} else if (rtw_p2p_chk_state(pwdinfo , P2P_STATE_LISTEN)
&& (time_after_eq((unsigned long)jiffies, (unsigned long)pwdev_priv->probe_resp_ie_update_time)
&& rtw_get_passing_time_ms(pwdev_priv->probe_resp_ie_update_time) < 50)
) {
if (padapter->wdinfo.listen_channel != remain_ch) {
padapter->wdinfo.listen_channel = remain_ch;
RTW_INFO(FUNC_ADPT_FMT" update listen_channel %u\n"
, FUNC_ADPT_ARG(padapter), padapter->wdinfo.listen_channel);
}
} else {
rtw_p2p_set_pre_state(pwdinfo, rtw_p2p_state(pwdinfo));
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, role=%d, p2p_state=%d\n", __func__, rtw_p2p_role(pwdinfo), rtw_p2p_state(pwdinfo));
#endif
}
for (i = 0; i < dvobj->iface_nums; i++) {
iface = dvobj->padapters[i];
if (check_fwstate(&iface->mlmepriv, _FW_UNDER_LINKING | WIFI_UNDER_WPS) ) {
RTW_INFO(ADPT_FMT"- _FW_UNDER_LINKING |WIFI_UNDER_WPS (mlme state:0x%x)\n", ADPT_ARG(iface), get_fwstate(&iface->mlmepriv));
remain_ch = iface->mlmeextpriv.cur_channel;
}
}
rtw_p2p_set_state(pwdinfo, P2P_STATE_LISTEN);
#ifdef RTW_ROCH_DURATION_ENLARGE
if (duration < 400)
duration = duration * 3; /* extend from exper */
#endif
#if defined(RTW_ROCH_BACK_OP) && defined(CONFIG_CONCURRENT_MODE)
if (rtw_mi_check_status(padapter, MI_LINKED)) {
if (is_p2p_find) /* p2p_find , duration<1000 */
duration = duration + pwdinfo->ext_listen_interval;
else /* p2p_listen, duration=5000 */
duration = pwdinfo->ext_listen_interval + (pwdinfo->ext_listen_interval / 4);
}
#endif /*defined (RTW_ROCH_BACK_OP) && defined(CONFIG_CONCURRENT_MODE) */
rtw_cfg80211_set_is_roch(padapter, true);
pcfg80211_wdinfo->ro_ch_wdev = wdev;
pcfg80211_wdinfo->remain_on_ch_cookie = *cookie;
pcfg80211_wdinfo->last_ro_ch_time = jiffies;
memcpy(&pcfg80211_wdinfo->remain_on_ch_channel, channel, sizeof(struct ieee80211_channel));
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
pcfg80211_wdinfo->remain_on_ch_type = channel_type;
#endif
pcfg80211_wdinfo->restore_channel = rtw_get_oper_ch(padapter);
#ifdef CONFIG_CONCURRENT_MODE
if (rtw_mi_check_status(padapter, MI_LINKED) && (0 != rtw_mi_get_union_chan(padapter))) {
if ((remain_ch != rtw_mi_get_union_chan(padapter)) && !check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
if (ATOMIC_READ(&pwdev_priv->switch_ch_to) == 1 ||
(remain_ch != pmlmeext->cur_channel)) {
rtw_mi_buddy_issue_nulldata(padapter, NULL, 1, 3, 500);
ATOMIC_SET(&pwdev_priv->switch_ch_to, 0);
#ifdef RTW_ROCH_BACK_OP
RTW_INFO("%s, set switch ch timer, duration=%d\n", __func__, duration - pwdinfo->ext_listen_interval);
_set_timer(&pwdinfo->ap_p2p_switch_timer, duration - pwdinfo->ext_listen_interval);
#endif
}
}
ready_on_channel = true;
} else
#endif /* CONFIG_CONCURRENT_MODE */
{
if (remain_ch != rtw_get_oper_ch(padapter))
ready_on_channel = true;
}
if (ready_on_channel ) {
#ifndef RTW_SINGLE_WIPHY
if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED))
#endif
{
#ifdef CONFIG_CONCURRENT_MODE
if (rtw_get_oper_ch(padapter) != remain_ch)
#endif
{
/* if (!padapter->mlmepriv.LinkDetectInfo.bBusyTraffic) */
set_channel_bwmode(padapter, remain_ch, HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20);
}
}
}
#ifdef CONFIG_BT_COEXIST
rtw_btcoex_ScanNotify(padapter, true);
#endif
RTW_INFO("%s, set ro ch timer, duration=%d\n", __func__, duration);
_set_timer(&pcfg80211_wdinfo->remain_on_ch_timer, duration);
rtw_cfg80211_ready_on_channel(wdev, *cookie, channel, channel_type, duration, GFP_KERNEL);
exit:
return err;
}
static s32 cfg80211_rtw_cancel_remain_on_channel(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
struct wireless_dev *wdev,
#else
struct net_device *ndev,
#endif
u64 cookie)
{
s32 err = 0;
_adapter *padapter;
struct rtw_wdev_priv *pwdev_priv;
struct wifidirect_info *pwdinfo;
struct cfg80211_wifidirect_info *pcfg80211_wdinfo;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
#if defined(RTW_DEDICATED_P2P_DEVICE)
if (wdev == wiphy_to_pd_wdev(wiphy))
padapter = wiphy_to_adapter(wiphy);
else
#endif
if (wdev_to_ndev(wdev))
padapter = (_adapter *)rtw_netdev_priv(wdev_to_ndev(wdev));
else {
err = -EINVAL;
goto exit;
}
#else
struct wireless_dev *wdev;
if (ndev == NULL) {
err = -EINVAL;
goto exit;
}
padapter = (_adapter *)rtw_netdev_priv(ndev);
wdev = ndev_to_wdev(ndev);
#endif
pwdev_priv = adapter_wdev_data(padapter);
pwdinfo = &padapter->wdinfo;
pcfg80211_wdinfo = &padapter->cfg80211_wdinfo;
RTW_INFO(FUNC_ADPT_FMT"%s cookie:0x%llx\n"
, FUNC_ADPT_ARG(padapter), wdev == wiphy_to_pd_wdev(wiphy) ? " PD" : ""
, cookie);
if (rtw_cfg80211_get_is_roch(padapter) ) {
_cancel_timer_ex(&padapter->cfg80211_wdinfo.remain_on_ch_timer);
p2p_cancel_roch_cmd(padapter, cookie, wdev, RTW_CMDF_WAIT_ACK);
}
exit:
return err;
}
inline int rtw_cfg80211_iface_has_p2p_group_cap(_adapter *adapter)
{
struct wiphy *wiphy = adapter_to_wiphy(adapter);
struct rtw_wdev_priv *wdev_data = adapter_wdev_data(adapter);
#if RTW_P2P_GROUP_INTERFACE
if (is_primary_adapter(adapter))
return 0;
#endif
return 1;
}
inline int rtw_cfg80211_is_p2p_scan(_adapter *adapter)
{
#if RTW_P2P_GROUP_INTERFACE
if (rtw_cfg80211_iface_has_p2p_group_cap(adapter))
#endif
{
struct wifidirect_info *wdinfo = &adapter->wdinfo;
return rtw_p2p_chk_state(wdinfo, P2P_STATE_SCAN)
|| rtw_p2p_chk_state(wdinfo, P2P_STATE_FIND_PHASE_SEARCH);
}
#if RTW_P2P_GROUP_INTERFACE
#if defined(RTW_DEDICATED_P2P_DEVICE)
if (wiphy_to_pd_wdev(adapter_to_wiphy(adapter))) /* pd_wdev exist */
return rtw_cfg80211_is_scan_by_pd_wdev(adapter);
#endif
{
/*
* For 2 RTW_P2P_GROUP_INTERFACE cases:
* 1. RTW_DEDICATED_P2P_DEVICE defined but upper layer don't use pd_wdev or
* 2. RTW_DEDICATED_P2P_DEVICE not defined
*/
struct rtw_wdev_priv *wdev_data = adapter_wdev_data(adapter);
unsigned long irqL;
int is_p2p_scan = 0;
_enter_critical_bh(&wdev_data->scan_req_lock, &irqL);
if (wdev_data->scan_request
&& wdev_data->scan_request->ssids
&& wdev_data->scan_request->ie
) {
if (!memcmp(wdev_data->scan_request->ssids->ssid, "DIRECT-", 7)
&& rtw_get_p2p_ie((u8 *)wdev_data->scan_request->ie, wdev_data->scan_request->ie_len, NULL, NULL))
is_p2p_scan = 1;
}
_exit_critical_bh(&wdev_data->scan_req_lock, &irqL);
return is_p2p_scan;
}
#endif
}
#if defined(RTW_DEDICATED_P2P_DEVICE)
int rtw_pd_iface_alloc(struct wiphy *wiphy, const char *name, struct wireless_dev **pd_wdev)
{
struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
struct wireless_dev *wdev = NULL;
struct rtw_netdev_priv_indicator *npi;
_adapter *primary_adpt = wiphy_to_adapter(wiphy);
int ret = 0;
if (wiphy_data->pd_wdev) {
RTW_WARN(FUNC_WIPHY_FMT" pd_wdev already exists\n", FUNC_WIPHY_ARG(wiphy));
ret = -EBUSY;
goto exit;
}
wdev = (struct wireless_dev *)rtw_zmalloc(sizeof(struct wireless_dev));
if (!wdev) {
RTW_WARN(FUNC_WIPHY_FMT" allocate wdev fail\n", FUNC_WIPHY_ARG(wiphy));
ret = -ENOMEM;
goto exit;
}
wdev->wiphy = wiphy;
wdev->iftype = NL80211_IFTYPE_P2P_DEVICE;
memcpy(wdev->address, adapter_mac_addr(primary_adpt), ETH_ALEN);
wiphy_data->pd_wdev = wdev;
*pd_wdev = wdev;
RTW_INFO(FUNC_WIPHY_FMT" pd_wdev:%p, addr="MAC_FMT" added\n"
, FUNC_WIPHY_ARG(wiphy), wdev, MAC_ARG(wdev_address(wdev)));
exit:
if (ret && wdev) {
rtw_mfree((u8 *)wdev, sizeof(struct wireless_dev));
wdev = NULL;
}
return ret;
}
void rtw_pd_iface_free(struct wiphy *wiphy)
{
struct dvobj_priv *dvobj = wiphy_to_dvobj(wiphy);
struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
u8 rtnl_lock_needed;
if (!wiphy_data->pd_wdev)
goto exit;
RTW_INFO(FUNC_WIPHY_FMT" pd_wdev:%p, addr="MAC_FMT"\n"
, FUNC_WIPHY_ARG(wiphy), wiphy_data->pd_wdev
, MAC_ARG(wdev_address(wiphy_data->pd_wdev)));
rtnl_lock_needed = rtw_rtnl_lock_needed(dvobj);
if (rtnl_lock_needed)
rtnl_lock();
cfg80211_unregister_wdev(wiphy_data->pd_wdev);
if (rtnl_lock_needed)
rtnl_unlock();
rtw_mfree((u8 *)wiphy_data->pd_wdev, sizeof(struct wireless_dev));
wiphy_data->pd_wdev = NULL;
exit:
return;
}
static int cfg80211_rtw_start_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
{
_adapter *adapter = wiphy_to_adapter(wiphy);
RTW_INFO(FUNC_WIPHY_FMT" wdev=%p\n", FUNC_WIPHY_ARG(wiphy), wdev);
rtw_p2p_enable(adapter, P2P_ROLE_DEVICE);
return 0;
}
static void cfg80211_rtw_stop_p2p_device(struct wiphy *wiphy, struct wireless_dev *wdev)
{
_adapter *adapter = wiphy_to_adapter(wiphy);
RTW_INFO(FUNC_WIPHY_FMT" wdev=%p\n", FUNC_WIPHY_ARG(wiphy), wdev);
if (rtw_cfg80211_is_p2p_scan(adapter))
rtw_scan_abort(adapter);
rtw_p2p_enable(adapter, P2P_ROLE_DISABLE);
}
inline int rtw_cfg80211_redirect_pd_wdev(struct wiphy *wiphy, u8 *ra, struct wireless_dev **wdev)
{
struct wireless_dev *pd_wdev = wiphy_to_pd_wdev(wiphy);
if (pd_wdev && pd_wdev != *wdev
&& !memcmp(wdev_address(pd_wdev), ra, ETH_ALEN)
) {
*wdev = pd_wdev;
return 1;
}
return 0;
}
inline int rtw_cfg80211_is_scan_by_pd_wdev(_adapter *adapter)
{
struct wiphy *wiphy = adapter_to_wiphy(adapter);
struct rtw_wdev_priv *wdev_data = adapter_wdev_data(adapter);
struct wireless_dev *wdev = NULL;
unsigned long irqL;
_enter_critical_bh(&wdev_data->scan_req_lock, &irqL);
if (wdev_data->scan_request)
wdev = wdev_data->scan_request->wdev;
_exit_critical_bh(&wdev_data->scan_req_lock, &irqL);
if (wdev && wdev == wiphy_to_pd_wdev(wiphy))
return 1;
return 0;
}
#endif /* RTW_DEDICATED_P2P_DEVICE */
#endif /* CONFIG_P2P */
inline void rtw_cfg80211_set_is_mgmt_tx(_adapter *adapter, u8 val)
{
struct rtw_wdev_priv *wdev_priv = adapter_wdev_data(adapter);
wdev_priv->is_mgmt_tx = val;
rtw_mi_update_iface_status(&(adapter->mlmepriv), 0);
}
inline u8 rtw_cfg80211_get_is_mgmt_tx(_adapter *adapter)
{
struct rtw_wdev_priv *wdev_priv = adapter_wdev_data(adapter);
return wdev_priv->is_mgmt_tx;
}
static int _cfg80211_rtw_mgmt_tx(_adapter *padapter, u8 tx_ch, u8 no_cck, const u8 *buf, size_t len, int wait_ack)
{
struct xmit_frame *pmgntframe;
struct pkt_attrib *pattrib;
unsigned char *pframe;
int ret = _FAIL;
bool ack = true;
struct rtw_ieee80211_hdr *pwlanhdr;
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter);
struct xmit_priv *pxmitpriv = &(padapter->xmitpriv);
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
#ifdef CONFIG_P2P
struct wifidirect_info *pwdinfo = &padapter->wdinfo;
#endif /* CONFIG_P2P */
/* struct cfg80211_wifidirect_info *pcfg80211_wdinfo = &padapter->cfg80211_wdinfo; */
rtw_mi_set_scan_deny(padapter, 1000);
rtw_mi_scan_abort(padapter, true);
rtw_cfg80211_set_is_mgmt_tx(padapter, 1);
#ifdef CONFIG_BT_COEXIST
rtw_btcoex_ScanNotify(padapter, true);
#endif
#ifdef CONFIG_P2P
if (rtw_cfg80211_get_is_roch(padapter) ) {
#ifdef CONFIG_CONCURRENT_MODE
if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED)) {
RTW_INFO("%s, extend ro ch time\n", __func__);
_set_timer(&padapter->cfg80211_wdinfo.remain_on_ch_timer, pwdinfo->ext_listen_period);
}
#endif /* CONFIG_CONCURRENT_MODE */
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_MCC_MODE
if (MCC_EN(padapter)) {
if (rtw_hal_check_mcc_status(padapter, MCC_STATUS_DOING_MCC))
/* don't set channel, issue frame directly */
goto issue_mgmt_frame;
}
#endif /* CONFIG_MCC_MODE */
#ifdef CONFIG_CONCURRENT_MODE
if (rtw_mi_check_status(padapter, MI_LINKED)) {
u8 union_ch = rtw_mi_get_union_chan(padapter);
u8 co_channel = 0xff;
co_channel = rtw_get_oper_ch(padapter);
if (tx_ch != union_ch) {
u16 ext_listen_period;
if (ATOMIC_READ(&pwdev_priv->switch_ch_to) == 1) {
rtw_mi_buddy_issue_nulldata(padapter, NULL, 1, 3, 500);
ATOMIC_SET(&pwdev_priv->switch_ch_to, 0);
/* RTW_INFO("%s, set switch ch timer, period=%d\n", __func__, pwdinfo->ext_listen_period); */
/* _set_timer(&pwdinfo->ap_p2p_switch_timer, pwdinfo->ext_listen_period); */
}
if (check_fwstate(&padapter->mlmepriv, _FW_LINKED))
ext_listen_period = 500;/*500ms*/
#ifdef CONFIG_P2P
else
ext_listen_period = pwdinfo->ext_listen_period;
_set_timer(&pwdinfo->ap_p2p_switch_timer, ext_listen_period);
#endif
RTW_INFO("%s, set switch ch timer, period=%d\n", __func__, ext_listen_period);
}
if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED))
pmlmeext->cur_channel = tx_ch;
if (tx_ch != co_channel)
set_channel_bwmode(padapter, tx_ch, HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20);
} else
#endif /* CONFIG_CONCURRENT_MODE */
/* if (tx_ch != pmlmeext->cur_channel) { */
if (tx_ch != rtw_get_oper_ch(padapter)) {
if (!check_fwstate(&padapter->mlmepriv, _FW_LINKED))
pmlmeext->cur_channel = tx_ch;
set_channel_bwmode(padapter, tx_ch, HAL_PRIME_CHNL_OFFSET_DONT_CARE, CHANNEL_WIDTH_20);
}
issue_mgmt_frame:
/* starting alloc mgmt frame to dump it */
pmgntframe = alloc_mgtxmitframe(pxmitpriv);
if (pmgntframe == NULL) {
/* ret = -ENOMEM; */
ret = _FAIL;
goto exit;
}
/* update attribute */
pattrib = &pmgntframe->attrib;
update_mgntframe_attrib(padapter, pattrib);
if (no_cck && IS_CCK_RATE(pattrib->rate)) {
/* force OFDM 6M rate*/
pattrib->rate = MGN_6M;
pattrib->raid = rtw_get_mgntframe_raid(padapter, WIRELESS_11G);
}
pattrib->retry_ctrl = false;
memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET);
pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET;
memcpy(pframe, (void *)buf, len);
pattrib->pktlen = len;
pwlanhdr = (struct rtw_ieee80211_hdr *)pframe;
/* update seq number */
pmlmeext->mgnt_seq = GetSequence(pwlanhdr);
pattrib->seqnum = pmlmeext->mgnt_seq;
pmlmeext->mgnt_seq++;
#ifdef CONFIG_P2P
rtw_xframe_chk_wfd_ie(pmgntframe);
#endif /* CONFIG_P2P */
pattrib->last_txcmdsz = pattrib->pktlen;
if (wait_ack) {
if (dump_mgntframe_and_wait_ack(padapter, pmgntframe) != _SUCCESS) {
ack = false;
ret = _FAIL;
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, ack == _FAIL\n", __func__);
#endif
} else {
#ifdef CONFIG_XMIT_ACK
rtw_msleep_os(50);
#endif
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, ack=%d, ok!\n", __func__, ack);
#endif
ret = _SUCCESS;
}
} else {
dump_mgntframe(padapter, pmgntframe);
ret = _SUCCESS;
}
exit:
rtw_cfg80211_set_is_mgmt_tx(padapter, 0);
#ifdef CONFIG_BT_COEXIST
rtw_btcoex_ScanNotify(padapter, false);
#endif
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, ret=%d\n", __func__, ret);
#endif
return ret;
}
static int cfg80211_rtw_mgmt_tx(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
struct wireless_dev *wdev,
#else
struct net_device *ndev,
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)) || defined(COMPAT_KERNEL_RELEASE)
struct ieee80211_channel *chan,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) || defined(COMPAT_KERNEL_RELEASE)
bool offchan,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
enum nl80211_channel_type channel_type,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
bool channel_type_valid,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) || defined(COMPAT_KERNEL_RELEASE)
unsigned int wait,
#endif
const u8 *buf, size_t len,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
bool no_cck,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
bool dont_wait_for_ack,
#endif
#else
struct cfg80211_mgmt_tx_params *params,
#endif
u64 *cookie)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(COMPAT_KERNEL_RELEASE)
struct ieee80211_channel *chan = params->chan;
bool offchan = params->offchan;
unsigned int wait = params->wait;
const u8 *buf = params->buf;
size_t len = params->len;
bool no_cck = params->no_cck;
bool dont_wait_for_ack = params->dont_wait_for_ack;
#endif
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
bool no_cck = 0;
#endif
int ret = 0;
int tx_ret;
int wait_ack = 1;
u32 dump_limit = RTW_MAX_MGMT_TX_CNT;
u32 dump_cnt = 0;
bool ack = true;
u8 tx_ch;
u8 category, action;
u8 frame_styp;
int type = (-1);
u32 start = jiffies;
_adapter *padapter;
struct dvobj_priv *dvobj;
struct rtw_wdev_priv *pwdev_priv;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
#if defined(RTW_DEDICATED_P2P_DEVICE)
if (wdev == wiphy_to_pd_wdev(wiphy))
padapter = wiphy_to_adapter(wiphy);
else
#endif
if (wdev_to_ndev(wdev))
padapter = (_adapter *)rtw_netdev_priv(wdev_to_ndev(wdev));
else {
ret = -EINVAL;
goto exit;
}
#else
struct wireless_dev *wdev;
if (ndev == NULL) {
ret = -EINVAL;
goto exit;
}
padapter = (_adapter *)rtw_netdev_priv(ndev);
wdev = ndev_to_wdev(ndev);
#endif
if (chan == NULL) {
ret = -EINVAL;
goto exit;
}
tx_ch = (u8)ieee80211_frequency_to_channel(chan->center_freq);
dvobj = adapter_to_dvobj(padapter);
pwdev_priv = adapter_wdev_data(padapter);
/* cookie generation */
*cookie = (unsigned long) buf;
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO(FUNC_ADPT_FMT"%s len=%zu, ch=%d"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
", ch_type=%d"
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
", channel_type_valid=%d"
#endif
"\n", FUNC_ADPT_ARG(padapter), wdev == wiphy_to_pd_wdev(wiphy) ? " PD" : ""
, len, tx_ch
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
, channel_type
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
, channel_type_valid
#endif
);
#endif /* CONFIG_DEBUG_CFG80211 */
/* indicate ack before issue frame to avoid racing with rsp frame */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
rtw_cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, GFP_KERNEL);
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 36))
cfg80211_action_tx_status(ndev, *cookie, buf, len, ack, GFP_KERNEL);
#endif
frame_styp = le16_to_cpu(((struct rtw_ieee80211_hdr_3addr *)buf)->frame_ctl) & IEEE80211_FCTL_STYPE;
if (IEEE80211_STYPE_PROBE_RESP == frame_styp) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("RTW_Tx: probe_resp tx_ch=%d, no_cck=%u, da="MAC_FMT"\n", tx_ch, no_cck, MAC_ARG(GetAddr1Ptr(buf)));
#endif /* CONFIG_DEBUG_CFG80211 */
wait_ack = 0;
goto dump;
}
if (rtw_action_frame_parse(buf, len, &category, &action) == false) {
RTW_INFO(FUNC_ADPT_FMT" frame_control:0x%x\n", FUNC_ADPT_ARG(padapter),
le16_to_cpu(((struct rtw_ieee80211_hdr_3addr *)buf)->frame_ctl));
goto exit;
}
RTW_INFO("RTW_Tx:tx_ch=%d, no_cck=%u, da="MAC_FMT"\n", tx_ch, no_cck, MAC_ARG(GetAddr1Ptr(buf)));
#ifdef CONFIG_P2P
type = rtw_p2p_check_frames(padapter, buf, len, true);
if (type >= 0) {
no_cck = 1; /* force no CCK for P2P frames */
goto dump;
}
#endif
if (category == RTW_WLAN_CATEGORY_PUBLIC)
RTW_INFO("RTW_Tx:%s\n", action_public_str(action));
else
RTW_INFO("RTW_Tx:category(%u), action(%u)\n", category, action);
dump:
rtw_ps_deny(padapter, PS_DENY_MGNT_TX);
if (_FAIL == rtw_pwr_wakeup(padapter)) {
ret = -EFAULT;
goto cancel_ps_deny;
}
while (1) {
u32 sleep_ms = 0;
u32 retry_guarantee_ms = 0;
dump_cnt++;
tx_ret = _cfg80211_rtw_mgmt_tx(padapter, tx_ch, no_cck, buf, len, wait_ack);
switch (action) {
case ACT_PUBLIC_GAS_INITIAL_REQ:
case ACT_PUBLIC_GAS_INITIAL_RSP:
sleep_ms = 50;
retry_guarantee_ms = RTW_MAX_MGMT_TX_MS_GAS;
}
if (tx_ret == _SUCCESS
|| (dump_cnt >= dump_limit && rtw_get_passing_time_ms(start) >= retry_guarantee_ms))
break;
if (sleep_ms > 0)
rtw_msleep_os(sleep_ms);
}
if (tx_ret != _SUCCESS || dump_cnt > 1) {
RTW_INFO(FUNC_ADPT_FMT" %s (%d/%d) in %d ms\n", FUNC_ADPT_ARG(padapter),
tx_ret == _SUCCESS ? "OK" : "FAIL", dump_cnt, dump_limit, rtw_get_passing_time_ms(start));
}
switch (type) {
case P2P_GO_NEGO_CONF:
if (0) {
RTW_INFO(FUNC_ADPT_FMT" Nego confirm. state=%u, status=%u, iaddr="MAC_FMT"\n"
, FUNC_ADPT_ARG(padapter), pwdev_priv->nego_info.state, pwdev_priv->nego_info.status
, MAC_ARG(pwdev_priv->nego_info.iface_addr));
}
if (pwdev_priv->nego_info.state == 2
&& pwdev_priv->nego_info.status == 0
&& rtw_check_invalid_mac_address(pwdev_priv->nego_info.iface_addr, false) == false
) {
_adapter *intended_iface = dvobj_get_adapter_by_addr(dvobj, pwdev_priv->nego_info.iface_addr);
if (intended_iface) {
RTW_INFO(FUNC_ADPT_FMT" Nego confirm. Allow only "ADPT_FMT" to scan for 2000 ms\n"
, FUNC_ADPT_ARG(padapter), ADPT_ARG(intended_iface));
/* allow only intended_iface to do scan for 2000 ms */
rtw_mi_set_scan_deny(padapter, 2000);
rtw_clear_scan_deny(intended_iface);
}
}
break;
case P2P_INVIT_RESP:
if (pwdev_priv->invit_info.flags & BIT(0)
&& pwdev_priv->invit_info.status == 0
) {
RTW_INFO(FUNC_ADPT_FMT" agree with invitation of persistent group\n",
FUNC_ADPT_ARG(padapter));
#if !RTW_P2P_GROUP_INTERFACE
rtw_mi_buddy_set_scan_deny(padapter, 5000);
#endif
rtw_pwr_wakeup_ex(padapter, 5000);
}
break;
}
cancel_ps_deny:
rtw_ps_deny_cancel(padapter, PS_DENY_MGNT_TX);
exit:
return ret;
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0)
static void cfg80211_rtw_mgmt_frame_register(struct wiphy *wiphy,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
struct wireless_dev *wdev,
#else
struct net_device *ndev,
#endif
u16 frame_type, bool reg)
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
struct net_device *ndev = wdev_to_ndev(wdev);
#endif
_adapter *adapter;
struct rtw_wdev_priv *pwdev_priv;
if (ndev == NULL)
goto exit;
adapter = (_adapter *)rtw_netdev_priv(ndev);
pwdev_priv = adapter_wdev_data(adapter);
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO(FUNC_ADPT_FMT" frame_type:%x, reg:%d\n", FUNC_ADPT_ARG(adapter),
frame_type, reg);
#endif
/* Wait QC Verify */
return;
switch (frame_type) {
case IEEE80211_STYPE_PROBE_REQ: /* 0x0040 */
SET_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_PROBE_REQ, reg);
break;
case IEEE80211_STYPE_ACTION: /* 0x00D0 */
SET_CFG80211_REPORT_MGMT(pwdev_priv, IEEE80211_STYPE_ACTION, reg);
break;
default:
break;
}
exit:
return;
}
#endif
#if defined(CONFIG_TDLS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
static int cfg80211_rtw_tdls_mgmt(struct wiphy *wiphy,
struct net_device *ndev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
const u8 *peer,
#else
u8 *peer,
#endif
u8 action_code,
u8 dialog_token,
u16 status_code,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0))
u32 peer_capability,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0))
bool initiator,
#endif
const u8 *buf,
size_t len)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv;
struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info;
int ret = 0;
struct tdls_txmgmt txmgmt;
if (hal_chk_wl_func(padapter, WL_FUNC_TDLS) == false) {
RTW_INFO("Discard tdls action:%d, since hal doesn't support tdls\n", action_code);
goto discard;
}
if (rtw_tdls_is_driver_setup(padapter)) {
RTW_INFO("Discard tdls action:%d, let driver to set up direct link\n", action_code);
goto discard;
}
memset(&txmgmt, 0x00, sizeof(struct tdls_txmgmt));
memcpy(txmgmt.peer, peer, ETH_ALEN);
txmgmt.action_code = action_code;
txmgmt.dialog_token = dialog_token;
txmgmt.status_code = status_code;
txmgmt.len = len;
txmgmt.buf = (u8 *)rtw_malloc(txmgmt.len);
if (txmgmt.buf == NULL) {
ret = -ENOMEM;
goto bad;
}
memcpy(txmgmt.buf, (void *)buf, txmgmt.len);
/* Debug purpose */
RTW_INFO("%s %d\n", __func__, __LINE__);
RTW_INFO("peer:"MAC_FMT", action code:%d, dialog:%d, status code:%d\n",
MAC_ARG(txmgmt.peer), txmgmt.action_code,
txmgmt.dialog_token, txmgmt.status_code);
if (txmgmt.len > 0) {
int i = 0;
for (; i < len; i++)
printk("%02x ", *(txmgmt.buf + i));
RTW_INFO("len:%d\n", (u32)txmgmt.len);
}
switch (txmgmt.action_code) {
case TDLS_SETUP_REQUEST:
issue_tdls_setup_req(padapter, &txmgmt, true);
break;
case TDLS_SETUP_RESPONSE:
issue_tdls_setup_rsp(padapter, &txmgmt);
break;
case TDLS_SETUP_CONFIRM:
issue_tdls_setup_cfm(padapter, &txmgmt);
break;
case TDLS_TEARDOWN:
issue_tdls_teardown(padapter, &txmgmt, true);
break;
case TDLS_DISCOVERY_REQUEST:
issue_tdls_dis_req(padapter, &txmgmt);
break;
case TDLS_DISCOVERY_RESPONSE:
issue_tdls_dis_rsp(padapter, &txmgmt, pmlmeinfo->enc_algo ? true : false);
break;
}
bad:
if (txmgmt.buf)
rtw_mfree(txmgmt.buf, txmgmt.len);
discard:
return ret;
}
static int cfg80211_rtw_tdls_oper(struct wiphy *wiphy,
struct net_device *ndev,
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
const u8 *peer,
#else
u8 *peer,
#endif
enum nl80211_tdls_operation oper)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct tdls_info *ptdlsinfo = &padapter->tdlsinfo;
struct tdls_txmgmt txmgmt;
struct sta_info *ptdls_sta = NULL;
RTW_INFO(FUNC_NDEV_FMT", nl80211_tdls_operation:%d\n", FUNC_NDEV_ARG(ndev), oper);
if (hal_chk_wl_func(padapter, WL_FUNC_TDLS) == false) {
RTW_INFO("Discard tdls oper:%d, since hal doesn't support tdls\n", oper);
return 0;
}
#ifdef CONFIG_LPS
rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_LEAVE, 1);
#endif /* CONFIG_LPS */
memset(&txmgmt, 0x00, sizeof(struct tdls_txmgmt));
if (peer)
memcpy(txmgmt.peer, peer, ETH_ALEN);
if (rtw_tdls_is_driver_setup(padapter)) {
/* these two cases are done by driver itself */
if (oper == NL80211_TDLS_ENABLE_LINK || oper == NL80211_TDLS_DISABLE_LINK)
return 0;
}
switch (oper) {
case NL80211_TDLS_DISCOVERY_REQ:
issue_tdls_dis_req(padapter, &txmgmt);
break;
case NL80211_TDLS_SETUP:
#ifdef CONFIG_WFD
if (_AES_ != padapter->securitypriv.dot11PrivacyAlgrthm) {
if (padapter->wdinfo.wfd_tdls_weaksec )
issue_tdls_setup_req(padapter, &txmgmt, true);
else
RTW_INFO("[%s] Current link is not AES, SKIP sending the tdls setup request!!\n", __func__);
} else
#endif /* CONFIG_WFD */
{
issue_tdls_setup_req(padapter, &txmgmt, true);
}
break;
case NL80211_TDLS_TEARDOWN:
ptdls_sta = rtw_get_stainfo(&(padapter->stapriv), txmgmt.peer);
if (ptdls_sta != NULL) {
txmgmt.status_code = _RSON_TDLS_TEAR_UN_RSN_;
issue_tdls_teardown(padapter, &txmgmt, true);
} else
RTW_INFO("TDLS peer not found\n");
break;
case NL80211_TDLS_ENABLE_LINK:
RTW_INFO(FUNC_NDEV_FMT", NL80211_TDLS_ENABLE_LINK;mac:"MAC_FMT"\n", FUNC_NDEV_ARG(ndev), MAC_ARG(peer));
ptdls_sta = rtw_get_stainfo(&(padapter->stapriv), (u8 *)peer);
if (ptdls_sta != NULL) {
ptdlsinfo->link_established = true;
ptdls_sta->tdls_sta_state |= TDLS_LINKED_STATE;
ptdls_sta->state |= _FW_LINKED;
rtw_tdls_cmd(padapter, txmgmt.peer, TDLS_ESTABLISHED);
}
break;
case NL80211_TDLS_DISABLE_LINK:
RTW_INFO(FUNC_NDEV_FMT", NL80211_TDLS_DISABLE_LINK;mac:"MAC_FMT"\n", FUNC_NDEV_ARG(ndev), MAC_ARG(peer));
ptdls_sta = rtw_get_stainfo(&(padapter->stapriv), (u8 *)peer);
if (ptdls_sta != NULL)
rtw_tdls_cmd(padapter, (u8 *)peer, TDLS_TEARDOWN_STA_LOCALLY);
break;
}
return 0;
}
#endif /* CONFIG_TDLS */
#if defined(CONFIG_PNO_SUPPORT) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
static int cfg80211_rtw_sched_scan_start(struct wiphy *wiphy,
struct net_device *dev,
struct cfg80211_sched_scan_request *request)
{
_adapter *padapter = (_adapter *)rtw_netdev_priv(dev);
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
u8 ret;
if (padapter->bup == false) {
RTW_INFO("%s: net device is down.\n", __func__);
return -EIO;
}
if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) ||
check_fwstate(pmlmepriv, _FW_LINKED) ||
check_fwstate(pmlmepriv, _FW_UNDER_LINKING) ) {
RTW_INFO("%s: device is busy.\n", __func__);
rtw_scan_abort(padapter);
}
if (request == NULL) {
RTW_INFO("%s: invalid cfg80211_requests parameters.\n", __func__);
return -EINVAL;
}
ret = rtw_android_cfg80211_pno_setup(dev, request->ssids,
request->n_ssids, request->interval);
if (ret < 0) {
RTW_INFO("%s ret: %d\n", __func__, ret);
goto exit;
}
ret = rtw_android_pno_enable(dev, true);
if (ret < 0) {
RTW_INFO("%s ret: %d\n", __func__, ret);
goto exit;
}
exit:
return ret;
}
static int cfg80211_rtw_sched_scan_stop(struct wiphy *wiphy,
struct net_device *dev)
{
return rtw_android_pno_enable(dev, false);
}
#endif /* CONFIG_PNO_SUPPORT */
static int rtw_cfg80211_set_beacon_wpsp2pie(struct net_device *ndev, char *buf, int len)
{
int ret = 0;
uint wps_ielen = 0;
u8 *wps_ie;
u32 p2p_ielen = 0;
u8 wps_oui[8] = {0x0, 0x50, 0xf2, 0x04};
u8 *p2p_ie;
u32 wfd_ielen = 0;
u8 *wfd_ie;
_adapter *padapter = (_adapter *)rtw_netdev_priv(ndev);
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv);
RTW_INFO(FUNC_NDEV_FMT" ielen=%d\n", FUNC_NDEV_ARG(ndev), len);
if (len > 0) {
wps_ie = rtw_get_wps_ie(buf, len, NULL, &wps_ielen);
if (wps_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("bcn_wps_ielen=%d\n", wps_ielen);
#endif
if (pmlmepriv->wps_beacon_ie) {
u32 free_len = pmlmepriv->wps_beacon_ie_len;
pmlmepriv->wps_beacon_ie_len = 0;
rtw_mfree(pmlmepriv->wps_beacon_ie, free_len);
pmlmepriv->wps_beacon_ie = NULL;
}
pmlmepriv->wps_beacon_ie = rtw_malloc(wps_ielen);
if (pmlmepriv->wps_beacon_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->wps_beacon_ie, wps_ie, wps_ielen);
pmlmepriv->wps_beacon_ie_len = wps_ielen;
update_beacon(padapter, _VENDOR_SPECIFIC_IE_, wps_oui, true);
}
/* buf += wps_ielen; */
/* len -= wps_ielen; */
#ifdef CONFIG_P2P
p2p_ie = rtw_get_p2p_ie(buf, len, NULL, &p2p_ielen);
if (p2p_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("bcn_p2p_ielen=%d\n", p2p_ielen);
#endif
if (pmlmepriv->p2p_beacon_ie) {
u32 free_len = pmlmepriv->p2p_beacon_ie_len;
pmlmepriv->p2p_beacon_ie_len = 0;
rtw_mfree(pmlmepriv->p2p_beacon_ie, free_len);
pmlmepriv->p2p_beacon_ie = NULL;
}
pmlmepriv->p2p_beacon_ie = rtw_malloc(p2p_ielen);
if (pmlmepriv->p2p_beacon_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->p2p_beacon_ie, p2p_ie, p2p_ielen);
pmlmepriv->p2p_beacon_ie_len = p2p_ielen;
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WFD
wfd_ie = rtw_get_wfd_ie(buf, len, NULL, &wfd_ielen);
if (wfd_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("bcn_wfd_ielen=%d\n", wfd_ielen);
#endif
if (rtw_mlme_update_wfd_ie_data(pmlmepriv, MLME_BEACON_IE, wfd_ie, wfd_ielen) != _SUCCESS)
return -EINVAL;
}
#endif /* CONFIG_WFD */
pmlmeext->bstart_bss = true;
}
return ret;
}
static int rtw_cfg80211_set_probe_resp_wpsp2pie(struct net_device *net, char *buf, int len)
{
int ret = 0;
uint wps_ielen = 0;
u8 *wps_ie;
u32 p2p_ielen = 0;
u8 *p2p_ie;
u32 wfd_ielen = 0;
u8 *wfd_ie;
_adapter *padapter = (_adapter *)rtw_netdev_priv(net);
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, ielen=%d\n", __func__, len);
#endif
if (len > 0) {
wps_ie = rtw_get_wps_ie(buf, len, NULL, &wps_ielen);
if (wps_ie) {
uint attr_contentlen = 0;
__be16 uconfig_method, *puconfig_method = NULL;
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("probe_resp_wps_ielen=%d\n", wps_ielen);
#endif
if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS)) {
u8 sr = 0;
rtw_get_wps_attr_content(wps_ie, wps_ielen, WPS_ATTR_SELECTED_REGISTRAR, (u8 *)(&sr), NULL);
if (sr != 0)
RTW_INFO("%s, got sr\n", __func__);
else {
RTW_INFO("GO mode process WPS under site-survey, sr no set\n");
return ret;
}
}
if (pmlmepriv->wps_probe_resp_ie) {
u32 free_len = pmlmepriv->wps_probe_resp_ie_len;
pmlmepriv->wps_probe_resp_ie_len = 0;
rtw_mfree(pmlmepriv->wps_probe_resp_ie, free_len);
pmlmepriv->wps_probe_resp_ie = NULL;
}
pmlmepriv->wps_probe_resp_ie = rtw_malloc(wps_ielen);
if (pmlmepriv->wps_probe_resp_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
/* add PUSH_BUTTON config_method by driver self in wpsie of probe_resp at GO Mode */
puconfig_method = (__be16 *)rtw_get_wps_attr_content(wps_ie, wps_ielen, WPS_ATTR_CONF_METHOD , NULL, &attr_contentlen);
if (puconfig_method != NULL) {
/* struct registry_priv *pregistrypriv = &padapter->registrypriv; */
struct wireless_dev *wdev = padapter->rtw_wdev;
#ifdef CONFIG_DEBUG_CFG80211
/* printk("config_method in wpsie of probe_resp = 0x%x\n", be16_to_cpu(*puconfig_method)); */
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
/* for WIFI-DIRECT LOGO 4.2.2, AUTO GO can't set PUSH_BUTTON flags */
if (wdev->iftype == NL80211_IFTYPE_P2P_GO) {
uconfig_method = cpu_to_be16(WPS_CM_PUSH_BUTTON);
*puconfig_method &= ~uconfig_method;
}
#endif
}
memcpy(pmlmepriv->wps_probe_resp_ie, wps_ie, wps_ielen);
pmlmepriv->wps_probe_resp_ie_len = wps_ielen;
}
/* buf += wps_ielen; */
/* len -= wps_ielen; */
#ifdef CONFIG_P2P
p2p_ie = rtw_get_p2p_ie(buf, len, NULL, &p2p_ielen);
if (p2p_ie) {
u8 is_GO = false;
u32 attr_contentlen = 0;
u16 cap_attr = 0;
__le16 le_tmp;
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("probe_resp_p2p_ielen=%d\n", p2p_ielen);
#endif
/* Check P2P Capability ATTR */
if (rtw_get_p2p_attr_content(p2p_ie, p2p_ielen, P2P_ATTR_CAPABILITY, (u8 *)&le_tmp, (uint *)&attr_contentlen)) {
u8 grp_cap = 0;
/* RTW_INFO( "[%s] Got P2P Capability Attr!!\n", __func__ ); */
cap_attr = le16_to_cpu(le_tmp);
grp_cap = (u8)((cap_attr >> 8) & 0xff);
is_GO = (grp_cap & BIT(0)) ? true : false;
if (is_GO)
RTW_INFO("Got P2P Capability Attr, grp_cap=0x%x, is_GO\n", grp_cap);
}
if (is_GO == false) {
if (pmlmepriv->p2p_probe_resp_ie) {
u32 free_len = pmlmepriv->p2p_probe_resp_ie_len;
pmlmepriv->p2p_probe_resp_ie_len = 0;
rtw_mfree(pmlmepriv->p2p_probe_resp_ie, free_len);
pmlmepriv->p2p_probe_resp_ie = NULL;
}
pmlmepriv->p2p_probe_resp_ie = rtw_malloc(p2p_ielen);
if (pmlmepriv->p2p_probe_resp_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->p2p_probe_resp_ie, p2p_ie, p2p_ielen);
pmlmepriv->p2p_probe_resp_ie_len = p2p_ielen;
} else {
if (pmlmepriv->p2p_go_probe_resp_ie) {
u32 free_len = pmlmepriv->p2p_go_probe_resp_ie_len;
pmlmepriv->p2p_go_probe_resp_ie_len = 0;
rtw_mfree(pmlmepriv->p2p_go_probe_resp_ie, free_len);
pmlmepriv->p2p_go_probe_resp_ie = NULL;
}
pmlmepriv->p2p_go_probe_resp_ie = rtw_malloc(p2p_ielen);
if (pmlmepriv->p2p_go_probe_resp_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->p2p_go_probe_resp_ie, p2p_ie, p2p_ielen);
pmlmepriv->p2p_go_probe_resp_ie_len = p2p_ielen;
}
}
#endif /* CONFIG_P2P */
#ifdef CONFIG_WFD
wfd_ie = rtw_get_wfd_ie(buf, len, NULL, &wfd_ielen);
if (wfd_ie) {
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("probe_resp_wfd_ielen=%d\n", wfd_ielen);
#endif
if (rtw_mlme_update_wfd_ie_data(pmlmepriv, MLME_PROBE_RESP_IE, wfd_ie, wfd_ielen) != _SUCCESS)
return -EINVAL;
}
#endif /* CONFIG_WFD */
}
return ret;
}
static int rtw_cfg80211_set_assoc_resp_wpsp2pie(struct net_device *net, char *buf, int len)
{
int ret = 0;
_adapter *padapter = (_adapter *)rtw_netdev_priv(net);
struct mlme_priv *pmlmepriv = &(padapter->mlmepriv);
u8 *ie;
u32 ie_len;
RTW_INFO("%s, ielen=%d\n", __func__, len);
if (len <= 0)
goto exit;
ie = rtw_get_wps_ie(buf, len, NULL, &ie_len);
if (ie && ie_len) {
if (pmlmepriv->wps_assoc_resp_ie) {
u32 free_len = pmlmepriv->wps_assoc_resp_ie_len;
pmlmepriv->wps_assoc_resp_ie_len = 0;
rtw_mfree(pmlmepriv->wps_assoc_resp_ie, free_len);
pmlmepriv->wps_assoc_resp_ie = NULL;
}
pmlmepriv->wps_assoc_resp_ie = rtw_malloc(ie_len);
if (pmlmepriv->wps_assoc_resp_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->wps_assoc_resp_ie, ie, ie_len);
pmlmepriv->wps_assoc_resp_ie_len = ie_len;
}
ie = rtw_get_p2p_ie(buf, len, NULL, &ie_len);
if (ie && ie_len) {
if (pmlmepriv->p2p_assoc_resp_ie) {
u32 free_len = pmlmepriv->p2p_assoc_resp_ie_len;
pmlmepriv->p2p_assoc_resp_ie_len = 0;
rtw_mfree(pmlmepriv->p2p_assoc_resp_ie, free_len);
pmlmepriv->p2p_assoc_resp_ie = NULL;
}
pmlmepriv->p2p_assoc_resp_ie = rtw_malloc(ie_len);
if (pmlmepriv->p2p_assoc_resp_ie == NULL) {
RTW_INFO("%s()-%d: rtw_malloc() ERROR!\n", __func__, __LINE__);
return -EINVAL;
}
memcpy(pmlmepriv->p2p_assoc_resp_ie, ie, ie_len);
pmlmepriv->p2p_assoc_resp_ie_len = ie_len;
}
#ifdef CONFIG_WFD
ie = rtw_get_wfd_ie(buf, len, NULL, &ie_len);
if (rtw_mlme_update_wfd_ie_data(pmlmepriv, MLME_ASSOC_RESP_IE, ie, ie_len) != _SUCCESS)
return -EINVAL;
#endif
exit:
return ret;
}
int rtw_cfg80211_set_mgnt_wpsp2pie(struct net_device *net, char *buf, int len,
int type)
{
int ret = 0;
uint wps_ielen = 0;
u32 p2p_ielen = 0;
#ifdef CONFIG_DEBUG_CFG80211
RTW_INFO("%s, ielen=%d\n", __func__, len);
#endif
if ((rtw_get_wps_ie(buf, len, NULL, &wps_ielen) && (wps_ielen > 0))
#ifdef CONFIG_P2P
|| (rtw_get_p2p_ie(buf, len, NULL, &p2p_ielen) && (p2p_ielen > 0))
#endif
) {
if (net != NULL) {
switch (type) {
case 0x1: /* BEACON */
ret = rtw_cfg80211_set_beacon_wpsp2pie(net, buf, len);
break;
case 0x2: /* PROBE_RESP */
ret = rtw_cfg80211_set_probe_resp_wpsp2pie(net, buf, len);
#ifdef CONFIG_P2P
if (ret == 0)
adapter_wdev_data((_adapter *)rtw_netdev_priv(net))->probe_resp_ie_update_time = jiffies;
#endif
break;
case 0x4: /* ASSOC_RESP */
ret = rtw_cfg80211_set_assoc_resp_wpsp2pie(net, buf, len);
break;
}
}
}
return ret;
}
static void rtw_cfg80211_init_ht_capab_ex(_adapter *padapter, struct ieee80211_sta_ht_cap *ht_cap, enum nl80211_band band, u8 rf_type)
{
struct registry_priv *pregistrypriv = &padapter->registrypriv;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
struct ht_priv *phtpriv = &pmlmepriv->htpriv;
u8 stbc_rx_enable = false;
rtw_ht_use_default_setting(padapter);
/* RX LDPC */
if (TEST_FLAG(phtpriv->ldpc_cap, LDPC_HT_ENABLE_RX))
ht_cap->cap |= IEEE80211_HT_CAP_LDPC_CODING;
/* TX STBC */
if (TEST_FLAG(phtpriv->stbc_cap, STBC_HT_ENABLE_TX))
ht_cap->cap |= IEEE80211_HT_CAP_TX_STBC;
/* RX STBC */
if (TEST_FLAG(phtpriv->stbc_cap, STBC_HT_ENABLE_RX)) {
/*rtw_rx_stbc 0: disable, bit(0):enable 2.4g, bit(1):enable 5g*/
if (band == NL80211_BAND_2GHZ)
stbc_rx_enable = (pregistrypriv->rx_stbc & BIT(0)) ? true : false;
if (band == NL80211_BAND_5GHZ)
stbc_rx_enable = (pregistrypriv->rx_stbc & BIT(1)) ? true : false;
if (stbc_rx_enable) {
switch (rf_type) {
case RF_1T1R:
ht_cap->cap |= IEEE80211_HT_CAP_RX_STBC_1R;/*RX STBC One spatial stream*/
break;
case RF_2T2R:
case RF_1T2R:
ht_cap->cap |= IEEE80211_HT_CAP_RX_STBC_1R;/* Only one spatial-stream STBC RX is supported */
break;
case RF_3T3R:
case RF_3T4R:
case RF_4T4R:
ht_cap->cap |= IEEE80211_HT_CAP_RX_STBC_1R;/* Only one spatial-stream STBC RX is supported */
break;
default:
RTW_INFO("[warning] rf_type %d is not expected\n", rf_type);
break;
}
}
}
}
static void rtw_cfg80211_init_ht_capab(_adapter *padapter, struct ieee80211_sta_ht_cap *ht_cap, enum nl80211_band band, u8 rf_type)
{
struct hal_spec_t *hal_spec = GET_HAL_SPEC(padapter);
u8 rx_nss = 0;
ht_cap->ht_supported = true;
ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_DSSSCCK40 | IEEE80211_HT_CAP_MAX_AMSDU;
rtw_cfg80211_init_ht_capab_ex(padapter, ht_cap, band, rf_type);
/*
*Maximum length of AMPDU that the STA can receive.
*Length = 2 ^ (13 + max_ampdu_length_exp) - 1 (octets)
*/
ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
/*Minimum MPDU start spacing , */
ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
rx_nss = rtw_min(rf_type_to_rf_rx_cnt(rf_type), hal_spec->rx_nss_num);
switch (rx_nss) {
case 1:
ht_cap->mcs.rx_mask[0] = 0xFF;
break;
case 2:
ht_cap->mcs.rx_mask[0] = 0xFF;
ht_cap->mcs.rx_mask[1] = 0xFF;
break;
case 3:
ht_cap->mcs.rx_mask[0] = 0xFF;
ht_cap->mcs.rx_mask[1] = 0xFF;
ht_cap->mcs.rx_mask[2] = 0xFF;
break;
case 4:
ht_cap->mcs.rx_mask[0] = 0xFF;
ht_cap->mcs.rx_mask[1] = 0xFF;
ht_cap->mcs.rx_mask[2] = 0xFF;
ht_cap->mcs.rx_mask[3] = 0xFF;
break;
default:
rtw_warn_on(1);
RTW_INFO("%s, error rf_type=%d\n", __func__, rf_type);
};
ht_cap->mcs.rx_highest =
cpu_to_le16(rtw_mcs_rate(rf_type,
hal_is_bw_support(padapter,
CHANNEL_WIDTH_40),
hal_is_bw_support(padapter,
CHANNEL_WIDTH_40) ?
ht_cap->cap & IEEE80211_HT_CAP_SGI_40 :
ht_cap->cap & IEEE80211_HT_CAP_SGI_20,
ht_cap->mcs.rx_mask));
}
void rtw_cfg80211_init_wdev_data(_adapter *padapter)
{
#ifdef CONFIG_CONCURRENT_MODE
struct rtw_wdev_priv *pwdev_priv = adapter_wdev_data(padapter);
ATOMIC_SET(&pwdev_priv->switch_ch_to, 1);
#endif
}
void rtw_cfg80211_init_wiphy(_adapter *padapter)
{
u8 rf_type;
struct ieee80211_supported_band *bands;
struct wireless_dev *pwdev = padapter->rtw_wdev;
struct wiphy *wiphy = pwdev->wiphy;
rtw_hal_get_hwreg(padapter, HW_VAR_RF_TYPE, (u8 *)(&rf_type));
RTW_INFO("%s:rf_type=%d\n", __func__, rf_type);
if (IsSupported24G(padapter->registrypriv.wireless_mode)) {
bands = wiphy->bands[NL80211_BAND_2GHZ];
if (bands)
rtw_cfg80211_init_ht_capab(padapter, &bands->ht_cap, NL80211_BAND_2GHZ, rf_type);
}
#ifdef CONFIG_IEEE80211_BAND_5GHZ
if (is_supported_5g(padapter->registrypriv.wireless_mode)) {
bands = wiphy->bands[NL80211_BAND_5GHZ];
if (bands)
rtw_cfg80211_init_ht_capab(padapter, &bands->ht_cap, NL80211_BAND_5GHZ, rf_type);
}
#endif
/* copy mac_addr to wiphy */
memcpy(wiphy->perm_addr, adapter_mac_addr(padapter), ETH_ALEN);
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
static struct ieee80211_iface_limit rtw_limits[] = {
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION)
#if defined(CONFIG_P2P) && ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE))
| BIT(NL80211_IFTYPE_P2P_CLIENT)
#endif
},
#ifdef CONFIG_AP_MODE
{
.max = 1,
.types = BIT(NL80211_IFTYPE_AP)
#if defined(CONFIG_P2P) && ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE))
| BIT(NL80211_IFTYPE_P2P_GO)
#endif
},
#endif
#if defined(RTW_DEDICATED_P2P_DEVICE)
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_DEVICE)
}
#endif
};
static struct ieee80211_iface_combination rtw_combinations[] = {
{
.limits = rtw_limits,
.n_limits = ARRAY_SIZE(rtw_limits),
#if defined(RTW_DEDICATED_P2P_DEVICE)
.max_interfaces = 3,
#else
.max_interfaces = 2,
#endif
.num_different_channels = 1,
},
};
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) */
static void rtw_cfg80211_preinit_wiphy(_adapter *adapter, struct wiphy *wiphy)
{
struct dvobj_priv *dvobj = adapter_to_dvobj(adapter);
struct registry_priv *regsty = dvobj_to_regsty(dvobj);
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->max_scan_ssids = RTW_SSID_SCAN_AMOUNT;
wiphy->max_scan_ie_len = RTW_SCAN_IE_LEN_MAX;
wiphy->max_num_pmkids = RTW_MAX_NUM_PMKIDS;
#if CONFIG_RTW_MACADDR_ACL && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
wiphy->max_acl_mac_addrs = NUM_ACL;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)) || defined(COMPAT_KERNEL_RELEASE)
wiphy->max_remain_on_channel_duration = RTW_MAX_REMAIN_ON_CHANNEL_DURATION;
#endif
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION)
| BIT(NL80211_IFTYPE_ADHOC)
#ifdef CONFIG_AP_MODE
| BIT(NL80211_IFTYPE_AP)
#ifdef CONFIG_WIFI_MONITOR
| BIT(NL80211_IFTYPE_MONITOR)
#endif
#endif
#if defined(CONFIG_P2P) && ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE))
| BIT(NL80211_IFTYPE_P2P_CLIENT)
| BIT(NL80211_IFTYPE_P2P_GO)
#if defined(RTW_DEDICATED_P2P_DEVICE)
| BIT(NL80211_IFTYPE_P2P_DEVICE)
#endif
#endif
;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
#ifdef CONFIG_AP_MODE
wiphy->mgmt_stypes = rtw_cfg80211_default_mgmt_stypes;
#endif /* CONFIG_AP_MODE */
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
#ifdef CONFIG_WIFI_MONITOR
wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
#endif
#endif
#if defined(RTW_SINGLE_WIPHY) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
wiphy->iface_combinations = rtw_combinations;
wiphy->n_iface_combinations = ARRAY_SIZE(rtw_combinations);
#endif
wiphy->cipher_suites = rtw_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(rtw_cipher_suites);
if (IsSupported24G(adapter->registrypriv.wireless_mode))
wiphy->bands[NL80211_BAND_2GHZ] = rtw_spt_band_alloc(NL80211_BAND_2GHZ);
#ifdef CONFIG_IEEE80211_BAND_5GHZ
if (is_supported_5g(adapter->registrypriv.wireless_mode))
wiphy->bands[NL80211_BAND_5GHZ] = rtw_spt_band_alloc(NL80211_BAND_5GHZ);
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0))
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS;
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
/* remove WIPHY_FLAG_OFFCHAN_TX, because we not support this feature */
/* wiphy->flags |= WIPHY_FLAG_OFFCHAN_TX | WIPHY_FLAG_HAVE_AP_SME; */
#endif
#if defined(CONFIG_PM) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
#else // kernel >= 4.12
wiphy->max_sched_scan_reqs = 1;
#endif
#ifdef CONFIG_PNO_SUPPORT
wiphy->max_sched_scan_ssids = MAX_PNO_LIST_COUNT;
#endif
#endif
#if defined(CONFIG_PM) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0))
wiphy->wowlan = wowlan_stub;
#else
wiphy->wowlan = &wowlan_stub;
#endif
#endif
#if defined(CONFIG_TDLS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
#ifndef CONFIG_TDLS_DRIVER_SETUP
wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP; /* Driver handles key exchange */
wiphy->flags |= NL80211_ATTR_HT_CAPABILITY;
#endif /* CONFIG_TDLS_DRIVER_SETUP */
#endif /* CONFIG_TDLS */
if (regsty->power_mgnt != PS_MODE_ACTIVE)
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
else
wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
/* wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; */
#endif
}
#ifdef CONFIG_RFKILL_POLL
void rtw_cfg80211_init_rfkill(struct wiphy *wiphy)
{
wiphy_rfkill_set_hw_state(wiphy, 0);
wiphy_rfkill_start_polling(wiphy);
}
void rtw_cfg80211_deinit_rfkill(struct wiphy *wiphy)
{
wiphy_rfkill_stop_polling(wiphy);
}
static void cfg80211_rtw_rfkill_poll(struct wiphy *wiphy)
{
_adapter *padapter = NULL;
bool blocked = false;
u8 valid = 0;
padapter = wiphy_to_adapter(wiphy);
if (adapter_to_dvobj(padapter)->processing_dev_remove ) {
/*RTW_INFO("cfg80211_rtw_rfkill_poll: device is removed!\n");*/
return;
}
blocked = rtw_hal_rfkill_poll(padapter, &valid);
/*RTW_INFO("cfg80211_rtw_rfkill_poll: valid=%d, blocked=%d\n",
valid, blocked);*/
if (valid)
wiphy_rfkill_set_hw_state(wiphy, blocked);
}
#endif
static struct cfg80211_ops rtw_cfg80211_ops = {
.change_virtual_intf = cfg80211_rtw_change_iface,
.add_key = cfg80211_rtw_add_key,
.get_key = cfg80211_rtw_get_key,
.del_key = cfg80211_rtw_del_key,
.set_default_key = cfg80211_rtw_set_default_key,
#if defined(CONFIG_GTK_OL) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0))
.set_rekey_data = cfg80211_rtw_set_rekey_data,
#endif /*CONFIG_GTK_OL*/
.get_station = cfg80211_rtw_get_station,
.scan = cfg80211_rtw_scan,
.set_wiphy_params = cfg80211_rtw_set_wiphy_params,
.connect = cfg80211_rtw_connect,
.disconnect = cfg80211_rtw_disconnect,
.join_ibss = cfg80211_rtw_join_ibss,
.leave_ibss = cfg80211_rtw_leave_ibss,
.set_tx_power = cfg80211_rtw_set_txpower,
.get_tx_power = cfg80211_rtw_get_txpower,
.set_power_mgmt = cfg80211_rtw_set_power_mgmt,
.set_pmksa = cfg80211_rtw_set_pmksa,
.del_pmksa = cfg80211_rtw_del_pmksa,
.flush_pmksa = cfg80211_rtw_flush_pmksa,
#ifdef CONFIG_AP_MODE
.add_virtual_intf = cfg80211_rtw_add_virtual_intf,
.del_virtual_intf = cfg80211_rtw_del_virtual_intf,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)) && !defined(COMPAT_KERNEL_RELEASE)
.add_beacon = cfg80211_rtw_add_beacon,
.set_beacon = cfg80211_rtw_set_beacon,
.del_beacon = cfg80211_rtw_del_beacon,
#else
.start_ap = cfg80211_rtw_start_ap,
.change_beacon = cfg80211_rtw_change_beacon,
.stop_ap = cfg80211_rtw_stop_ap,
#endif
#if CONFIG_RTW_MACADDR_ACL && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
.set_mac_acl = cfg80211_rtw_set_mac_acl,
#endif
.add_station = cfg80211_rtw_add_station,
.del_station = cfg80211_rtw_del_station,
.change_station = cfg80211_rtw_change_station,
.dump_station = cfg80211_rtw_dump_station,
.change_bss = cfg80211_rtw_change_bss,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
.set_channel = cfg80211_rtw_set_channel,
#endif
/* .auth = cfg80211_rtw_auth, */
/* .assoc = cfg80211_rtw_assoc, */
#endif /* CONFIG_AP_MODE */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
.set_monitor_channel = cfg80211_rtw_set_monitor_channel,
#endif
#ifdef CONFIG_P2P
.remain_on_channel = cfg80211_rtw_remain_on_channel,
.cancel_remain_on_channel = cfg80211_rtw_cancel_remain_on_channel,
#if defined(RTW_DEDICATED_P2P_DEVICE)
.start_p2p_device = cfg80211_rtw_start_p2p_device,
.stop_p2p_device = cfg80211_rtw_stop_p2p_device,
#endif
#endif /* CONFIG_P2P */
#ifdef CONFIG_RTW_80211R
.update_ft_ies = cfg80211_rtw_update_ft_ies,
#endif
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)) || defined(COMPAT_KERNEL_RELEASE)
.mgmt_tx = cfg80211_rtw_mgmt_tx,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0)
.mgmt_frame_register = cfg80211_rtw_mgmt_frame_register,
#endif
#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35))
.action = cfg80211_rtw_mgmt_tx,
#endif
#if defined(CONFIG_TDLS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
.tdls_mgmt = cfg80211_rtw_tdls_mgmt,
.tdls_oper = cfg80211_rtw_tdls_oper,
#endif /* CONFIG_TDLS */
#if defined(CONFIG_PNO_SUPPORT) && (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0))
.sched_scan_start = cfg80211_rtw_sched_scan_start,
.sched_scan_stop = cfg80211_rtw_sched_scan_stop,
#endif /* CONFIG_PNO_SUPPORT */
#ifdef CONFIG_RFKILL_POLL
.rfkill_poll = cfg80211_rtw_rfkill_poll,
#endif
};
struct wiphy *rtw_wiphy_alloc(_adapter *padapter, struct device *dev)
{
struct wiphy *wiphy;
struct rtw_wiphy_data *wiphy_data;
/* wiphy */
wiphy = wiphy_new(&rtw_cfg80211_ops, sizeof(struct rtw_wiphy_data));
if (!wiphy) {
RTW_INFO("Couldn't allocate wiphy device\n");
goto exit;
}
set_wiphy_dev(wiphy, dev);
/* wiphy_data */
wiphy_data = rtw_wiphy_priv(wiphy);
wiphy_data->dvobj = adapter_to_dvobj(padapter);
#ifndef RTW_SINGLE_WIPHY
wiphy_data->adapter = padapter;
#endif
rtw_cfg80211_preinit_wiphy(padapter, wiphy);
RTW_INFO(FUNC_WIPHY_FMT"\n", FUNC_WIPHY_ARG(wiphy));
exit:
return wiphy;
}
void rtw_wiphy_free(struct wiphy *wiphy)
{
if (!wiphy)
return;
RTW_INFO(FUNC_WIPHY_FMT"\n", FUNC_WIPHY_ARG(wiphy));
if (wiphy->bands[NL80211_BAND_2GHZ]) {
rtw_spt_band_free(wiphy->bands[NL80211_BAND_2GHZ]);
wiphy->bands[NL80211_BAND_2GHZ] = NULL;
}
if (wiphy->bands[NL80211_BAND_5GHZ]) {
rtw_spt_band_free(wiphy->bands[NL80211_BAND_5GHZ]);
wiphy->bands[NL80211_BAND_5GHZ] = NULL;
}
wiphy_free(wiphy);
}
int rtw_wiphy_register(struct wiphy *wiphy)
{
RTW_INFO(FUNC_WIPHY_FMT"\n", FUNC_WIPHY_ARG(wiphy));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(RTW_VENDOR_EXT_SUPPORT)
rtw_cfgvendor_attach(wiphy);
#endif
return wiphy_register(wiphy);
}
void rtw_wiphy_unregister(struct wiphy *wiphy)
{
RTW_INFO(FUNC_WIPHY_FMT"\n", FUNC_WIPHY_ARG(wiphy));
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)) || defined(RTW_VENDOR_EXT_SUPPORT)
rtw_cfgvendor_detach(wiphy);
#endif
#if defined(RTW_DEDICATED_P2P_DEVICE)
rtw_pd_iface_free(wiphy);
#endif
return wiphy_unregister(wiphy);
}
int rtw_wdev_alloc(_adapter *padapter, struct wiphy *wiphy)
{
int ret = 0;
struct net_device *pnetdev = padapter->pnetdev;
struct wireless_dev *wdev;
struct rtw_wdev_priv *pwdev_priv;
RTW_INFO("%s(padapter=%p)\n", __func__, padapter);
/* wdev */
wdev = (struct wireless_dev *)rtw_zmalloc(sizeof(struct wireless_dev));
if (!wdev) {
RTW_INFO("Couldn't allocate wireless device\n");
ret = -ENOMEM;
goto exit;
}
wdev->wiphy = wiphy;
wdev->netdev = pnetdev;
wdev->iftype = NL80211_IFTYPE_STATION; /* will be init in rtw_hal_init() */
/* Must sync with _rtw_init_mlme_priv() */
/* pmlmepriv->fw_state = WIFI_STATION_STATE */
/* wdev->iftype = NL80211_IFTYPE_MONITOR; */ /* for rtw_setopmode_cmd() in cfg80211_rtw_change_iface() */
padapter->rtw_wdev = wdev;
pnetdev->ieee80211_ptr = wdev;
/* init pwdev_priv */
pwdev_priv = adapter_wdev_data(padapter);
pwdev_priv->rtw_wdev = wdev;
pwdev_priv->pmon_ndev = NULL;
pwdev_priv->ifname_mon[0] = '\0';
pwdev_priv->padapter = padapter;
pwdev_priv->scan_request = NULL;
spin_lock_init(&pwdev_priv->scan_req_lock);
pwdev_priv->p2p_enabled = false;
pwdev_priv->probe_resp_ie_update_time = jiffies;
pwdev_priv->provdisc_req_issued = false;
rtw_wdev_invit_info_init(&pwdev_priv->invit_info);
rtw_wdev_nego_info_init(&pwdev_priv->nego_info);
pwdev_priv->bandroid_scan = false;
if (padapter->registrypriv.power_mgnt != PS_MODE_ACTIVE)
pwdev_priv->power_mgmt = true;
else
pwdev_priv->power_mgmt = false;
_rtw_mutex_init(&pwdev_priv->roch_mutex);
#ifdef CONFIG_CONCURRENT_MODE
ATOMIC_SET(&pwdev_priv->switch_ch_to, 1);
#endif
/* init regulary domain */
rtw_regd_init(padapter);
exit:
return ret;
}
void rtw_wdev_free(struct wireless_dev *wdev)
{
if (!wdev)
return;
RTW_INFO("%s(wdev=%p)\n", __func__, wdev);
if (wdev_to_ndev(wdev)) {
_adapter *adapter = (_adapter *)rtw_netdev_priv(wdev_to_ndev(wdev));
struct rtw_wdev_priv *wdev_priv = adapter_wdev_data(adapter);
_rtw_mutex_free(&wdev_priv->roch_mutex);
}
rtw_mfree((u8 *)wdev, sizeof(struct wireless_dev));
}
void rtw_wdev_unregister(struct wireless_dev *wdev)
{
struct net_device *ndev;
_adapter *adapter;
struct rtw_wdev_priv *pwdev_priv;
if (!wdev)
return;
RTW_INFO("%s(wdev=%p)\n", __func__, wdev);
ndev = wdev_to_ndev(wdev);
if (!ndev)
return;
adapter = (_adapter *)rtw_netdev_priv(ndev);
pwdev_priv = adapter_wdev_data(adapter);
rtw_cfg80211_indicate_scan_done(adapter, true);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
if (wdev->current_bss) {
u8 locally_generated = 1;
RTW_INFO(FUNC_ADPT_FMT" clear current_bss by cfg80211_disconnected\n", FUNC_ADPT_ARG(adapter));
cfg80211_disconnected(adapter->pnetdev, 0, NULL, 0, locally_generated, GFP_ATOMIC);
}
#elif ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0))) || defined(COMPAT_KERNEL_RELEASE)
if (wdev->current_bss) {
RTW_INFO(FUNC_ADPT_FMT" clear current_bss by cfg80211_disconnected\n", FUNC_ADPT_ARG(adapter));
cfg80211_disconnected(adapter->pnetdev, 0, NULL, 0, GFP_ATOMIC);
}
#endif
if (pwdev_priv->pmon_ndev) {
RTW_INFO("%s, unregister monitor interface\n", __func__);
unregister_netdev(pwdev_priv->pmon_ndev);
}
}
int rtw_cfg80211_ndev_res_alloc(_adapter *adapter)
{
int ret = _FAIL;
#if !defined(RTW_SINGLE_WIPHY)
struct wiphy *wiphy;
struct device *dev = dvobj_to_dev(adapter_to_dvobj(adapter));
wiphy = rtw_wiphy_alloc(adapter, dev);
if (wiphy == NULL)
goto exit;
adapter->wiphy = wiphy;
#endif
if (rtw_wdev_alloc(adapter, adapter_to_wiphy(adapter)) == 0)
ret = _SUCCESS;
#if !defined(RTW_SINGLE_WIPHY)
if (ret != _SUCCESS) {
rtw_wiphy_free(wiphy);
adapter->wiphy = NULL;
}
#endif
exit:
return ret;
}
void rtw_cfg80211_ndev_res_free(_adapter *adapter)
{
rtw_wdev_free(adapter->rtw_wdev);
#if !defined(RTW_SINGLE_WIPHY)
rtw_wiphy_free(adapter_to_wiphy(adapter));
adapter->wiphy = NULL;
#endif
}
int rtw_cfg80211_ndev_res_register(_adapter *adapter)
{
int ret = _FAIL;
#if !defined(RTW_SINGLE_WIPHY)
if (rtw_wiphy_register(adapter_to_wiphy(adapter)) < 0) {
RTW_INFO("%s rtw_wiphy_register fail for if%d\n", __func__, (adapter->iface_id + 1));
goto exit;
}
#ifdef CONFIG_RFKILL_POLL
rtw_cfg80211_init_rfkill(adapter_to_wiphy(adapter));
#endif
#endif
ret = _SUCCESS;
exit:
return ret;
}
void rtw_cfg80211_ndev_res_unregister(_adapter *adapter)
{
rtw_wdev_unregister(adapter->rtw_wdev);
}
int rtw_cfg80211_dev_res_alloc(struct dvobj_priv *dvobj)
{
int ret = _FAIL;
#if defined(RTW_SINGLE_WIPHY)
struct wiphy *wiphy;
struct device *dev = dvobj_to_dev(dvobj);
wiphy = rtw_wiphy_alloc(dvobj_get_primary_adapter(dvobj), dev);
if (wiphy == NULL)
goto exit;
dvobj->wiphy = wiphy;
#endif
ret = _SUCCESS;
exit:
return ret;
}
void rtw_cfg80211_dev_res_free(struct dvobj_priv *dvobj)
{
#if defined(RTW_SINGLE_WIPHY)
rtw_wiphy_free(dvobj_to_wiphy(dvobj));
dvobj->wiphy = NULL;
#endif
}
int rtw_cfg80211_dev_res_register(struct dvobj_priv *dvobj)
{
int ret = _FAIL;
#if defined(RTW_SINGLE_WIPHY)
if (rtw_wiphy_register(dvobj_to_wiphy(dvobj)) != 0)
goto exit;
#ifdef CONFIG_RFKILL_POLL
rtw_cfg80211_init_rfkill(dvobj_to_wiphy(dvobj));
#endif
#endif
ret = _SUCCESS;
exit:
return ret;
}
void rtw_cfg80211_dev_res_unregister(struct dvobj_priv *dvobj)
{
#if defined(RTW_SINGLE_WIPHY)
#ifdef CONFIG_RFKILL_POLL
rtw_cfg80211_deinit_rfkill(dvobj_to_wiphy(dvobj));
#endif
rtw_wiphy_unregister(dvobj_to_wiphy(dvobj));
#endif
}
#endif /* CONFIG_IOCTL_CFG80211 */