mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-30 07:34:12 +08:00
It has been a while since my last pull request, so we accumulated
another relatively large set of changes: * TDLS off-channel support set from Arik/Liad, with some support patches I did * custom regulatory fixes from Arik * minstrel VHT fix (and a small optimisation) from Felix * add back radiotap vendor namespace support (myself) * random MAC address scanning for cfg80211/mac80211/hwsim (myself) * CSA improvements (Luca) * WoWLAN Net Detect (wake on network found) support (Luca) * and lots of other smaller changes from many people -----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJUbghkAAoJEDBSmw7B7bqroosP/RABYXUMua+k3Ccq7T4eU4jV AEO2p2gt5nHBzEl1NCJtdUTJkZ6ftT7ehvAkV2zboB0FBoAoBTbZ8YDtcBBiWaY6 wJ5TYuOl6LFo7csAxWxpCzUxW3M+iq26itpyW9Zt9WWxP4QLSNPFyXEXV3SEh45n HcDW9A0SE+mgdaTQ2LEMBJ5XWxG/Ic7i9Xn6Py3o4x7NsTB4EqFNOD0WXcPCq7M0 H8xlsIYIBYoGNMsV/2Nu7CEgcSXfDLqWcs9uPHQMCvWPjx/vIoEyOgTwJlE9bQHh 2tloc1LBP6XKQ6g2bJ/pBaQVnZGugcOJhD6KUq3ckNm9qIP1ZtRmJslH4V6pUSCS eGl3TcAKSSE4BWIa7AaETWXeoH4X68dO7PM7pnflQRCQLzCJRbDWCdqjBst/AxBT 6hvAFAvExEcWBkNVSTJ2egRk/C9cDFKRaCWQ1h4wX9yvh+8efe1D0DDWLW9a9qv1 LsoGJE72BZdXn2CaQEME+CjTd3fWmn6u729d/c863cq2kspCSOof0QD0X9uWhBUx BvqtgbQjGZzAvHFcjBd7yRd5hz0aDfLyBL59bq2IBzaU1QmyekNPqzSMSD+5ZlCp uxEeE5AY2+pcNZV1KRtkvgAByfUgAVd0FHZcVb8SIM6QZ3IhqiOuzxuXtxv6hrYP V/76B+ath4Sv1IPF56ex =q4e6 -----END PGP SIGNATURE----- Merge tag 'mac80211-next-for-john-2014-11-20' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next Johannes Berg <johannes@sipsolutions.net> says: "It has been a while since my last pull request, so we accumulated another relatively large set of changes: * TDLS off-channel support set from Arik/Liad, with some support patches I did * custom regulatory fixes from Arik * minstrel VHT fix (and a small optimisation) from Felix * add back radiotap vendor namespace support (myself) * random MAC address scanning for cfg80211/mac80211/hwsim (myself) * CSA improvements (Luca) * WoWLAN Net Detect (wake on network found) support (Luca) * and lots of other smaller changes from many people" Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
commit
9a638ddfb0
@ -547,7 +547,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
|
||||
|
||||
static void
|
||||
ath5k_sw_scan_start(struct ieee80211_hw *hw)
|
||||
ath5k_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct ath5k_hw *ah = hw->priv;
|
||||
if (!ah->assoc)
|
||||
@ -556,7 +558,7 @@ ath5k_sw_scan_start(struct ieee80211_hw *hw)
|
||||
|
||||
|
||||
static void
|
||||
ath5k_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
ath5k_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ath5k_hw *ah = hw->priv;
|
||||
ath5k_hw_set_ledstate(ah, ah->assoc ?
|
||||
|
@ -963,7 +963,7 @@ static void ath_scan_send_probe(struct ath_softc *sc,
|
||||
struct ieee80211_tx_info *info;
|
||||
int band = sc->offchannel.chan.chandef.chan->band;
|
||||
|
||||
skb = ieee80211_probereq_get(sc->hw, vif,
|
||||
skb = ieee80211_probereq_get(sc->hw, vif->addr,
|
||||
ssid->ssid, ssid->ssid_len, req->ie_len);
|
||||
if (!skb)
|
||||
return;
|
||||
|
@ -1691,7 +1691,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
|
||||
static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct ath9k_htc_priv *priv = hw->priv;
|
||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||
@ -1705,7 +1707,8 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
||||
static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ath9k_htc_priv *priv = hw->priv;
|
||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||
|
@ -2183,14 +2183,17 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
|
||||
static void ath9k_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct ath_softc *sc = hw->priv;
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
set_bit(ATH_OP_SCANNING, &common->op_flags);
|
||||
}
|
||||
|
||||
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void ath9k_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ath_softc *sc = hw->priv;
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
|
@ -494,7 +494,9 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
|
||||
static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct wcn36xx *wcn = hw->priv;
|
||||
|
||||
@ -502,7 +504,8 @@ static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
|
||||
wcn36xx_smd_start_scan(wcn);
|
||||
}
|
||||
|
||||
static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wcn36xx *wcn = hw->priv;
|
||||
|
||||
|
@ -5110,7 +5110,9 @@ static void b43_op_sta_notify(struct ieee80211_hw *hw,
|
||||
B43_WARN_ON(!vif || wl->vif != vif);
|
||||
}
|
||||
|
||||
static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw)
|
||||
static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct b43_wl *wl = hw_to_b43_wl(hw);
|
||||
struct b43_wldev *dev;
|
||||
@ -5124,7 +5126,8 @@ static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw)
|
||||
mutex_unlock(&wl->mutex);
|
||||
}
|
||||
|
||||
static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw)
|
||||
static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct b43_wl *wl = hw_to_b43_wl(hw);
|
||||
struct b43_wldev *dev;
|
||||
|
@ -764,7 +764,9 @@ brcms_ops_configure_filter(struct ieee80211_hw *hw,
|
||||
return;
|
||||
}
|
||||
|
||||
static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw)
|
||||
static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct brcms_info *wl = hw->priv;
|
||||
spin_lock_bh(&wl->lock);
|
||||
@ -773,7 +775,8 @@ static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw)
|
||||
return;
|
||||
}
|
||||
|
||||
static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct brcms_info *wl = hw->priv;
|
||||
spin_lock_bh(&wl->lock);
|
||||
|
@ -78,7 +78,7 @@ int cw1200_hw_scan(struct ieee80211_hw *hw,
|
||||
if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS)
|
||||
return -EINVAL;
|
||||
|
||||
frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
|
||||
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
|
||||
req->ie_len);
|
||||
if (!frame.skb)
|
||||
return -ENOMEM;
|
||||
|
@ -415,6 +415,8 @@ struct mac80211_hwsim_data {
|
||||
bool destroy_on_close;
|
||||
struct work_struct destroy_work;
|
||||
u32 portid;
|
||||
char alpha2[2];
|
||||
const struct ieee80211_regdomain *regd;
|
||||
|
||||
struct ieee80211_channel *tmp_chan;
|
||||
struct delayed_work roc_done;
|
||||
@ -422,6 +424,7 @@ struct mac80211_hwsim_data {
|
||||
struct cfg80211_scan_request *hw_scan_request;
|
||||
struct ieee80211_vif *hw_scan_vif;
|
||||
int scan_chan_idx;
|
||||
u8 scan_addr[ETH_ALEN];
|
||||
|
||||
struct ieee80211_channel *channel;
|
||||
u64 beacon_int /* beacon interval in us */;
|
||||
@ -830,6 +833,9 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
|
||||
.ret = false,
|
||||
};
|
||||
|
||||
if (data->scanning && memcmp(addr, data->scan_addr, ETH_ALEN) == 0)
|
||||
return true;
|
||||
|
||||
memcpy(md.addr, addr, ETH_ALEN);
|
||||
|
||||
ieee80211_iterate_active_interfaces_atomic(data->hw,
|
||||
@ -984,6 +990,53 @@ static void mac80211_hwsim_tx_iter(void *_data, u8 *addr,
|
||||
data->receive = true;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_add_vendor_rtap(struct sk_buff *skb)
|
||||
{
|
||||
/*
|
||||
* To enable this code, #define the HWSIM_RADIOTAP_OUI,
|
||||
* e.g. like this:
|
||||
* #define HWSIM_RADIOTAP_OUI "\x02\x00\x00"
|
||||
* (but you should use a valid OUI, not that)
|
||||
*
|
||||
* If anyone wants to 'donate' a radiotap OUI/subns code
|
||||
* please send a patch removing this #ifdef and changing
|
||||
* the values accordingly.
|
||||
*/
|
||||
#ifdef HWSIM_RADIOTAP_OUI
|
||||
struct ieee80211_vendor_radiotap *rtap;
|
||||
|
||||
/*
|
||||
* Note that this code requires the headroom in the SKB
|
||||
* that was allocated earlier.
|
||||
*/
|
||||
rtap = (void *)skb_push(skb, sizeof(*rtap) + 8 + 4);
|
||||
rtap->oui[0] = HWSIM_RADIOTAP_OUI[0];
|
||||
rtap->oui[1] = HWSIM_RADIOTAP_OUI[1];
|
||||
rtap->oui[2] = HWSIM_RADIOTAP_OUI[2];
|
||||
rtap->subns = 127;
|
||||
|
||||
/*
|
||||
* Radiotap vendor namespaces can (and should) also be
|
||||
* split into fields by using the standard radiotap
|
||||
* presence bitmap mechanism. Use just BIT(0) here for
|
||||
* the presence bitmap.
|
||||
*/
|
||||
rtap->present = BIT(0);
|
||||
/* We have 8 bytes of (dummy) data */
|
||||
rtap->len = 8;
|
||||
/* For testing, also require it to be aligned */
|
||||
rtap->align = 8;
|
||||
/* And also test that padding works, 4 bytes */
|
||||
rtap->pad = 4;
|
||||
/* push the data */
|
||||
memcpy(rtap->data, "ABCDEFGH", 8);
|
||||
/* make sure to clear padding, mac80211 doesn't */
|
||||
memset(rtap->data + 8, 0, 4);
|
||||
|
||||
IEEE80211_SKB_RXCB(skb)->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
|
||||
struct sk_buff *skb,
|
||||
struct ieee80211_channel *chan)
|
||||
@ -1098,6 +1151,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
|
||||
rx_status.mactime = now + data2->tsf_offset;
|
||||
|
||||
memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
|
||||
|
||||
mac80211_hwsim_add_vendor_rtap(nskb);
|
||||
|
||||
data2->rx_pkts++;
|
||||
data2->rx_bytes += nskb->len;
|
||||
ieee80211_rx_irqsafe(data2->hw, nskb);
|
||||
@ -1752,7 +1808,7 @@ static void hw_scan_work(struct work_struct *work)
|
||||
struct sk_buff *probe;
|
||||
|
||||
probe = ieee80211_probereq_get(hwsim->hw,
|
||||
hwsim->hw_scan_vif,
|
||||
hwsim->scan_addr,
|
||||
req->ssids[i].ssid,
|
||||
req->ssids[i].ssid_len,
|
||||
req->ie_len);
|
||||
@ -1790,6 +1846,12 @@ static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
|
||||
hwsim->hw_scan_request = req;
|
||||
hwsim->hw_scan_vif = vif;
|
||||
hwsim->scan_chan_idx = 0;
|
||||
if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
|
||||
get_random_mask_addr(hwsim->scan_addr,
|
||||
hw_req->req.mac_addr,
|
||||
hw_req->req.mac_addr_mask);
|
||||
else
|
||||
memcpy(hwsim->scan_addr, vif->addr, ETH_ALEN);
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
|
||||
wiphy_debug(hw->wiphy, "hwsim hw_scan request\n");
|
||||
@ -1816,7 +1878,9 @@ static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw,
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw)
|
||||
static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
|
||||
@ -1828,13 +1892,16 @@ static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw)
|
||||
}
|
||||
|
||||
printk(KERN_DEBUG "hwsim sw_scan request, prepping stuff\n");
|
||||
|
||||
memcpy(hwsim->scan_addr, mac_addr, ETH_ALEN);
|
||||
hwsim->scanning = true;
|
||||
|
||||
out:
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mac80211_hwsim_data *hwsim = hw->priv;
|
||||
|
||||
@ -1842,6 +1909,7 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
|
||||
printk(KERN_DEBUG "hwsim sw_scan_complete\n");
|
||||
hwsim->scanning = false;
|
||||
memset(hwsim->scan_addr, 0, ETH_ALEN);
|
||||
|
||||
mutex_unlock(&hwsim->mutex);
|
||||
}
|
||||
@ -2057,36 +2125,26 @@ static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
|
||||
HWSIM_MCGRP_CONFIG, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static struct sk_buff *build_radio_msg(int cmd, int id,
|
||||
struct hwsim_new_radio_params *param)
|
||||
static int append_radio_msg(struct sk_buff *skb, int id,
|
||||
struct hwsim_new_radio_params *param)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
void *data;
|
||||
int ret;
|
||||
|
||||
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0, cmd);
|
||||
if (!data)
|
||||
goto error;
|
||||
|
||||
ret = nla_put_u32(skb, HWSIM_ATTR_RADIO_ID, id);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
|
||||
if (param->channels) {
|
||||
ret = nla_put_u32(skb, HWSIM_ATTR_CHANNELS, param->channels);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (param->reg_alpha2) {
|
||||
ret = nla_put(skb, HWSIM_ATTR_REG_HINT_ALPHA2, 2,
|
||||
param->reg_alpha2);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (param->regd) {
|
||||
@ -2099,54 +2157,64 @@ static struct sk_buff *build_radio_msg(int cmd, int id,
|
||||
if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) {
|
||||
ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (param->reg_strict) {
|
||||
ret = nla_put_flag(skb, HWSIM_ATTR_REG_STRICT_REG);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (param->p2p_device) {
|
||||
ret = nla_put_flag(skb, HWSIM_ATTR_SUPPORT_P2P_DEVICE);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (param->use_chanctx) {
|
||||
ret = nla_put_flag(skb, HWSIM_ATTR_USE_CHANCTX);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (param->hwname) {
|
||||
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME,
|
||||
strlen(param->hwname), param->hwname);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
return ret;
|
||||
}
|
||||
|
||||
genlmsg_end(skb, data);
|
||||
|
||||
return skb;
|
||||
|
||||
error:
|
||||
nlmsg_free(skb);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hswim_mcast_new_radio(int id, struct genl_info *info,
|
||||
static void hwsim_mcast_new_radio(int id, struct genl_info *info,
|
||||
struct hwsim_new_radio_params *param)
|
||||
{
|
||||
struct sk_buff *mcast_skb;
|
||||
void *data;
|
||||
|
||||
mcast_skb = build_radio_msg(HWSIM_CMD_NEW_RADIO, id, param);
|
||||
mcast_skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!mcast_skb)
|
||||
return;
|
||||
|
||||
data = genlmsg_put(mcast_skb, 0, 0, &hwsim_genl_family, 0,
|
||||
HWSIM_CMD_NEW_RADIO);
|
||||
if (!data)
|
||||
goto out_err;
|
||||
|
||||
if (append_radio_msg(mcast_skb, id, param) < 0)
|
||||
goto out_err;
|
||||
|
||||
genlmsg_end(mcast_skb, data);
|
||||
|
||||
hwsim_mcast_config_msg(mcast_skb, info);
|
||||
return;
|
||||
|
||||
out_err:
|
||||
genlmsg_cancel(mcast_skb, data);
|
||||
nlmsg_free(mcast_skb);
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
@ -2267,7 +2335,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR |
|
||||
NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
|
||||
NL80211_FEATURE_STATIC_SMPS |
|
||||
NL80211_FEATURE_DYNAMIC_SMPS;
|
||||
NL80211_FEATURE_DYNAMIC_SMPS |
|
||||
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
|
||||
|
||||
/* ask mac80211 to reserve space for magic */
|
||||
hw->vif_data_size = sizeof(struct hwsim_vif_priv);
|
||||
@ -2353,6 +2422,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
if (param->reg_strict)
|
||||
hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG;
|
||||
if (param->regd) {
|
||||
data->regd = param->regd;
|
||||
hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
|
||||
wiphy_apply_custom_regulatory(hw->wiphy, param->regd);
|
||||
/* give the regulatory workqueue a chance to run */
|
||||
@ -2371,8 +2441,11 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
|
||||
wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr);
|
||||
|
||||
if (param->reg_alpha2)
|
||||
if (param->reg_alpha2) {
|
||||
data->alpha2[0] = param->reg_alpha2[0];
|
||||
data->alpha2[1] = param->reg_alpha2[1];
|
||||
regulatory_hint(hw->wiphy, param->reg_alpha2);
|
||||
}
|
||||
|
||||
data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir);
|
||||
debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
|
||||
@ -2392,7 +2465,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
|
||||
if (idx > 0)
|
||||
hswim_mcast_new_radio(idx, info, param);
|
||||
hwsim_mcast_new_radio(idx, info, param);
|
||||
|
||||
return idx;
|
||||
|
||||
@ -2426,12 +2499,10 @@ static void hwsim_mcast_del_radio(int id, const char *hwname,
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
if (hwname) {
|
||||
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname),
|
||||
hwname);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
}
|
||||
ret = nla_put(skb, HWSIM_ATTR_RADIO_NAME, strlen(hwname),
|
||||
hwname);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
genlmsg_end(skb, data);
|
||||
|
||||
@ -2455,6 +2526,44 @@ static void mac80211_hwsim_del_radio(struct mac80211_hwsim_data *data,
|
||||
ieee80211_free_hw(data->hw);
|
||||
}
|
||||
|
||||
static int mac80211_hwsim_get_radio(struct sk_buff *skb,
|
||||
struct mac80211_hwsim_data *data,
|
||||
u32 portid, u32 seq,
|
||||
struct netlink_callback *cb, int flags)
|
||||
{
|
||||
void *hdr;
|
||||
struct hwsim_new_radio_params param = { };
|
||||
int res = -EMSGSIZE;
|
||||
|
||||
hdr = genlmsg_put(skb, portid, seq, &hwsim_genl_family, flags,
|
||||
HWSIM_CMD_GET_RADIO);
|
||||
if (!hdr)
|
||||
return -EMSGSIZE;
|
||||
|
||||
if (cb)
|
||||
genl_dump_check_consistent(cb, hdr, &hwsim_genl_family);
|
||||
|
||||
param.reg_alpha2 = data->alpha2;
|
||||
param.reg_strict = !!(data->hw->wiphy->regulatory_flags &
|
||||
REGULATORY_STRICT_REG);
|
||||
param.p2p_device = !!(data->hw->wiphy->interface_modes &
|
||||
BIT(NL80211_IFTYPE_P2P_DEVICE));
|
||||
param.use_chanctx = data->use_chanctx;
|
||||
param.regd = data->regd;
|
||||
param.channels = data->channels;
|
||||
param.hwname = wiphy_name(data->hw->wiphy);
|
||||
|
||||
res = append_radio_msg(skb, data->idx, ¶m);
|
||||
if (res < 0)
|
||||
goto out_err;
|
||||
|
||||
return genlmsg_end(skb, hdr);
|
||||
|
||||
out_err:
|
||||
genlmsg_cancel(skb, hdr);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void mac80211_hwsim_free(void)
|
||||
{
|
||||
struct mac80211_hwsim_data *data;
|
||||
@ -2465,7 +2574,8 @@ static void mac80211_hwsim_free(void)
|
||||
list))) {
|
||||
list_del(&data->list);
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
mac80211_hwsim_del_radio(data, NULL, NULL);
|
||||
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
|
||||
NULL);
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
}
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
@ -2744,14 +2854,14 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
if (data->idx != idx)
|
||||
continue;
|
||||
} else {
|
||||
if (hwname &&
|
||||
strcmp(hwname, wiphy_name(data->hw->wiphy)))
|
||||
if (strcmp(hwname, wiphy_name(data->hw->wiphy)))
|
||||
continue;
|
||||
}
|
||||
|
||||
list_del(&data->list);
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
mac80211_hwsim_del_radio(data, hwname, info);
|
||||
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
|
||||
info);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
@ -2759,6 +2869,77 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
|
||||
{
|
||||
struct mac80211_hwsim_data *data;
|
||||
struct sk_buff *skb;
|
||||
int idx, res = -ENODEV;
|
||||
|
||||
if (!info->attrs[HWSIM_ATTR_RADIO_ID])
|
||||
return -EINVAL;
|
||||
idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
|
||||
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
list_for_each_entry(data, &hwsim_radios, list) {
|
||||
if (data->idx != idx)
|
||||
continue;
|
||||
|
||||
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
res = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
res = mac80211_hwsim_get_radio(skb, data, info->snd_portid,
|
||||
info->snd_seq, NULL, 0);
|
||||
if (res < 0) {
|
||||
nlmsg_free(skb);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
genlmsg_reply(skb, info);
|
||||
break;
|
||||
}
|
||||
|
||||
out_err:
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int hwsim_dump_radio_nl(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
int idx = cb->args[0];
|
||||
struct mac80211_hwsim_data *data = NULL;
|
||||
int res;
|
||||
|
||||
spin_lock_bh(&hwsim_radio_lock);
|
||||
|
||||
if (idx == hwsim_radio_idx)
|
||||
goto done;
|
||||
|
||||
list_for_each_entry(data, &hwsim_radios, list) {
|
||||
if (data->idx < idx)
|
||||
continue;
|
||||
|
||||
res = mac80211_hwsim_get_radio(skb, data,
|
||||
NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, cb,
|
||||
NLM_F_MULTI);
|
||||
if (res < 0)
|
||||
break;
|
||||
|
||||
idx = data->idx + 1;
|
||||
}
|
||||
|
||||
cb->args[0] = idx;
|
||||
|
||||
done:
|
||||
spin_unlock_bh(&hwsim_radio_lock);
|
||||
return skb->len;
|
||||
}
|
||||
|
||||
/* Generic Netlink operations array */
|
||||
static const struct genl_ops hwsim_ops[] = {
|
||||
{
|
||||
@ -2789,6 +2970,12 @@ static const struct genl_ops hwsim_ops[] = {
|
||||
.doit = hwsim_del_radio_nl,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
{
|
||||
.cmd = HWSIM_CMD_GET_RADIO,
|
||||
.policy = hwsim_genl_policy,
|
||||
.doit = hwsim_get_radio_nl,
|
||||
.dumpit = hwsim_dump_radio_nl,
|
||||
},
|
||||
};
|
||||
|
||||
static void destroy_radio(struct work_struct *work)
|
||||
@ -2796,7 +2983,7 @@ static void destroy_radio(struct work_struct *work)
|
||||
struct mac80211_hwsim_data *data =
|
||||
container_of(work, struct mac80211_hwsim_data, destroy_work);
|
||||
|
||||
mac80211_hwsim_del_radio(data, NULL, NULL);
|
||||
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), NULL);
|
||||
}
|
||||
|
||||
static void remove_user_radios(u32 portid)
|
||||
|
@ -69,6 +69,8 @@ enum hwsim_tx_control_flags {
|
||||
* returns the radio ID (>= 0) or negative on errors, if successful
|
||||
* then multicast the result
|
||||
* @HWSIM_CMD_DEL_RADIO: destroy a radio, reply is multicasted
|
||||
* @HWSIM_CMD_GET_RADIO: fetch information about existing radios, uses:
|
||||
* %HWSIM_ATTR_RADIO_ID
|
||||
* @__HWSIM_CMD_MAX: enum limit
|
||||
*/
|
||||
enum {
|
||||
@ -78,6 +80,7 @@ enum {
|
||||
HWSIM_CMD_TX_INFO_FRAME,
|
||||
HWSIM_CMD_NEW_RADIO,
|
||||
HWSIM_CMD_DEL_RADIO,
|
||||
HWSIM_CMD_GET_RADIO,
|
||||
__HWSIM_CMD_MAX,
|
||||
};
|
||||
#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
|
||||
|
@ -5548,7 +5548,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void mwl8k_sw_scan_start(struct ieee80211_hw *hw)
|
||||
static void mwl8k_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct mwl8k_priv *priv = hw->priv;
|
||||
u8 tmp;
|
||||
@ -5565,7 +5567,8 @@ static void mwl8k_sw_scan_start(struct ieee80211_hw *hw)
|
||||
priv->sw_scan_start = true;
|
||||
}
|
||||
|
||||
static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct mwl8k_priv *priv = hw->priv;
|
||||
u8 tmp;
|
||||
|
@ -1437,8 +1437,11 @@ int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw);
|
||||
void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw);
|
||||
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr);
|
||||
void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
int rt2x00mac_get_stats(struct ieee80211_hw *hw,
|
||||
struct ieee80211_low_level_stats *stats);
|
||||
void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
|
||||
|
@ -568,7 +568,9 @@ int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove);
|
||||
|
||||
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw)
|
||||
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct rt2x00_dev *rt2x00dev = hw->priv;
|
||||
set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags);
|
||||
@ -576,7 +578,8 @@ void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start);
|
||||
|
||||
void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct rt2x00_dev *rt2x00dev = hw->priv;
|
||||
clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags);
|
||||
|
@ -1361,7 +1361,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtl_op_sw_scan_start(struct ieee80211_hw *hw)
|
||||
static void rtl_op_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
struct rtl_priv *rtlpriv = rtl_priv(hw);
|
||||
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
|
||||
@ -1396,7 +1398,8 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw)
|
||||
rtlpriv->cfg->ops->scan_operation_backup(hw, SCAN_OPT_BACKUP_BAND0);
|
||||
}
|
||||
|
||||
static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct rtl_priv *rtlpriv = rtl_priv(hw);
|
||||
struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
|
||||
|
@ -1029,7 +1029,7 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
|
||||
goto out_sleep;
|
||||
}
|
||||
|
||||
skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
|
||||
skb = ieee80211_probereq_get(wl->hw, wl->vif->addr, ssid, ssid_len,
|
||||
req->ie_len);
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -1145,7 +1145,7 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
|
||||
wl1271_debug(DEBUG_SCAN, "build probe request band %d", band);
|
||||
|
||||
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
|
||||
skb = ieee80211_probereq_get(wl->hw, vif->addr, ssid, ssid_len,
|
||||
ie0_len + ie1_len);
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -856,7 +856,9 @@ static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vnt_sw_scan_start(struct ieee80211_hw *hw)
|
||||
static void vnt_sw_scan_start(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *addr)
|
||||
{
|
||||
struct vnt_private *priv = hw->priv;
|
||||
|
||||
@ -865,7 +867,8 @@ static void vnt_sw_scan_start(struct ieee80211_hw *hw)
|
||||
vnt_update_pre_ed_threshold(priv, true);
|
||||
}
|
||||
|
||||
static void vnt_sw_scan_complete(struct ieee80211_hw *hw)
|
||||
static void vnt_sw_scan_complete(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct vnt_private *priv = hw->priv;
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/*
|
||||
* DS bit usage
|
||||
@ -1066,6 +1067,12 @@ struct ieee80211_pspoll {
|
||||
|
||||
/* TDLS */
|
||||
|
||||
/* Channel switch timing */
|
||||
struct ieee80211_ch_switch_timing {
|
||||
__le16 switch_time;
|
||||
__le16 switch_timeout;
|
||||
} __packed;
|
||||
|
||||
/* Link-id information element */
|
||||
struct ieee80211_tdls_lnkie {
|
||||
u8 ie_type; /* Link Identifier IE */
|
||||
@ -1107,6 +1114,15 @@ struct ieee80211_tdls_data {
|
||||
u8 dialog_token;
|
||||
u8 variable[0];
|
||||
} __packed discover_req;
|
||||
struct {
|
||||
u8 target_channel;
|
||||
u8 oper_class;
|
||||
u8 variable[0];
|
||||
} __packed chan_switch_req;
|
||||
struct {
|
||||
__le16 status_code;
|
||||
u8 variable[0];
|
||||
} __packed chan_switch_resp;
|
||||
} u;
|
||||
} __packed;
|
||||
|
||||
@ -2018,6 +2034,11 @@ enum ieee80211_tdls_actioncode {
|
||||
*/
|
||||
#define WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING BIT(2)
|
||||
|
||||
/* TDLS capabilities in the the 4th byte of @WLAN_EID_EXT_CAPABILITY */
|
||||
#define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4)
|
||||
#define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5)
|
||||
#define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH BIT(6)
|
||||
|
||||
/* Interworking capabilities are set in 7th bit of 4th byte of the
|
||||
* @WLAN_EID_EXT_CAPABILITY information element
|
||||
*/
|
||||
@ -2029,6 +2050,7 @@ enum ieee80211_tdls_actioncode {
|
||||
*/
|
||||
#define WLAN_EXT_CAPA5_TDLS_ENABLED BIT(5)
|
||||
#define WLAN_EXT_CAPA5_TDLS_PROHIBITED BIT(6)
|
||||
#define WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED BIT(7)
|
||||
|
||||
#define WLAN_EXT_CAPA8_OPMODE_NOTIF BIT(6)
|
||||
#define WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED BIT(7)
|
||||
@ -2036,6 +2058,9 @@ enum ieee80211_tdls_actioncode {
|
||||
/* TDLS specific payload type in the LLC/SNAP header */
|
||||
#define WLAN_TDLS_SNAP_RFTYPE 0x2
|
||||
|
||||
/* BSS Coex IE information field bits */
|
||||
#define WLAN_BSS_COEX_INFORMATION_REQUEST BIT(0)
|
||||
|
||||
/**
|
||||
* enum - mesh synchronization method identifier
|
||||
*
|
||||
@ -2418,6 +2443,30 @@ static inline bool ieee80211_check_tim(const struct ieee80211_tim_ie *tim,
|
||||
return !!(tim->virtual_map[index] & mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_get_tdls_action - get tdls packet action (or -1, if not tdls packet)
|
||||
* @skb: the skb containing the frame, length will not be checked
|
||||
* @hdr_size: the size of the ieee80211_hdr that starts at skb->data
|
||||
*
|
||||
* This function assumes the frame is a data frame, and that the network header
|
||||
* is in the correct place.
|
||||
*/
|
||||
static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
|
||||
{
|
||||
if (!skb_is_nonlinear(skb) &&
|
||||
skb->len > (skb_network_offset(skb) + 2)) {
|
||||
/* Point to where the indication of TDLS should start */
|
||||
const u8 *tdls_data = skb_network_header(skb) - 2;
|
||||
|
||||
if (get_unaligned_be16(tdls_data) == ETH_P_TDLS &&
|
||||
tdls_data[2] == WLAN_TDLS_SNAP_RFTYPE &&
|
||||
tdls_data[3] == WLAN_CATEGORY_TDLS)
|
||||
return tdls_data[4];
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* convert time units */
|
||||
#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
|
||||
#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
|
||||
|
@ -1437,6 +1437,10 @@ struct cfg80211_ssid {
|
||||
* @aborted: (internal) scan request was notified as aborted
|
||||
* @notified: (internal) scan request was notified as done or aborted
|
||||
* @no_cck: used to send probe requests at non CCK rate in 2GHz band
|
||||
* @mac_addr: MAC address used with randomisation
|
||||
* @mac_addr_mask: MAC address mask used with randomisation, bits that
|
||||
* are 0 in the mask should be randomised, bits that are 1 should
|
||||
* be taken from the @mac_addr
|
||||
*/
|
||||
struct cfg80211_scan_request {
|
||||
struct cfg80211_ssid *ssids;
|
||||
@ -1451,6 +1455,9 @@ struct cfg80211_scan_request {
|
||||
|
||||
struct wireless_dev *wdev;
|
||||
|
||||
u8 mac_addr[ETH_ALEN] __aligned(2);
|
||||
u8 mac_addr_mask[ETH_ALEN] __aligned(2);
|
||||
|
||||
/* internal */
|
||||
struct wiphy *wiphy;
|
||||
unsigned long scan_start;
|
||||
@ -1461,6 +1468,17 @@ struct cfg80211_scan_request {
|
||||
struct ieee80211_channel *channels[0];
|
||||
};
|
||||
|
||||
static inline void get_random_mask_addr(u8 *buf, const u8 *addr, const u8 *mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
get_random_bytes(buf, ETH_ALEN);
|
||||
for (i = 0; i < ETH_ALEN; i++) {
|
||||
buf[i] &= ~mask[i];
|
||||
buf[i] |= addr[i] & mask[i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* struct cfg80211_match_set - sets of attributes to match
|
||||
*
|
||||
@ -1494,6 +1512,10 @@ struct cfg80211_match_set {
|
||||
* @channels: channels to scan
|
||||
* @min_rssi_thold: for drivers only supporting a single threshold, this
|
||||
* contains the minimum over all matchsets
|
||||
* @mac_addr: MAC address used with randomisation
|
||||
* @mac_addr_mask: MAC address mask used with randomisation, bits that
|
||||
* are 0 in the mask should be randomised, bits that are 1 should
|
||||
* be taken from the @mac_addr
|
||||
*/
|
||||
struct cfg80211_sched_scan_request {
|
||||
struct cfg80211_ssid *ssids;
|
||||
@ -1508,6 +1530,9 @@ struct cfg80211_sched_scan_request {
|
||||
int n_match_sets;
|
||||
s32 min_rssi_thold;
|
||||
|
||||
u8 mac_addr[ETH_ALEN] __aligned(2);
|
||||
u8 mac_addr_mask[ETH_ALEN] __aligned(2);
|
||||
|
||||
/* internal */
|
||||
struct wiphy *wiphy;
|
||||
struct net_device *dev;
|
||||
@ -1940,6 +1965,7 @@ struct cfg80211_wowlan_tcp {
|
||||
* @rfkill_release: wake up when rfkill is released
|
||||
* @tcp: TCP connection establishment/wakeup parameters, see nl80211.h.
|
||||
* NULL if not configured.
|
||||
* @nd_config: configuration for the scan to be used for net detect wake.
|
||||
*/
|
||||
struct cfg80211_wowlan {
|
||||
bool any, disconnect, magic_pkt, gtk_rekey_failure,
|
||||
@ -1948,6 +1974,7 @@ struct cfg80211_wowlan {
|
||||
struct cfg80211_pkt_pattern *patterns;
|
||||
struct cfg80211_wowlan_tcp *tcp;
|
||||
int n_patterns;
|
||||
struct cfg80211_sched_scan_request *nd_config;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1979,6 +2006,35 @@ struct cfg80211_coalesce {
|
||||
int n_rules;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_wowlan_nd_match - information about the match
|
||||
*
|
||||
* @ssid: SSID of the match that triggered the wake up
|
||||
* @n_channels: Number of channels where the match occurred. This
|
||||
* value may be zero if the driver can't report the channels.
|
||||
* @channels: center frequencies of the channels where a match
|
||||
* occurred (in MHz)
|
||||
*/
|
||||
struct cfg80211_wowlan_nd_match {
|
||||
struct cfg80211_ssid ssid;
|
||||
int n_channels;
|
||||
u32 channels[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_wowlan_nd_info - net detect wake up information
|
||||
*
|
||||
* @n_matches: Number of match information instances provided in
|
||||
* @matches. This value may be zero if the driver can't provide
|
||||
* match information.
|
||||
* @matches: Array of pointers to matches containing information about
|
||||
* the matches that triggered the wake up.
|
||||
*/
|
||||
struct cfg80211_wowlan_nd_info {
|
||||
int n_matches;
|
||||
struct cfg80211_wowlan_nd_match *matches[];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cfg80211_wowlan_wakeup - wakeup report
|
||||
* @disconnect: woke up by getting disconnected
|
||||
@ -1998,6 +2054,7 @@ struct cfg80211_coalesce {
|
||||
* @tcp_match: TCP wakeup packet received
|
||||
* @tcp_connlost: TCP connection lost or failed to establish
|
||||
* @tcp_nomoretokens: TCP data ran out of tokens
|
||||
* @net_detect: if not %NULL, woke up because of net detect
|
||||
*/
|
||||
struct cfg80211_wowlan_wakeup {
|
||||
bool disconnect, magic_pkt, gtk_rekey_failure,
|
||||
@ -2007,6 +2064,7 @@ struct cfg80211_wowlan_wakeup {
|
||||
s32 pattern_idx;
|
||||
u32 packet_present_len, packet_len;
|
||||
const void *packet;
|
||||
struct cfg80211_wowlan_nd_info *net_detect;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2367,6 +2425,12 @@ struct cfg80211_qos_map {
|
||||
* (invoked with the wireless_dev mutex held)
|
||||
* @leave_ocb: leave the current OCB network
|
||||
* (invoked with the wireless_dev mutex held)
|
||||
*
|
||||
* @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
|
||||
* is responsible for continually initiating channel-switching operations
|
||||
* and returning to the base channel for communication with the AP.
|
||||
* @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
|
||||
* peers must be on the base channel when the call completes.
|
||||
*/
|
||||
struct cfg80211_ops {
|
||||
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
|
||||
@ -2622,6 +2686,14 @@ struct cfg80211_ops {
|
||||
u16 admitted_time);
|
||||
int (*del_tx_ts)(struct wiphy *wiphy, struct net_device *dev,
|
||||
u8 tsid, const u8 *peer);
|
||||
|
||||
int (*tdls_channel_switch)(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
const u8 *addr, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
void (*tdls_cancel_channel_switch)(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
const u8 *addr);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2796,6 +2868,7 @@ struct ieee80211_txrx_stypes {
|
||||
* @WIPHY_WOWLAN_EAP_IDENTITY_REQ: supports wakeup on EAP identity request
|
||||
* @WIPHY_WOWLAN_4WAY_HANDSHAKE: supports wakeup on 4-way handshake failure
|
||||
* @WIPHY_WOWLAN_RFKILL_RELEASE: supports wakeup on RF-kill release
|
||||
* @WIPHY_WOWLAN_NET_DETECT: supports wakeup on network detection
|
||||
*/
|
||||
enum wiphy_wowlan_support_flags {
|
||||
WIPHY_WOWLAN_ANY = BIT(0),
|
||||
@ -2806,6 +2879,7 @@ enum wiphy_wowlan_support_flags {
|
||||
WIPHY_WOWLAN_EAP_IDENTITY_REQ = BIT(5),
|
||||
WIPHY_WOWLAN_4WAY_HANDSHAKE = BIT(6),
|
||||
WIPHY_WOWLAN_RFKILL_RELEASE = BIT(7),
|
||||
WIPHY_WOWLAN_NET_DETECT = BIT(8),
|
||||
};
|
||||
|
||||
struct wiphy_wowlan_tcp_support {
|
||||
@ -2824,6 +2898,11 @@ struct wiphy_wowlan_tcp_support {
|
||||
* @pattern_max_len: maximum length of each pattern
|
||||
* @pattern_min_len: minimum length of each pattern
|
||||
* @max_pkt_offset: maximum Rx packet offset
|
||||
* @max_nd_match_sets: maximum number of matchsets for net-detect,
|
||||
* similar, but not necessarily identical, to max_match_sets for
|
||||
* scheduled scans.
|
||||
* See &struct cfg80211_sched_scan_request.@match_sets for more
|
||||
* details.
|
||||
* @tcp: TCP wakeup support information
|
||||
*/
|
||||
struct wiphy_wowlan_support {
|
||||
@ -2832,6 +2911,7 @@ struct wiphy_wowlan_support {
|
||||
int pattern_max_len;
|
||||
int pattern_min_len;
|
||||
int max_pkt_offset;
|
||||
int max_nd_match_sets;
|
||||
const struct wiphy_wowlan_tcp_support *tcp;
|
||||
};
|
||||
|
||||
@ -4719,6 +4799,20 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
|
||||
void cfg80211_ch_switch_notify(struct net_device *dev,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
/*
|
||||
* cfg80211_ch_switch_started_notify - notify channel switch start
|
||||
* @dev: the device on which the channel switch started
|
||||
* @chandef: the future channel definition
|
||||
* @count: the number of TBTTs until the channel switch happens
|
||||
*
|
||||
* Inform the userspace about the channel switch that has just
|
||||
* started, so that it can take appropriate actions (eg. starting
|
||||
* channel switch on other vifs), if necessary.
|
||||
*/
|
||||
void cfg80211_ch_switch_started_notify(struct net_device *dev,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
u8 count);
|
||||
|
||||
/**
|
||||
* ieee80211_operating_class_to_band - convert operating class to band
|
||||
*
|
||||
|
@ -882,6 +882,9 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
|
||||
* subframes share the same sequence number. Reported subframes can be
|
||||
* either regular MSDU or singly A-MSDUs. Subframes must not be
|
||||
* interleaved with other frames.
|
||||
* @RX_FLAG_RADIOTAP_VENDOR_DATA: This frame contains vendor-specific
|
||||
* radiotap data in the skb->data (before the frame) as described by
|
||||
* the &struct ieee80211_vendor_radiotap.
|
||||
*/
|
||||
enum mac80211_rx_flags {
|
||||
RX_FLAG_MMIC_ERROR = BIT(0),
|
||||
@ -911,6 +914,7 @@ enum mac80211_rx_flags {
|
||||
RX_FLAG_10MHZ = BIT(28),
|
||||
RX_FLAG_5MHZ = BIT(29),
|
||||
RX_FLAG_AMSDU_MORE = BIT(30),
|
||||
RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31),
|
||||
};
|
||||
|
||||
#define RX_FLAG_STBC_SHIFT 26
|
||||
@ -981,6 +985,39 @@ struct ieee80211_rx_status {
|
||||
u8 ampdu_delimiter_crc;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_vendor_radiotap - vendor radiotap data information
|
||||
* @present: presence bitmap for this vendor namespace
|
||||
* (this could be extended in the future if any vendor needs more
|
||||
* bits, the radiotap spec does allow for that)
|
||||
* @align: radiotap vendor namespace alignment. This defines the needed
|
||||
* alignment for the @data field below, not for the vendor namespace
|
||||
* description itself (which has a fixed 2-byte alignment)
|
||||
* Must be a power of two, and be set to at least 1!
|
||||
* @oui: radiotap vendor namespace OUI
|
||||
* @subns: radiotap vendor sub namespace
|
||||
* @len: radiotap vendor sub namespace skip length, if alignment is done
|
||||
* then that's added to this, i.e. this is only the length of the
|
||||
* @data field.
|
||||
* @pad: number of bytes of padding after the @data, this exists so that
|
||||
* the skb data alignment can be preserved even if the data has odd
|
||||
* length
|
||||
* @data: the actual vendor namespace data
|
||||
*
|
||||
* This struct, including the vendor data, goes into the skb->data before
|
||||
* the 802.11 header. It's split up in mac80211 using the align/oui/subns
|
||||
* data.
|
||||
*/
|
||||
struct ieee80211_vendor_radiotap {
|
||||
u32 present;
|
||||
u8 align;
|
||||
u8 oui[3];
|
||||
u8 subns;
|
||||
u8 pad;
|
||||
u16 len;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* enum ieee80211_conf_flags - configuration flags
|
||||
*
|
||||
@ -1789,6 +1826,31 @@ struct ieee80211_scan_request {
|
||||
struct cfg80211_scan_request req;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee80211_tdls_ch_sw_params - TDLS channel switch parameters
|
||||
*
|
||||
* @sta: peer this TDLS channel-switch request/response came from
|
||||
* @chandef: channel referenced in a TDLS channel-switch request
|
||||
* @action_code: see &enum ieee80211_tdls_actioncode
|
||||
* @status: channel-switch response status
|
||||
* @timestamp: time at which the frame was received
|
||||
* @switch_time: switch-timing parameter received in the frame
|
||||
* @switch_timeout: switch-timing parameter received in the frame
|
||||
* @tmpl_skb: TDLS switch-channel response template
|
||||
* @ch_sw_tm_ie: offset of the channel-switch timing IE inside @tmpl_skb
|
||||
*/
|
||||
struct ieee80211_tdls_ch_sw_params {
|
||||
struct ieee80211_sta *sta;
|
||||
struct cfg80211_chan_def *chandef;
|
||||
u8 action_code;
|
||||
u32 status;
|
||||
u32 timestamp;
|
||||
u16 switch_time;
|
||||
u16 switch_timeout;
|
||||
struct sk_buff *tmpl_skb;
|
||||
u32 ch_sw_tm_ie;
|
||||
};
|
||||
|
||||
/**
|
||||
* wiphy_to_ieee80211_hw - return a mac80211 driver hw struct from a wiphy
|
||||
*
|
||||
@ -2560,7 +2622,9 @@ enum ieee80211_reconfig_type {
|
||||
*
|
||||
* @sw_scan_start: Notifier function that is called just before a software scan
|
||||
* is started. Can be NULL, if the driver doesn't need this notification.
|
||||
* The callback can sleep.
|
||||
* The mac_addr parameter allows supporting NL80211_SCAN_FLAG_RANDOM_ADDR,
|
||||
* the driver may set the NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR flag if it
|
||||
* can use this parameter. The callback can sleep.
|
||||
*
|
||||
* @sw_scan_complete: Notifier function that is called just after a
|
||||
* software scan finished. Can be NULL, if the driver doesn't need
|
||||
@ -2631,6 +2695,9 @@ enum ieee80211_reconfig_type {
|
||||
* uses hardware rate control (%IEEE80211_HW_HAS_RATE_CONTROL) since
|
||||
* otherwise the rate control algorithm is notified directly.
|
||||
* Must be atomic.
|
||||
* @sta_rate_tbl_update: Notifies the driver that the rate table changed. This
|
||||
* is only used if the configured rate control algorithm actually uses
|
||||
* the new rate table API, and is therefore optional. Must be atomic.
|
||||
*
|
||||
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
|
||||
* bursting) for a hardware TX queue.
|
||||
@ -2878,6 +2945,23 @@ enum ieee80211_reconfig_type {
|
||||
*
|
||||
* @get_txpower: get current maximum tx power (in dBm) based on configuration
|
||||
* and hardware limits.
|
||||
*
|
||||
* @tdls_channel_switch: Start channel-switching with a TDLS peer. The driver
|
||||
* is responsible for continually initiating channel-switching operations
|
||||
* and returning to the base channel for communication with the AP. The
|
||||
* driver receives a channel-switch request template and the location of
|
||||
* the switch-timing IE within the template as part of the invocation.
|
||||
* The template is valid only within the call, and the driver can
|
||||
* optionally copy the skb for further re-use.
|
||||
* @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
|
||||
* peers must be on the base channel when the call completes.
|
||||
* @tdls_recv_channel_switch: a TDLS channel-switch related frame (request or
|
||||
* response) has been received from a remote peer. The driver gets
|
||||
* parameters parsed from the incoming frame and may use them to continue
|
||||
* an ongoing channel-switch operation. In addition, a channel-switch
|
||||
* response template is provided, together with the location of the
|
||||
* switch-timing IE within the template. The skb can only be used within
|
||||
* the function call.
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
@ -2937,8 +3021,11 @@ struct ieee80211_ops {
|
||||
struct ieee80211_scan_ies *ies);
|
||||
int (*sched_scan_stop)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
void (*sw_scan_start)(struct ieee80211_hw *hw);
|
||||
void (*sw_scan_complete)(struct ieee80211_hw *hw);
|
||||
void (*sw_scan_start)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *mac_addr);
|
||||
void (*sw_scan_complete)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif);
|
||||
int (*get_stats)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_low_level_stats *stats);
|
||||
void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx,
|
||||
@ -2972,6 +3059,9 @@ struct ieee80211_ops {
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
u32 changed);
|
||||
void (*sta_rate_tbl_update)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
int (*conf_tx)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif, u16 ac,
|
||||
const struct ieee80211_tx_queue_params *params);
|
||||
@ -3089,6 +3179,18 @@ struct ieee80211_ops {
|
||||
u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
|
||||
int (*get_txpower)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
int *dbm);
|
||||
|
||||
int (*tdls_channel_switch)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie);
|
||||
void (*tdls_cancel_channel_switch)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
void (*tdls_recv_channel_switch)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_tdls_ch_sw_params *params);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3729,7 +3831,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
|
||||
/**
|
||||
* ieee80211_probereq_get - retrieve a Probe Request template
|
||||
* @hw: pointer obtained from ieee80211_alloc_hw().
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
* @src_addr: source MAC address
|
||||
* @ssid: SSID buffer
|
||||
* @ssid_len: length of SSID
|
||||
* @tailroom: tailroom to reserve at end of SKB for IEs
|
||||
@ -3740,7 +3842,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
|
||||
* Return: The Probe Request template. %NULL on error.
|
||||
*/
|
||||
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *src_addr,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
size_t tailroom);
|
||||
|
||||
@ -4979,6 +5081,43 @@ void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
|
||||
enum nl80211_tdls_operation oper,
|
||||
u16 reason_code, gfp_t gfp);
|
||||
|
||||
/**
|
||||
* ieee80211_reserve_tid - request to reserve a specific TID
|
||||
*
|
||||
* There is sometimes a need (such as in TDLS) for blocking the driver from
|
||||
* using a specific TID so that the FW can use it for certain operations such
|
||||
* as sending PTI requests. To make sure that the driver doesn't use that TID,
|
||||
* this function must be called as it flushes out packets on this TID and marks
|
||||
* it as blocked, so that any transmit for the station on this TID will be
|
||||
* redirected to the alternative TID in the same AC.
|
||||
*
|
||||
* Note that this function blocks and may call back into the driver, so it
|
||||
* should be called without driver locks held. Also note this function should
|
||||
* only be called from the driver's @sta_state callback.
|
||||
*
|
||||
* @sta: the station to reserve the TID for
|
||||
* @tid: the TID to reserve
|
||||
*
|
||||
* Returns: 0 on success, else on failure
|
||||
*/
|
||||
int ieee80211_reserve_tid(struct ieee80211_sta *sta, u8 tid);
|
||||
|
||||
/**
|
||||
* ieee80211_unreserve_tid - request to unreserve a specific TID
|
||||
*
|
||||
* Once there is no longer any need for reserving a certain TID, this function
|
||||
* should be called, and no longer will packets have their TID modified for
|
||||
* preventing use of this TID in the driver.
|
||||
*
|
||||
* Note that this function blocks and acquires a lock, so it should be called
|
||||
* without driver locks held. Also note this function should only be called
|
||||
* from the driver's @sta_state callback.
|
||||
*
|
||||
* @sta: the station
|
||||
* @tid: the TID to unreserve
|
||||
*/
|
||||
void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
|
||||
|
||||
/**
|
||||
* ieee80211_ie_split - split an IE buffer according to ordering
|
||||
*
|
||||
|
@ -643,7 +643,18 @@
|
||||
* @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
|
||||
* independently of the userspace SME, send this event indicating
|
||||
* %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
|
||||
* attributes determining channel width.
|
||||
* attributes determining channel width. This indication may also be
|
||||
* sent when a remotely-initiated switch (e.g., when a STA receives a CSA
|
||||
* from the remote AP) is completed;
|
||||
*
|
||||
* @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
|
||||
* has been started on an interface, regardless of the initiator
|
||||
* (ie. whether it was requested from a remote device or
|
||||
* initiated on our own). It indicates that
|
||||
* %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
|
||||
* after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's. The userspace may
|
||||
* decide to react to this indication by requesting other
|
||||
* interfaces to change channel as well.
|
||||
*
|
||||
* @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
|
||||
* its %NL80211_ATTR_WDEV identifier. It must have been created with
|
||||
@ -751,6 +762,18 @@
|
||||
* @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
|
||||
* network is determined by the network interface.
|
||||
*
|
||||
* @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
|
||||
* identified by the %NL80211_ATTR_MAC parameter. A target channel is
|
||||
* provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
|
||||
* channel width/type. The target operating class is given via
|
||||
* %NL80211_ATTR_OPER_CLASS.
|
||||
* The driver is responsible for continually initiating channel-switching
|
||||
* operations and returning to the base channel for communication with the
|
||||
* AP.
|
||||
* @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
|
||||
* peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
|
||||
* when this command completes.
|
||||
*
|
||||
* @NL80211_CMD_MAX: highest used command number
|
||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -930,6 +953,11 @@ enum nl80211_commands {
|
||||
NL80211_CMD_JOIN_OCB,
|
||||
NL80211_CMD_LEAVE_OCB,
|
||||
|
||||
NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
|
||||
|
||||
NL80211_CMD_TDLS_CHANNEL_SWITCH,
|
||||
NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
|
||||
|
||||
/* add new commands above here */
|
||||
|
||||
/* used to define NL80211_CMD_MAX below */
|
||||
@ -1624,9 +1652,9 @@ enum nl80211_commands {
|
||||
* @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
|
||||
* As specified in the &enum nl80211_tdls_peer_capability.
|
||||
*
|
||||
* @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
|
||||
* @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
|
||||
* creation then the new interface will be owned by the netlink socket
|
||||
* that created it and will be destroyed when the socket is closed
|
||||
* that created it and will be destroyed when the socket is closed.
|
||||
*
|
||||
* @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
|
||||
* the TDLS link initiator.
|
||||
@ -1656,6 +1684,11 @@ enum nl80211_commands {
|
||||
* @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
|
||||
* &enum nl80211_smps_mode.
|
||||
*
|
||||
* @NL80211_ATTR_OPER_CLASS: operating class
|
||||
*
|
||||
* @NL80211_ATTR_MAC_MASK: MAC address mask
|
||||
*
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
@ -1991,7 +2024,7 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_TDLS_PEER_CAPABILITY,
|
||||
|
||||
NL80211_ATTR_IFACE_SOCKET_OWNER,
|
||||
NL80211_ATTR_SOCKET_OWNER,
|
||||
|
||||
NL80211_ATTR_CSA_C_OFFSETS_TX,
|
||||
NL80211_ATTR_MAX_CSA_COUNTERS,
|
||||
@ -2008,15 +2041,21 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_SMPS_MODE,
|
||||
|
||||
NL80211_ATTR_OPER_CLASS,
|
||||
|
||||
NL80211_ATTR_MAC_MASK,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
|
||||
NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
/* source-level API compatibility */
|
||||
#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
|
||||
#define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
|
||||
#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
|
||||
|
||||
/*
|
||||
* Allow user space programs to use #ifdef on new attributes by defining them
|
||||
@ -2652,6 +2691,11 @@ enum nl80211_sched_scan_match_attr {
|
||||
* @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
|
||||
* base on contiguous rules and wider channels will be allowed to cross
|
||||
* multiple contiguous/overlapping frequency ranges.
|
||||
* @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT
|
||||
* @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
|
||||
* @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
|
||||
* @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
|
||||
* @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
|
||||
*/
|
||||
enum nl80211_reg_rule_flags {
|
||||
NL80211_RRF_NO_OFDM = 1<<0,
|
||||
@ -2664,11 +2708,18 @@ enum nl80211_reg_rule_flags {
|
||||
NL80211_RRF_NO_IR = 1<<7,
|
||||
__NL80211_RRF_NO_IBSS = 1<<8,
|
||||
NL80211_RRF_AUTO_BW = 1<<11,
|
||||
NL80211_RRF_GO_CONCURRENT = 1<<12,
|
||||
NL80211_RRF_NO_HT40MINUS = 1<<13,
|
||||
NL80211_RRF_NO_HT40PLUS = 1<<14,
|
||||
NL80211_RRF_NO_80MHZ = 1<<15,
|
||||
NL80211_RRF_NO_160MHZ = 1<<16,
|
||||
};
|
||||
|
||||
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
|
||||
#define NL80211_RRF_NO_IBSS NL80211_RRF_NO_IR
|
||||
#define NL80211_RRF_NO_IR NL80211_RRF_NO_IR
|
||||
#define NL80211_RRF_NO_HT40 (NL80211_RRF_NO_HT40MINUS |\
|
||||
NL80211_RRF_NO_HT40PLUS)
|
||||
|
||||
/* For backport compatibility with older userspace */
|
||||
#define NL80211_RRF_NO_IR_ALL (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
|
||||
@ -3566,6 +3617,25 @@ struct nl80211_pattern_support {
|
||||
* @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
|
||||
* the TCP connection ran out of tokens to use for data to send to the
|
||||
* service
|
||||
* @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
|
||||
* is detected. This is a nested attribute that contains the
|
||||
* same attributes used with @NL80211_CMD_START_SCHED_SCAN. It
|
||||
* specifies how the scan is performed (e.g. the interval and the
|
||||
* channels to scan) as well as the scan results that will
|
||||
* trigger a wake (i.e. the matchsets).
|
||||
* @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
|
||||
* containing an array with information about what triggered the
|
||||
* wake up. If no elements are present in the array, it means
|
||||
* that the information is not available. If more than one
|
||||
* element is present, it means that more than one match
|
||||
* occurred.
|
||||
* Each element in the array is a nested attribute that contains
|
||||
* one optional %NL80211_ATTR_SSID attribute and one optional
|
||||
* %NL80211_ATTR_SCAN_FREQUENCIES attribute. At least one of
|
||||
* these attributes must be present. If
|
||||
* %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
|
||||
* frequency, it means that the match occurred in more than one
|
||||
* channel.
|
||||
* @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
|
||||
* @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
|
||||
*
|
||||
@ -3591,6 +3661,8 @@ enum nl80211_wowlan_triggers {
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
|
||||
NL80211_WOWLAN_TRIG_NET_DETECT,
|
||||
NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
|
||||
|
||||
/* keep last */
|
||||
NUM_NL80211_WOWLAN_TRIG,
|
||||
@ -4070,6 +4142,20 @@ enum nl80211_ap_sme_features {
|
||||
* @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
|
||||
* the vif's MAC address upon creation.
|
||||
* See 'macaddr' field in the vif_params (cfg80211.h).
|
||||
* @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
|
||||
* operating as a TDLS peer.
|
||||
* @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
|
||||
* random MAC address during scan (if the device is unassociated); the
|
||||
* %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
|
||||
* address mask/value will be used.
|
||||
* @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
|
||||
* using a random MAC address for every scan iteration during scheduled
|
||||
* scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
|
||||
* be set for scheduled scan and the MAC address mask/value will be used.
|
||||
* @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
|
||||
* random MAC address for every scan iteration during "net detect", i.e.
|
||||
* scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
|
||||
* be set for scheduled scan and the MAC address mask/value will be used.
|
||||
*/
|
||||
enum nl80211_feature_flags {
|
||||
NL80211_FEATURE_SK_TX_STATUS = 1 << 0,
|
||||
@ -4100,6 +4186,10 @@ enum nl80211_feature_flags {
|
||||
NL80211_FEATURE_DYNAMIC_SMPS = 1 << 25,
|
||||
NL80211_FEATURE_SUPPORTS_WMM_ADMISSION = 1 << 26,
|
||||
NL80211_FEATURE_MAC_ON_CREATE = 1 << 27,
|
||||
NL80211_FEATURE_TDLS_CHANNEL_SWITCH = 1 << 28,
|
||||
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR = 1 << 29,
|
||||
NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR = 1 << 30,
|
||||
NL80211_FEATURE_ND_RANDOM_MAC_ADDR = 1 << 31,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -4148,11 +4238,21 @@ enum nl80211_connect_failed_reason {
|
||||
* dangerous because will destroy stations performance as a lot of frames
|
||||
* will be lost while scanning off-channel, therefore it must be used only
|
||||
* when really needed
|
||||
* @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
|
||||
* for scheduled scan: a different one for every scan iteration). When the
|
||||
* flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
|
||||
* @NL80211_ATTR_MAC_MASK attributes may also be given in which case only
|
||||
* the masked bits will be preserved from the MAC address and the remainder
|
||||
* randomised. If the attributes are not given full randomisation (46 bits,
|
||||
* locally administered 1, multicast 0) is assumed.
|
||||
* This flag must not be requested when the feature isn't supported, check
|
||||
* the nl80211 feature flags for the device.
|
||||
*/
|
||||
enum nl80211_scan_flags {
|
||||
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
|
||||
NL80211_SCAN_FLAG_FLUSH = 1<<1,
|
||||
NL80211_SCAN_FLAG_AP = 1<<2,
|
||||
NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -509,6 +509,10 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
|
||||
struct tid_ampdu_tx *tid_tx;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN(sta->reserved_tid == tid,
|
||||
"Requested to start BA session on reserved tid=%d", tid))
|
||||
return -EINVAL;
|
||||
|
||||
trace_api_start_tx_ba_session(pubsta, tid);
|
||||
|
||||
if (WARN_ON_ONCE(!local->ops->ampdu_action))
|
||||
@ -765,6 +769,9 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
WARN(sta->reserved_tid == tid,
|
||||
"Requested to stop BA session on reserved tid=%d", tid);
|
||||
|
||||
if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
|
||||
/* already in progress stopping it */
|
||||
ret = 0;
|
||||
|
@ -1042,6 +1042,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
|
||||
clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
|
||||
}
|
||||
|
||||
/* mark TDLS channel switch support, if the AP allows it */
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
|
||||
!sdata->u.mgd.tdls_chan_switch_prohibited &&
|
||||
params->ext_capab_len >= 4 &&
|
||||
params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
|
||||
set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
|
||||
|
||||
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
|
||||
sta->sta.uapsd_queues = params->uapsd_queues;
|
||||
sta->sta.max_sp = params->max_sp;
|
||||
@ -3158,6 +3165,12 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ch_switch.timestamp = 0;
|
||||
ch_switch.device_timestamp = 0;
|
||||
ch_switch.block_tx = params->block_tx;
|
||||
ch_switch.chandef = params->chandef;
|
||||
ch_switch.count = params->count;
|
||||
|
||||
err = drv_pre_channel_switch(sdata, &ch_switch);
|
||||
if (err)
|
||||
goto out;
|
||||
@ -3175,12 +3188,6 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
ch_switch.timestamp = 0;
|
||||
ch_switch.device_timestamp = 0;
|
||||
ch_switch.block_tx = params->block_tx;
|
||||
ch_switch.chandef = params->chandef;
|
||||
ch_switch.count = params->count;
|
||||
|
||||
err = ieee80211_set_csa_beacon(sdata, params, &changed);
|
||||
if (err) {
|
||||
ieee80211_vif_unreserve_chanctx(sdata);
|
||||
@ -3195,6 +3202,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
ieee80211_stop_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
cfg80211_ch_switch_started_notify(sdata->dev, &sdata->csa_chandef,
|
||||
params->count);
|
||||
|
||||
if (changed) {
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
drv_channel_switch_beacon(sdata, ¶ms->chandef);
|
||||
@ -3511,6 +3521,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
||||
|
||||
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
|
||||
IEEE80211_TX_INTFL_NL80211_FRAME_TX;
|
||||
info->band = band;
|
||||
|
||||
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
|
||||
skb->priority = 7;
|
||||
@ -3518,7 +3529,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
|
||||
nullfunc->qos_ctrl = cpu_to_le16(7);
|
||||
|
||||
local_bh_disable();
|
||||
ieee80211_xmit(sdata, skb, band);
|
||||
ieee80211_xmit(sdata, skb);
|
||||
local_bh_enable();
|
||||
rcu_read_unlock();
|
||||
|
||||
@ -3741,6 +3752,8 @@ const struct cfg80211_ops mac80211_config_ops = {
|
||||
.set_rekey_data = ieee80211_set_rekey_data,
|
||||
.tdls_oper = ieee80211_tdls_oper,
|
||||
.tdls_mgmt = ieee80211_tdls_mgmt,
|
||||
.tdls_channel_switch = ieee80211_tdls_channel_switch,
|
||||
.tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
|
||||
.probe_client = ieee80211_probe_client,
|
||||
.set_noack_map = ieee80211_set_noack_map,
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -74,7 +74,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
|
||||
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
|
||||
|
||||
int res = scnprintf(buf, sizeof(buf),
|
||||
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
|
||||
TEST(PS_DRIVER), TEST(AUTHORIZED),
|
||||
TEST(SHORT_PREAMBLE),
|
||||
@ -82,10 +82,11 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
|
||||
TEST(WDS), TEST(CLEAR_PS_FILT),
|
||||
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
|
||||
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
|
||||
TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
|
||||
TEST(INSERTED), TEST(RATE_CONTROL),
|
||||
TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER),
|
||||
TEST(MPSP_RECIPIENT));
|
||||
TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR),
|
||||
TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL),
|
||||
TEST(4ADDR_EVENT), TEST(INSERTED),
|
||||
TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN),
|
||||
TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT));
|
||||
#undef TEST
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
|
||||
}
|
||||
|
@ -380,23 +380,26 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void drv_sw_scan_start(struct ieee80211_local *local)
|
||||
static inline void drv_sw_scan_start(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *mac_addr)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
trace_drv_sw_scan_start(local);
|
||||
trace_drv_sw_scan_start(local, sdata, mac_addr);
|
||||
if (local->ops->sw_scan_start)
|
||||
local->ops->sw_scan_start(&local->hw);
|
||||
local->ops->sw_scan_start(&local->hw, &sdata->vif, mac_addr);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline void drv_sw_scan_complete(struct ieee80211_local *local)
|
||||
static inline void drv_sw_scan_complete(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
trace_drv_sw_scan_complete(local);
|
||||
trace_drv_sw_scan_complete(local, sdata);
|
||||
if (local->ops->sw_scan_complete)
|
||||
local->ops->sw_scan_complete(&local->hw);
|
||||
local->ops->sw_scan_complete(&local->hw, &sdata->vif);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
@ -621,6 +624,21 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
sdata = get_bss_sdata(sdata);
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return;
|
||||
|
||||
trace_drv_sta_rate_tbl_update(local, sdata, sta);
|
||||
if (local->ops->sta_rate_tbl_update)
|
||||
local->ops->sta_rate_tbl_update(&local->hw, &sdata->vif, sta);
|
||||
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline int drv_conf_tx(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata, u16 ac,
|
||||
const struct ieee80211_tx_queue_params *params)
|
||||
@ -1296,4 +1314,57 @@ static inline int drv_get_txpower(struct ieee80211_local *local,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_tdls_channel_switch(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta *sta, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return -EIO;
|
||||
|
||||
if (!local->ops->tdls_channel_switch)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
trace_drv_tdls_channel_switch(local, sdata, sta, oper_class, chandef);
|
||||
ret = local->ops->tdls_channel_switch(&local->hw, &sdata->vif, sta,
|
||||
oper_class, chandef, tmpl_skb,
|
||||
ch_sw_tm_ie);
|
||||
trace_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
drv_tdls_cancel_channel_switch(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
might_sleep();
|
||||
if (!check_sdata_in_driver(sdata))
|
||||
return;
|
||||
|
||||
if (!local->ops->tdls_cancel_channel_switch)
|
||||
return;
|
||||
|
||||
trace_drv_tdls_cancel_channel_switch(local, sdata, sta);
|
||||
local->ops->tdls_cancel_channel_switch(&local->hw, &sdata->vif, sta);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
static inline void
|
||||
drv_tdls_recv_channel_switch(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_tdls_ch_sw_params *params)
|
||||
{
|
||||
trace_drv_tdls_recv_channel_switch(local, sdata, params);
|
||||
if (local->ops->tdls_recv_channel_switch)
|
||||
local->ops->tdls_recv_channel_switch(&local->hw, &sdata->vif,
|
||||
params);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
#endif /* __MAC80211_DRIVER_OPS */
|
||||
|
@ -525,8 +525,13 @@ struct ieee80211_if_managed {
|
||||
struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
|
||||
struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
|
||||
|
||||
/* TDLS support */
|
||||
u8 tdls_peer[ETH_ALEN] __aligned(2);
|
||||
struct delayed_work tdls_peer_del_work;
|
||||
struct sk_buff *orig_teardown_skb; /* The original teardown skb */
|
||||
struct sk_buff *teardown_skb; /* A copy to send through the AP */
|
||||
spinlock_t teardown_lock; /* To lock changing teardown_skb */
|
||||
bool tdls_chan_switch_prohibited;
|
||||
|
||||
/* WMM-AC TSPEC support */
|
||||
struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
|
||||
@ -988,6 +993,7 @@ enum sdata_queue_type {
|
||||
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
|
||||
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
|
||||
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
|
||||
IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -1005,6 +1011,7 @@ enum queue_stop_reason {
|
||||
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH,
|
||||
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
|
||||
IEEE80211_QUEUE_STOP_REASON_RESERVE_TID,
|
||||
|
||||
IEEE80211_QUEUE_STOP_REASONS,
|
||||
};
|
||||
@ -1231,7 +1238,7 @@ struct ieee80211_local {
|
||||
unsigned long scanning;
|
||||
struct cfg80211_ssid scan_ssid;
|
||||
struct cfg80211_scan_request *int_scan_req;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
struct cfg80211_scan_request __rcu *scan_req;
|
||||
struct ieee80211_scan_request *hw_scan_req;
|
||||
struct cfg80211_chan_def scan_chandef;
|
||||
enum ieee80211_band hw_scan_band;
|
||||
@ -1241,7 +1248,8 @@ struct ieee80211_local {
|
||||
|
||||
struct work_struct sched_scan_stopped_work;
|
||||
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
|
||||
struct cfg80211_sched_scan_request *sched_scan_req;
|
||||
struct cfg80211_sched_scan_request __rcu *sched_scan_req;
|
||||
u8 scan_addr[ETH_ALEN];
|
||||
|
||||
unsigned long leave_oper_channel_time;
|
||||
enum mac80211_scan_state next_scan_state;
|
||||
@ -1395,6 +1403,9 @@ struct ieee802_11_elems {
|
||||
size_t total_len;
|
||||
|
||||
/* pointers to IEs */
|
||||
const struct ieee80211_tdls_lnkie *lnk_id;
|
||||
const struct ieee80211_ch_switch_timing *ch_sw_timing;
|
||||
const u8 *ext_capab;
|
||||
const u8 *ssid;
|
||||
const u8 *supp_rates;
|
||||
const u8 *ds_params;
|
||||
@ -1429,6 +1440,7 @@ struct ieee802_11_elems {
|
||||
const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
|
||||
|
||||
/* length of them, respectively */
|
||||
u8 ext_capab_len;
|
||||
u8 ssid_len;
|
||||
u8 supp_rates_len;
|
||||
u8 tim_len;
|
||||
@ -1625,8 +1637,14 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev);
|
||||
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev,
|
||||
u32 info_flags);
|
||||
void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
|
||||
struct sk_buff_head *skbs);
|
||||
struct sk_buff *
|
||||
ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u32 info_flags);
|
||||
|
||||
/* HT */
|
||||
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
|
||||
@ -1753,8 +1771,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
|
||||
gfp_t gfp);
|
||||
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
bool bss_notify);
|
||||
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
|
||||
enum ieee80211_band band);
|
||||
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
|
||||
|
||||
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, int tid,
|
||||
@ -1865,6 +1882,9 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
|
||||
struct sk_buff_head *skbs);
|
||||
void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void __ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
unsigned int queues);
|
||||
|
||||
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
u16 transaction, u16 auth_alg, u16 status,
|
||||
@ -1881,12 +1901,14 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
u8 bands_used, u32 *rate_masks,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *dst, u32 ratemask,
|
||||
const u8 *src, const u8 *dst,
|
||||
u32 ratemask,
|
||||
struct ieee80211_channel *chan,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len,
|
||||
bool directed);
|
||||
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *src, const u8 *dst,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len,
|
||||
u32 ratemask, bool directed, u32 tx_flags,
|
||||
@ -1992,6 +2014,14 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, enum nl80211_tdls_operation oper);
|
||||
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
|
||||
int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *addr, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
const u8 *addr);
|
||||
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
|
||||
extern const struct ethtool_ops ieee80211_ethtool_ops;
|
||||
|
||||
|
@ -1208,6 +1208,8 @@ static void ieee80211_iface_work(struct work_struct *work)
|
||||
WLAN_BACK_RECIPIENT, 0,
|
||||
false);
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
} else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
|
||||
ieee80211_process_tdls_channel_switch(sdata, skb);
|
||||
} else if (ieee80211_is_action(mgmt->frame_control) &&
|
||||
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
|
||||
int len = skb->len;
|
||||
|
@ -764,6 +764,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
||||
local->hw.offchannel_tx_hw_queue >= local->hw.queues))
|
||||
return -EINVAL;
|
||||
|
||||
if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
|
||||
(!local->ops->tdls_channel_switch ||
|
||||
!local->ops->tdls_cancel_channel_switch ||
|
||||
!local->ops->tdls_recv_channel_switch))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
|
||||
return -EINVAL;
|
||||
|
@ -1049,6 +1049,8 @@ static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
|
||||
sdata->csa_block_tx = false;
|
||||
}
|
||||
|
||||
cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
|
||||
|
||||
sdata->vif.csa_active = false;
|
||||
ifmgd->csa_waiting_bcn = false;
|
||||
|
||||
@ -1205,6 +1207,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
|
||||
csa_ie.count);
|
||||
|
||||
if (local->ops->channel_switch) {
|
||||
/* use driver's channel switch callback */
|
||||
drv_channel_switch(local, sdata, &ch_switch);
|
||||
@ -2221,7 +2226,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
|
||||
else
|
||||
ssid_len = ssid[1];
|
||||
|
||||
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
|
||||
ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
|
||||
ssid + 2, ssid_len, NULL,
|
||||
0, (u32) -1, true, 0,
|
||||
ifmgd->associated->channel, false);
|
||||
rcu_read_unlock();
|
||||
@ -2324,7 +2330,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
|
||||
else
|
||||
ssid_len = ssid[1];
|
||||
|
||||
skb = ieee80211_build_probe_req(sdata, cbss->bssid,
|
||||
skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
|
||||
(u32) -1, cbss->channel,
|
||||
ssid + 2, ssid_len,
|
||||
NULL, 0, true);
|
||||
@ -2798,6 +2804,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
ifmgd->aid = aid;
|
||||
ifmgd->tdls_chan_switch_prohibited =
|
||||
elems.ext_capab && elems.ext_capab_len >= 5 &&
|
||||
(elems.ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
|
||||
|
||||
/*
|
||||
* Some APs are erroneously not including some information in their
|
||||
@ -3642,7 +3651,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
||||
* Direct probe is sent to broadcast address as some APs
|
||||
* will not answer to direct packet in unassociated state.
|
||||
*/
|
||||
ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
|
||||
ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
|
||||
ssidie + 2, ssidie[1],
|
||||
NULL, 0, (u32) -1, true, 0,
|
||||
auth_data->bss->channel, false);
|
||||
rcu_read_unlock();
|
||||
@ -3999,6 +4009,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
|
||||
else
|
||||
ifmgd->req_smps = IEEE80211_SMPS_OFF;
|
||||
|
||||
/* Setup TDLS data */
|
||||
spin_lock_init(&ifmgd->teardown_lock);
|
||||
ifmgd->teardown_skb = NULL;
|
||||
ifmgd->orig_teardown_skb = NULL;
|
||||
}
|
||||
|
||||
/* scan finished notification */
|
||||
@ -4861,6 +4876,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
|
||||
}
|
||||
if (ifmgd->auth_data)
|
||||
ieee80211_destroy_auth_data(sdata, false);
|
||||
spin_lock_bh(&ifmgd->teardown_lock);
|
||||
if (ifmgd->teardown_skb) {
|
||||
kfree_skb(ifmgd->teardown_skb);
|
||||
ifmgd->teardown_skb = NULL;
|
||||
ifmgd->orig_teardown_skb = NULL;
|
||||
}
|
||||
spin_unlock_bh(&ifmgd->teardown_lock);
|
||||
del_timer_sync(&ifmgd->timer);
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
|
@ -385,7 +385,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
|
||||
*rate = alt_rate;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
} else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) {
|
||||
/* handle legacy rates */
|
||||
if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))
|
||||
return;
|
||||
@ -696,6 +696,7 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
|
||||
struct ieee80211_sta *pubsta,
|
||||
struct ieee80211_sta_rates *rates)
|
||||
{
|
||||
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
||||
struct ieee80211_sta_rates *old;
|
||||
|
||||
/*
|
||||
@ -709,6 +710,8 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
|
||||
if (old)
|
||||
kfree_rcu(old, rcu_head);
|
||||
|
||||
drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rate_control_set_rates);
|
||||
|
@ -690,6 +690,9 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
|
||||
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
||||
u16 tid;
|
||||
|
||||
if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
|
||||
return;
|
||||
|
||||
if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
|
||||
return;
|
||||
|
||||
@ -700,9 +703,6 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
|
||||
if (likely(sta->ampdu_mlme.tid_tx[tid]))
|
||||
return;
|
||||
|
||||
if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
|
||||
return;
|
||||
|
||||
ieee80211_start_tx_ba_session(pubsta, tid, 5000);
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,8 @@
|
||||
* only useful for monitoring.
|
||||
*/
|
||||
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
|
||||
struct sk_buff *skb)
|
||||
struct sk_buff *skb,
|
||||
unsigned int rtap_vendor_space)
|
||||
{
|
||||
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
|
||||
if (likely(skb->len > FCS_LEN))
|
||||
@ -52,20 +53,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
|
||||
}
|
||||
}
|
||||
|
||||
__pskb_pull(skb, rtap_vendor_space);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
|
||||
static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
|
||||
unsigned int rtap_vendor_space)
|
||||
{
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
struct ieee80211_hdr *hdr;
|
||||
|
||||
hdr = (void *)(skb->data + rtap_vendor_space);
|
||||
|
||||
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
|
||||
RX_FLAG_FAILED_PLCP_CRC |
|
||||
RX_FLAG_AMPDU_IS_ZEROLEN))
|
||||
return true;
|
||||
|
||||
if (unlikely(skb->len < 16 + present_fcs_len))
|
||||
if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
|
||||
return true;
|
||||
|
||||
if (ieee80211_is_ctl(hdr->frame_control) &&
|
||||
@ -77,8 +83,9 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_rx_radiotap_space(struct ieee80211_local *local,
|
||||
struct ieee80211_rx_status *status)
|
||||
ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
|
||||
struct ieee80211_rx_status *status,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
int len;
|
||||
|
||||
@ -121,6 +128,21 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
|
||||
len += 2 * hweight8(status->chains);
|
||||
}
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
|
||||
struct ieee80211_vendor_radiotap *rtap = (void *)skb->data;
|
||||
|
||||
/* vendor presence bitmap */
|
||||
len += 4;
|
||||
/* alignment for fixed 6-byte vendor data header */
|
||||
len = ALIGN(len, 2);
|
||||
/* vendor data header */
|
||||
len += 6;
|
||||
if (WARN_ON(rtap->align == 0))
|
||||
rtap->align = 1;
|
||||
len = ALIGN(len, rtap->align);
|
||||
len += rtap->len + rtap->pad;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -144,13 +166,20 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
||||
u16 channel_flags = 0;
|
||||
int mpdulen, chain;
|
||||
unsigned long chains = status->chains;
|
||||
struct ieee80211_vendor_radiotap rtap = {};
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
|
||||
rtap = *(struct ieee80211_vendor_radiotap *)skb->data;
|
||||
/* rtap.len and rtap.pad are undone immediately */
|
||||
skb_pull(skb, sizeof(rtap) + rtap.len + rtap.pad);
|
||||
}
|
||||
|
||||
mpdulen = skb->len;
|
||||
if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
|
||||
mpdulen += FCS_LEN;
|
||||
|
||||
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
|
||||
memset(rthdr, 0, rtap_len);
|
||||
memset(rthdr, 0, rtap_len - rtap.len - rtap.pad);
|
||||
it_present = &rthdr->it_present;
|
||||
|
||||
/* radiotap header, set always present flags */
|
||||
@ -172,6 +201,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
||||
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
|
||||
}
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
|
||||
it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
|
||||
BIT(IEEE80211_RADIOTAP_EXT);
|
||||
put_unaligned_le32(it_present_val, it_present);
|
||||
it_present++;
|
||||
it_present_val = rtap.present;
|
||||
}
|
||||
|
||||
put_unaligned_le32(it_present_val, it_present);
|
||||
|
||||
pos = (void *)(it_present + 1);
|
||||
@ -366,6 +403,22 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
|
||||
*pos++ = status->chain_signal[chain];
|
||||
*pos++ = chain;
|
||||
}
|
||||
|
||||
if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
|
||||
/* ensure 2 byte alignment for the vendor field as required */
|
||||
if ((pos - (u8 *)rthdr) & 1)
|
||||
*pos++ = 0;
|
||||
*pos++ = rtap.oui[0];
|
||||
*pos++ = rtap.oui[1];
|
||||
*pos++ = rtap.oui[2];
|
||||
*pos++ = rtap.subns;
|
||||
put_unaligned_le16(rtap.len, pos);
|
||||
pos += 2;
|
||||
/* align the actual payload as requested */
|
||||
while ((pos - (u8 *)rthdr) & (rtap.align - 1))
|
||||
*pos++ = 0;
|
||||
/* data (and possible padding) already follows */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -379,10 +432,17 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
{
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
int needed_headroom;
|
||||
int rt_hdrlen, needed_headroom;
|
||||
struct sk_buff *skb, *skb2;
|
||||
struct net_device *prev_dev = NULL;
|
||||
int present_fcs_len = 0;
|
||||
unsigned int rtap_vendor_space = 0;
|
||||
|
||||
if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
|
||||
struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
|
||||
|
||||
rtap_vendor_space = sizeof(*rtap) + rtap->len + rtap->pad;
|
||||
}
|
||||
|
||||
/*
|
||||
* First, we may need to make a copy of the skb because
|
||||
@ -396,25 +456,27 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
|
||||
present_fcs_len = FCS_LEN;
|
||||
|
||||
/* ensure hdr->frame_control is in skb head */
|
||||
if (!pskb_may_pull(origskb, 2)) {
|
||||
/* ensure hdr->frame_control and vendor radiotap data are in skb head */
|
||||
if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) {
|
||||
dev_kfree_skb(origskb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!local->monitors) {
|
||||
if (should_drop_frame(origskb, present_fcs_len)) {
|
||||
if (should_drop_frame(origskb, present_fcs_len,
|
||||
rtap_vendor_space)) {
|
||||
dev_kfree_skb(origskb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return remove_monitor_info(local, origskb);
|
||||
return remove_monitor_info(local, origskb, rtap_vendor_space);
|
||||
}
|
||||
|
||||
/* room for the radiotap header based on driver features */
|
||||
needed_headroom = ieee80211_rx_radiotap_space(local, status);
|
||||
rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb);
|
||||
needed_headroom = rt_hdrlen - rtap_vendor_space;
|
||||
|
||||
if (should_drop_frame(origskb, present_fcs_len)) {
|
||||
if (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) {
|
||||
/* only need to expand headroom if necessary */
|
||||
skb = origskb;
|
||||
origskb = NULL;
|
||||
@ -438,15 +500,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
||||
*/
|
||||
skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
|
||||
|
||||
origskb = remove_monitor_info(local, origskb);
|
||||
origskb = remove_monitor_info(local, origskb,
|
||||
rtap_vendor_space);
|
||||
|
||||
if (!skb)
|
||||
return origskb;
|
||||
}
|
||||
|
||||
/* prepend radiotap information */
|
||||
ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom,
|
||||
true);
|
||||
ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true);
|
||||
|
||||
skb_reset_mac_header(skb);
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
@ -985,7 +1047,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
|
||||
}
|
||||
|
||||
static ieee80211_rx_result debug_noinline
|
||||
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
|
||||
@ -994,10 +1056,16 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
* Drop duplicate 802.11 retransmissions
|
||||
* (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
|
||||
*/
|
||||
if (rx->skb->len >= 24 && rx->sta &&
|
||||
!ieee80211_is_ctl(hdr->frame_control) &&
|
||||
!ieee80211_is_qos_nullfunc(hdr->frame_control) &&
|
||||
!is_multicast_ether_addr(hdr->addr1)) {
|
||||
|
||||
if (rx->skb->len < 24)
|
||||
return RX_CONTINUE;
|
||||
|
||||
if (ieee80211_is_ctl(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control) ||
|
||||
is_multicast_ether_addr(hdr->addr1))
|
||||
return RX_CONTINUE;
|
||||
|
||||
if (rx->sta) {
|
||||
if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
|
||||
rx->sta->last_seq_ctrl[rx->seqno_idx] ==
|
||||
hdr->seq_ctrl)) {
|
||||
@ -1011,6 +1079,14 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
}
|
||||
}
|
||||
|
||||
return RX_CONTINUE;
|
||||
}
|
||||
|
||||
static ieee80211_rx_result debug_noinline
|
||||
ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
|
||||
|
||||
if (unlikely(rx->skb->len < 16)) {
|
||||
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
|
||||
return RX_DROP_MONITOR;
|
||||
@ -2257,6 +2333,27 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
|
||||
if (!ieee80211_frame_allowed(rx, fc))
|
||||
return RX_DROP_MONITOR;
|
||||
|
||||
/* directly handle TDLS channel switch requests/responses */
|
||||
if (unlikely(((struct ethhdr *)rx->skb->data)->h_proto ==
|
||||
cpu_to_be16(ETH_P_TDLS))) {
|
||||
struct ieee80211_tdls_data *tf = (void *)rx->skb->data;
|
||||
|
||||
if (pskb_may_pull(rx->skb,
|
||||
offsetof(struct ieee80211_tdls_data, u)) &&
|
||||
tf->payload_type == WLAN_TDLS_SNAP_RFTYPE &&
|
||||
tf->category == WLAN_CATEGORY_TDLS &&
|
||||
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
|
||||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
|
||||
rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW;
|
||||
skb_queue_tail(&sdata->skb_queue, rx->skb);
|
||||
ieee80211_queue_work(&rx->local->hw, &sdata->work);
|
||||
if (rx->sta)
|
||||
rx->sta->rx_packets++;
|
||||
|
||||
return RX_QUEUED;
|
||||
}
|
||||
}
|
||||
|
||||
if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
|
||||
unlikely(port_control) && sdata->bss) {
|
||||
sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
|
||||
@ -2892,8 +2989,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
|
||||
if (!local->cooked_mntrs)
|
||||
goto out_free_skb;
|
||||
|
||||
/* vendor data is long removed here */
|
||||
status->flag &= ~RX_FLAG_RADIOTAP_VENDOR_DATA;
|
||||
/* room for the radiotap header based on driver features */
|
||||
needed_headroom = ieee80211_rx_radiotap_space(local, status);
|
||||
needed_headroom = ieee80211_rx_radiotap_hdrlen(local, status, skb);
|
||||
|
||||
if (skb_headroom(skb) < needed_headroom &&
|
||||
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC))
|
||||
@ -3046,6 +3145,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
|
||||
goto rxh_next; \
|
||||
} while (0);
|
||||
|
||||
CALL_RXH(ieee80211_rx_h_check_dup)
|
||||
CALL_RXH(ieee80211_rx_h_check)
|
||||
|
||||
ieee80211_rx_reorder_ampdu(rx, &reorder_release);
|
||||
|
@ -184,9 +184,21 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
|
||||
return;
|
||||
|
||||
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
|
||||
/* ignore ProbeResp to foreign address */
|
||||
if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) &&
|
||||
(!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr)))
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
struct cfg80211_sched_scan_request *sched_scan_req;
|
||||
|
||||
scan_req = rcu_dereference(local->scan_req);
|
||||
sched_scan_req = rcu_dereference(local->sched_scan_req);
|
||||
|
||||
/* ignore ProbeResp to foreign address unless scanning
|
||||
* with randomised address
|
||||
*/
|
||||
if (!(sdata1 &&
|
||||
(ether_addr_equal(mgmt->da, sdata1->vif.addr) ||
|
||||
scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) &&
|
||||
!(sdata2 &&
|
||||
(ether_addr_equal(mgmt->da, sdata2->vif.addr) ||
|
||||
sched_scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)))
|
||||
return;
|
||||
|
||||
elements = mgmt->u.probe_resp.variable;
|
||||
@ -234,11 +246,14 @@ ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
|
||||
/* return false if no more work */
|
||||
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
|
||||
{
|
||||
struct cfg80211_scan_request *req = local->scan_req;
|
||||
struct cfg80211_scan_request *req;
|
||||
struct cfg80211_chan_def chandef;
|
||||
u8 bands_used = 0;
|
||||
int i, ielen, n_chans;
|
||||
|
||||
req = rcu_dereference_protected(local->scan_req,
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
|
||||
return false;
|
||||
|
||||
@ -281,6 +296,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
|
||||
bands_used, req->rates, &chandef);
|
||||
local->hw_scan_req->req.ie_len = ielen;
|
||||
local->hw_scan_req->req.no_cck = req->no_cck;
|
||||
ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr);
|
||||
ether_addr_copy(local->hw_scan_req->req.mac_addr_mask,
|
||||
req->mac_addr_mask);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -290,6 +308,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
bool hw_scan = local->ops->hw_scan;
|
||||
bool was_scanning = local->scanning;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
struct ieee80211_sub_if_data *scan_sdata;
|
||||
|
||||
lockdep_assert_held(&local->mtx);
|
||||
|
||||
@ -322,9 +342,15 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
||||
kfree(local->hw_scan_req);
|
||||
local->hw_scan_req = NULL;
|
||||
|
||||
if (local->scan_req != local->int_scan_req)
|
||||
cfg80211_scan_done(local->scan_req, aborted);
|
||||
local->scan_req = NULL;
|
||||
scan_req = rcu_dereference_protected(local->scan_req,
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
if (scan_req != local->int_scan_req)
|
||||
cfg80211_scan_done(scan_req, aborted);
|
||||
RCU_INIT_POINTER(local->scan_req, NULL);
|
||||
|
||||
scan_sdata = rcu_dereference_protected(local->scan_sdata,
|
||||
lockdep_is_held(&local->mtx));
|
||||
RCU_INIT_POINTER(local->scan_sdata, NULL);
|
||||
|
||||
local->scanning = 0;
|
||||
@ -335,7 +361,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
||||
|
||||
if (!hw_scan) {
|
||||
ieee80211_configure_filter(local);
|
||||
drv_sw_scan_complete(local);
|
||||
drv_sw_scan_complete(local, scan_sdata);
|
||||
ieee80211_offchannel_return(local);
|
||||
}
|
||||
|
||||
@ -361,7 +387,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_scan_completed);
|
||||
|
||||
static int ieee80211_start_sw_scan(struct ieee80211_local *local)
|
||||
static int ieee80211_start_sw_scan(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
/* Software scan is not supported in multi-channel cases */
|
||||
if (local->use_chanctx)
|
||||
@ -380,7 +407,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
|
||||
* nullfunc frames and probe requests will be dropped in
|
||||
* ieee80211_tx_h_check_assoc().
|
||||
*/
|
||||
drv_sw_scan_start(local);
|
||||
drv_sw_scan_start(local, sdata, local->scan_addr);
|
||||
|
||||
local->leave_oper_channel_time = jiffies;
|
||||
local->next_scan_state = SCAN_DECISION;
|
||||
@ -440,23 +467,26 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
|
||||
{
|
||||
int i;
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
enum ieee80211_band band = local->hw.conf.chandef.chan->band;
|
||||
u32 tx_flags;
|
||||
|
||||
scan_req = rcu_dereference_protected(local->scan_req,
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
|
||||
if (local->scan_req->no_cck)
|
||||
if (scan_req->no_cck)
|
||||
tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
|
||||
|
||||
sdata = rcu_dereference_protected(local->scan_sdata,
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
for (i = 0; i < local->scan_req->n_ssids; i++)
|
||||
for (i = 0; i < scan_req->n_ssids; i++)
|
||||
ieee80211_send_probe_req(
|
||||
sdata, NULL,
|
||||
local->scan_req->ssids[i].ssid,
|
||||
local->scan_req->ssids[i].ssid_len,
|
||||
local->scan_req->ie, local->scan_req->ie_len,
|
||||
local->scan_req->rates[band], false,
|
||||
sdata, local->scan_addr, NULL,
|
||||
scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
|
||||
scan_req->ie, scan_req->ie_len,
|
||||
scan_req->rates[band], false,
|
||||
tx_flags, local->hw.conf.chandef.chan, true);
|
||||
|
||||
/*
|
||||
@ -480,7 +510,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
if (!ieee80211_can_scan(local, sdata)) {
|
||||
/* wait for the work to finish/time out */
|
||||
local->scan_req = req;
|
||||
rcu_assign_pointer(local->scan_req, req);
|
||||
rcu_assign_pointer(local->scan_sdata, sdata);
|
||||
return 0;
|
||||
}
|
||||
@ -530,9 +560,16 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
*/
|
||||
}
|
||||
|
||||
local->scan_req = req;
|
||||
rcu_assign_pointer(local->scan_req, req);
|
||||
rcu_assign_pointer(local->scan_sdata, sdata);
|
||||
|
||||
if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
|
||||
get_random_mask_addr(local->scan_addr,
|
||||
req->mac_addr,
|
||||
req->mac_addr_mask);
|
||||
else
|
||||
memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN);
|
||||
|
||||
if (local->ops->hw_scan) {
|
||||
__set_bit(SCAN_HW_SCANNING, &local->scanning);
|
||||
} else if ((req->n_channels == 1) &&
|
||||
@ -549,7 +586,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
/* Notify driver scan is starting, keep order of operations
|
||||
* same as normal software scan, in case that matters. */
|
||||
drv_sw_scan_start(local);
|
||||
drv_sw_scan_start(local, sdata, local->scan_addr);
|
||||
|
||||
ieee80211_configure_filter(local); /* accept probe-responses */
|
||||
|
||||
@ -558,7 +595,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
if ((req->channels[0]->flags &
|
||||
IEEE80211_CHAN_NO_IR) ||
|
||||
!local->scan_req->n_ssids) {
|
||||
!req->n_ssids) {
|
||||
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
|
||||
} else {
|
||||
ieee80211_scan_state_send_probe(local, &next_delay);
|
||||
@ -579,8 +616,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
|
||||
if (local->ops->hw_scan) {
|
||||
WARN_ON(!ieee80211_prep_hw_scan(local));
|
||||
rc = drv_hw_scan(local, sdata, local->hw_scan_req);
|
||||
} else
|
||||
rc = ieee80211_start_sw_scan(local);
|
||||
} else {
|
||||
rc = ieee80211_start_sw_scan(local, sdata);
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
kfree(local->hw_scan_req);
|
||||
@ -617,6 +655,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_channel *next_chan;
|
||||
enum mac80211_scan_state next_scan_state;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
|
||||
/*
|
||||
* check if at least one STA interface is associated,
|
||||
@ -641,7 +680,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
|
||||
}
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
|
||||
next_chan = local->scan_req->channels[local->scan_channel_idx];
|
||||
scan_req = rcu_dereference_protected(local->scan_req,
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
next_chan = scan_req->channels[local->scan_channel_idx];
|
||||
|
||||
/*
|
||||
* we're currently scanning a different channel, let's
|
||||
@ -656,7 +698,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
|
||||
local->leave_oper_channel_time + HZ / 8);
|
||||
|
||||
if (associated && !tx_empty) {
|
||||
if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
|
||||
if (scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
|
||||
next_scan_state = SCAN_ABORT;
|
||||
else
|
||||
next_scan_state = SCAN_SUSPEND;
|
||||
@ -677,14 +719,18 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
|
||||
int skip;
|
||||
struct ieee80211_channel *chan;
|
||||
enum nl80211_bss_scan_width oper_scan_width;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
|
||||
scan_req = rcu_dereference_protected(local->scan_req,
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
skip = 0;
|
||||
chan = local->scan_req->channels[local->scan_channel_idx];
|
||||
chan = scan_req->channels[local->scan_channel_idx];
|
||||
|
||||
local->scan_chandef.chan = chan;
|
||||
local->scan_chandef.center_freq1 = chan->center_freq;
|
||||
local->scan_chandef.center_freq2 = 0;
|
||||
switch (local->scan_req->scan_width) {
|
||||
switch (scan_req->scan_width) {
|
||||
case NL80211_BSS_CHAN_WIDTH_5:
|
||||
local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
|
||||
break;
|
||||
@ -698,7 +744,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
|
||||
oper_scan_width = cfg80211_chandef_to_scan_width(
|
||||
&local->_oper_chandef);
|
||||
if (chan == local->_oper_chandef.chan &&
|
||||
oper_scan_width == local->scan_req->scan_width)
|
||||
oper_scan_width == scan_req->scan_width)
|
||||
local->scan_chandef = local->_oper_chandef;
|
||||
else
|
||||
local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
@ -727,8 +773,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
|
||||
*
|
||||
* In any case, it is not necessary for a passive scan.
|
||||
*/
|
||||
if (chan->flags & IEEE80211_CHAN_NO_IR ||
|
||||
!local->scan_req->n_ssids) {
|
||||
if (chan->flags & IEEE80211_CHAN_NO_IR || !scan_req->n_ssids) {
|
||||
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
|
||||
local->next_scan_state = SCAN_DECISION;
|
||||
return;
|
||||
@ -777,6 +822,7 @@ void ieee80211_scan_work(struct work_struct *work)
|
||||
struct ieee80211_local *local =
|
||||
container_of(work, struct ieee80211_local, scan_work.work);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct cfg80211_scan_request *scan_req;
|
||||
unsigned long next_delay = 0;
|
||||
bool aborted;
|
||||
|
||||
@ -784,6 +830,8 @@ void ieee80211_scan_work(struct work_struct *work)
|
||||
|
||||
sdata = rcu_dereference_protected(local->scan_sdata,
|
||||
lockdep_is_held(&local->mtx));
|
||||
scan_req = rcu_dereference_protected(local->scan_req,
|
||||
lockdep_is_held(&local->mtx));
|
||||
|
||||
/* When scanning on-channel, the first-callback means completed. */
|
||||
if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
|
||||
@ -796,20 +844,19 @@ void ieee80211_scan_work(struct work_struct *work)
|
||||
goto out_complete;
|
||||
}
|
||||
|
||||
if (!sdata || !local->scan_req)
|
||||
if (!sdata || !scan_req)
|
||||
goto out;
|
||||
|
||||
if (local->scan_req && !local->scanning) {
|
||||
struct cfg80211_scan_request *req = local->scan_req;
|
||||
if (!local->scanning) {
|
||||
int rc;
|
||||
|
||||
local->scan_req = NULL;
|
||||
RCU_INIT_POINTER(local->scan_req, NULL);
|
||||
RCU_INIT_POINTER(local->scan_sdata, NULL);
|
||||
|
||||
rc = __ieee80211_start_scan(sdata, req);
|
||||
rc = __ieee80211_start_scan(sdata, scan_req);
|
||||
if (rc) {
|
||||
/* need to complete scan in cfg80211 */
|
||||
local->scan_req = req;
|
||||
rcu_assign_pointer(local->scan_req, scan_req);
|
||||
aborted = true;
|
||||
goto out_complete;
|
||||
} else
|
||||
@ -829,7 +876,7 @@ void ieee80211_scan_work(struct work_struct *work)
|
||||
switch (local->next_scan_state) {
|
||||
case SCAN_DECISION:
|
||||
/* if no more bands/channels left, complete scan */
|
||||
if (local->scan_channel_idx >= local->scan_req->n_channels) {
|
||||
if (local->scan_channel_idx >= scan_req->n_channels) {
|
||||
aborted = false;
|
||||
goto out_complete;
|
||||
}
|
||||
@ -1043,7 +1090,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
|
||||
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
|
||||
if (ret == 0) {
|
||||
rcu_assign_pointer(local->sched_scan_sdata, sdata);
|
||||
local->sched_scan_req = req;
|
||||
rcu_assign_pointer(local->sched_scan_req, req);
|
||||
}
|
||||
|
||||
kfree(ie);
|
||||
@ -1052,7 +1099,7 @@ out:
|
||||
if (ret) {
|
||||
/* Clean in case of failure after HW restart or upon resume. */
|
||||
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
|
||||
local->sched_scan_req = NULL;
|
||||
RCU_INIT_POINTER(local->sched_scan_req, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1090,7 +1137,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
|
||||
}
|
||||
|
||||
/* We don't want to restart sched scan anymore. */
|
||||
local->sched_scan_req = NULL;
|
||||
RCU_INIT_POINTER(local->sched_scan_req, NULL);
|
||||
|
||||
if (rcu_access_pointer(local->sched_scan_sdata)) {
|
||||
ret = drv_sched_scan_stop(local, sdata);
|
||||
@ -1125,7 +1172,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local)
|
||||
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
|
||||
|
||||
/* If sched scan was aborted by the driver. */
|
||||
local->sched_scan_req = NULL;
|
||||
RCU_INIT_POINTER(local->sched_scan_req, NULL);
|
||||
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
|
@ -351,6 +351,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
sta->sta_state = IEEE80211_STA_NONE;
|
||||
|
||||
/* Mark TID as unreserved */
|
||||
sta->reserved_tid = IEEE80211_TID_UNRESERVED;
|
||||
|
||||
ktime_get_ts(&uptime);
|
||||
sta->last_connected = uptime.tv_sec;
|
||||
ewma_init(&sta->avg_signal, 1024, 8);
|
||||
@ -847,6 +850,15 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
|
||||
if (WARN_ON(ret))
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* for TDLS peers, make sure to return to the base channel before
|
||||
* removal.
|
||||
*/
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
|
||||
drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
|
||||
clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
|
||||
}
|
||||
|
||||
list_del_rcu(&sta->list);
|
||||
|
||||
drv_sta_pre_rcu_remove(local, sta->sdata, sta);
|
||||
@ -1249,7 +1261,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
|
||||
return;
|
||||
}
|
||||
|
||||
ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
|
||||
info->band = chanctx_conf->def.chan->band;
|
||||
ieee80211_xmit(sdata, skb);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,9 @@
|
||||
* packets. This means the link is enabled.
|
||||
* @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
|
||||
* station.
|
||||
* @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
|
||||
* @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
|
||||
* TDLS peer
|
||||
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
|
||||
* keeping station in power-save mode, reply when the driver
|
||||
* unblocks the station.
|
||||
@ -78,6 +81,8 @@ enum ieee80211_sta_info_flags {
|
||||
WLAN_STA_TDLS_PEER,
|
||||
WLAN_STA_TDLS_PEER_AUTH,
|
||||
WLAN_STA_TDLS_INITIATOR,
|
||||
WLAN_STA_TDLS_CHAN_SWITCH,
|
||||
WLAN_STA_TDLS_OFF_CHANNEL,
|
||||
WLAN_STA_UAPSD,
|
||||
WLAN_STA_SP,
|
||||
WLAN_STA_4ADDR_EVENT,
|
||||
@ -249,6 +254,9 @@ struct ieee80211_tx_latency_stat {
|
||||
u32 bin_count;
|
||||
};
|
||||
|
||||
/* Value to indicate no TID reservation */
|
||||
#define IEEE80211_TID_UNRESERVED 0xff
|
||||
|
||||
/**
|
||||
* struct sta_info - STA information
|
||||
*
|
||||
@ -337,6 +345,7 @@ struct ieee80211_tx_latency_stat {
|
||||
* AP only.
|
||||
* @cipher_scheme: optional cipher scheme for this station
|
||||
* @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
|
||||
* @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
|
||||
*/
|
||||
struct sta_info {
|
||||
/* General information, mostly static */
|
||||
@ -454,6 +463,8 @@ struct sta_info {
|
||||
/* TDLS timeout data */
|
||||
unsigned long last_tdls_pkt_time;
|
||||
|
||||
u8 reserved_tid;
|
||||
|
||||
/* keep last! */
|
||||
struct ieee80211_sta sta;
|
||||
};
|
||||
|
@ -390,6 +390,46 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handles the tx for TDLS teardown frames.
|
||||
* If the frame wasn't ACKed by the peer - it will be re-sent through the AP
|
||||
*/
|
||||
static void ieee80211_tdls_td_tx_handle(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u32 flags)
|
||||
{
|
||||
struct sk_buff *teardown_skb;
|
||||
struct sk_buff *orig_teardown_skb;
|
||||
bool is_teardown = false;
|
||||
|
||||
/* Get the teardown data we need and free the lock */
|
||||
spin_lock(&sdata->u.mgd.teardown_lock);
|
||||
teardown_skb = sdata->u.mgd.teardown_skb;
|
||||
orig_teardown_skb = sdata->u.mgd.orig_teardown_skb;
|
||||
if ((skb == orig_teardown_skb) && teardown_skb) {
|
||||
sdata->u.mgd.teardown_skb = NULL;
|
||||
sdata->u.mgd.orig_teardown_skb = NULL;
|
||||
is_teardown = true;
|
||||
}
|
||||
spin_unlock(&sdata->u.mgd.teardown_lock);
|
||||
|
||||
if (is_teardown) {
|
||||
/* This mechanism relies on being able to get ACKs */
|
||||
WARN_ON(!(local->hw.flags &
|
||||
IEEE80211_HW_REPORTS_TX_ACK_STATUS));
|
||||
|
||||
/* Check if peer has ACKed */
|
||||
if (flags & IEEE80211_TX_STAT_ACK) {
|
||||
dev_kfree_skb_any(teardown_skb);
|
||||
} else {
|
||||
tdls_dbg(sdata,
|
||||
"TDLS Resending teardown through AP\n");
|
||||
|
||||
ieee80211_subif_start_xmit(teardown_skb, skb->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||
struct sk_buff *skb, bool dropped)
|
||||
{
|
||||
@ -426,8 +466,19 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
|
||||
if (!sdata) {
|
||||
skb->dev = NULL;
|
||||
} else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
|
||||
ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control,
|
||||
acked);
|
||||
unsigned int hdr_size =
|
||||
ieee80211_hdrlen(hdr->frame_control);
|
||||
|
||||
/* Check to see if packet is a TDLS teardown packet */
|
||||
if (ieee80211_is_data(hdr->frame_control) &&
|
||||
(ieee80211_get_tdls_action(skb, hdr_size) ==
|
||||
WLAN_TDLS_TEARDOWN))
|
||||
ieee80211_tdls_td_tx_handle(local, sdata, skb,
|
||||
info->flags);
|
||||
else
|
||||
ieee80211_mgd_conn_tx_status(sdata,
|
||||
hdr->frame_control,
|
||||
acked);
|
||||
} else if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
|
||||
cfg80211_probe_status(sdata->dev, hdr->addr1,
|
||||
|
@ -35,19 +35,101 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
|
||||
mutex_unlock(&local->mtx);
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
|
||||
static void ieee80211_tdls_add_ext_capab(struct ieee80211_local *local,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
u8 *pos = (void *)skb_put(skb, 7);
|
||||
bool chan_switch = local->hw.wiphy->features &
|
||||
NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
|
||||
|
||||
*pos++ = WLAN_EID_EXT_CAPABILITY;
|
||||
*pos++ = 5; /* len */
|
||||
*pos++ = 0x0;
|
||||
*pos++ = 0x0;
|
||||
*pos++ = 0x0;
|
||||
*pos++ = 0x0;
|
||||
*pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
|
||||
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
|
||||
}
|
||||
|
||||
static u8
|
||||
ieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u16 start, u16 end,
|
||||
u16 spacing)
|
||||
{
|
||||
u8 subband_cnt = 0, ch_cnt = 0;
|
||||
struct ieee80211_channel *ch;
|
||||
struct cfg80211_chan_def chandef;
|
||||
int i, subband_start;
|
||||
|
||||
for (i = start; i <= end; i += spacing) {
|
||||
if (!ch_cnt)
|
||||
subband_start = i;
|
||||
|
||||
ch = ieee80211_get_channel(sdata->local->hw.wiphy, i);
|
||||
if (ch) {
|
||||
/* we will be active on the channel */
|
||||
u32 flags = IEEE80211_CHAN_DISABLED |
|
||||
IEEE80211_CHAN_NO_IR;
|
||||
cfg80211_chandef_create(&chandef, ch,
|
||||
NL80211_CHAN_HT20);
|
||||
if (cfg80211_chandef_usable(sdata->local->hw.wiphy,
|
||||
&chandef, flags)) {
|
||||
ch_cnt++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch_cnt) {
|
||||
u8 *pos = skb_put(skb, 2);
|
||||
*pos++ = ieee80211_frequency_to_channel(subband_start);
|
||||
*pos++ = ch_cnt;
|
||||
|
||||
subband_cnt++;
|
||||
ch_cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return subband_cnt;
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
/*
|
||||
* Add possible channels for TDLS. These are channels that are allowed
|
||||
* to be active.
|
||||
*/
|
||||
u8 subband_cnt;
|
||||
u8 *pos = skb_put(skb, 2);
|
||||
|
||||
*pos++ = WLAN_EID_SUPPORTED_CHANNELS;
|
||||
|
||||
/*
|
||||
* 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
|
||||
* this doesn't happen in real world scenarios.
|
||||
*/
|
||||
|
||||
/* 2GHz, with 5MHz spacing */
|
||||
subband_cnt = ieee80211_tdls_add_subband(sdata, skb, 2412, 2472, 5);
|
||||
|
||||
/* 5GHz, with 20MHz spacing */
|
||||
subband_cnt += ieee80211_tdls_add_subband(sdata, skb, 5000, 5825, 20);
|
||||
|
||||
/* length */
|
||||
*pos = 2 * subband_cnt;
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
|
||||
{
|
||||
u8 *pos = (void *)skb_put(skb, 3);
|
||||
|
||||
*pos++ = WLAN_EID_BSS_COEX_2040;
|
||||
*pos++ = 1; /* len */
|
||||
|
||||
*pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
|
||||
}
|
||||
|
||||
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
|
||||
u16 status_code)
|
||||
{
|
||||
@ -190,6 +272,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
ieee80211_add_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
|
||||
ieee80211_tdls_add_supp_channels(sdata, skb);
|
||||
|
||||
/* add any custom IEs that go before Extended Capabilities */
|
||||
if (extra_ies_len) {
|
||||
@ -209,7 +292,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_ext_capab(skb);
|
||||
ieee80211_tdls_add_ext_capab(local, skb);
|
||||
|
||||
/* add the QoS element if we support it */
|
||||
if (local->hw.queues >= IEEE80211_NUM_ACS &&
|
||||
@ -271,6 +354,10 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (ht_cap.ht_supported &&
|
||||
(ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
|
||||
ieee80211_tdls_add_bss_coex_ie(skb);
|
||||
|
||||
/* add any remaining IEs */
|
||||
if (extra_ies_len) {
|
||||
noffset = extra_ies_len;
|
||||
@ -362,11 +449,68 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_tdls_data *tf;
|
||||
size_t offset = 0, noffset;
|
||||
u8 *pos;
|
||||
|
||||
if (WARN_ON_ONCE(!chandef))
|
||||
return;
|
||||
|
||||
tf = (void *)skb->data;
|
||||
tf->u.chan_switch_req.target_channel =
|
||||
ieee80211_frequency_to_channel(chandef->chan->center_freq);
|
||||
tf->u.chan_switch_req.oper_class = oper_class;
|
||||
|
||||
if (extra_ies_len) {
|
||||
static const u8 before_lnkie[] = {
|
||||
WLAN_EID_SECONDARY_CHANNEL_OFFSET,
|
||||
};
|
||||
noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
|
||||
before_lnkie,
|
||||
ARRAY_SIZE(before_lnkie),
|
||||
offset);
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
offset = noffset;
|
||||
}
|
||||
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
|
||||
/* add any remaining IEs */
|
||||
if (extra_ies_len) {
|
||||
noffset = extra_ies_len;
|
||||
pos = skb_put(skb, noffset - offset);
|
||||
memcpy(pos, extra_ies + offset, noffset - offset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
u16 status_code, bool initiator,
|
||||
const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
{
|
||||
if (status_code == 0)
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
|
||||
if (extra_ies_len)
|
||||
memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
|
||||
}
|
||||
|
||||
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, const u8 *peer,
|
||||
u8 action_code, u16 status_code,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len)
|
||||
size_t extra_ies_len, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
switch (action_code) {
|
||||
case WLAN_TDLS_SETUP_REQUEST:
|
||||
@ -393,6 +537,18 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
|
||||
if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
|
||||
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
|
||||
break;
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
|
||||
ieee80211_tdls_add_chan_switch_req_ies(sdata, skb, peer,
|
||||
initiator, extra_ies,
|
||||
extra_ies_len,
|
||||
oper_class, chandef);
|
||||
break;
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
|
||||
ieee80211_tdls_add_chan_switch_resp_ies(sdata, skb, peer,
|
||||
status_code,
|
||||
initiator, extra_ies,
|
||||
extra_ies_len);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@ -459,6 +615,19 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
|
||||
skb_put(skb, sizeof(tf->u.discover_req));
|
||||
tf->u.discover_req.dialog_token = dialog_token;
|
||||
break;
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
|
||||
tf->category = WLAN_CATEGORY_TDLS;
|
||||
tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
|
||||
|
||||
skb_put(skb, sizeof(tf->u.chan_switch_req));
|
||||
break;
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
|
||||
tf->category = WLAN_CATEGORY_TDLS;
|
||||
tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
|
||||
|
||||
skb_put(skb, sizeof(tf->u.chan_switch_resp));
|
||||
tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -502,32 +671,33 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code,
|
||||
u8 dialog_token, u16 status_code,
|
||||
u32 peer_capability, bool initiator,
|
||||
const u8 *extra_ies, size_t extra_ies_len)
|
||||
static struct sk_buff *
|
||||
ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *peer, u8 action_code,
|
||||
u8 dialog_token, u16 status_code,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb = NULL;
|
||||
bool send_direct;
|
||||
struct sta_info *sta;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
max(sizeof(struct ieee80211_mgmt),
|
||||
sizeof(struct ieee80211_tdls_data)) +
|
||||
50 + /* supported rates */
|
||||
7 + /* ext capab */
|
||||
26 + /* max(WMM-info, WMM-param) */
|
||||
2 + max(sizeof(struct ieee80211_ht_cap),
|
||||
sizeof(struct ieee80211_ht_operation)) +
|
||||
extra_ies_len +
|
||||
sizeof(struct ieee80211_tdls_lnkie));
|
||||
skb = netdev_alloc_skb(sdata->dev,
|
||||
local->hw.extra_tx_headroom +
|
||||
max(sizeof(struct ieee80211_mgmt),
|
||||
sizeof(struct ieee80211_tdls_data)) +
|
||||
50 + /* supported rates */
|
||||
7 + /* ext capab */
|
||||
26 + /* max(WMM-info, WMM-param) */
|
||||
2 + max(sizeof(struct ieee80211_ht_cap),
|
||||
sizeof(struct ieee80211_ht_operation)) +
|
||||
50 + /* supported channels */
|
||||
3 + /* 40/20 BSS coex */
|
||||
extra_ies_len +
|
||||
sizeof(struct ieee80211_tdls_lnkie));
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
return NULL;
|
||||
|
||||
skb_reserve(skb, local->hw.extra_tx_headroom);
|
||||
|
||||
@ -537,16 +707,18 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
case WLAN_TDLS_SETUP_CONFIRM:
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
case WLAN_TDLS_DISCOVERY_REQUEST:
|
||||
ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
|
||||
ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy,
|
||||
sdata->dev, peer,
|
||||
action_code, dialog_token,
|
||||
status_code, skb);
|
||||
send_direct = false;
|
||||
break;
|
||||
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
|
||||
ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
|
||||
ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev,
|
||||
peer, action_code,
|
||||
dialog_token, status_code,
|
||||
skb);
|
||||
send_direct = true;
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUPP;
|
||||
@ -556,6 +728,30 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
|
||||
initiator, extra_ies, extra_ies_len, oper_class,
|
||||
chandef);
|
||||
return skb;
|
||||
|
||||
fail:
|
||||
dev_kfree_skb(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *peer, u8 action_code, u8 dialog_token,
|
||||
u16 status_code, u32 peer_capability,
|
||||
bool initiator, const u8 *extra_ies,
|
||||
size_t extra_ies_len, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct sk_buff *skb = NULL;
|
||||
struct sta_info *sta;
|
||||
u32 flags = 0;
|
||||
int ret = 0;
|
||||
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, peer);
|
||||
|
||||
@ -586,6 +782,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
initiator = false;
|
||||
break;
|
||||
case WLAN_TDLS_TEARDOWN:
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
|
||||
/* any value is ok */
|
||||
break;
|
||||
default:
|
||||
@ -600,9 +798,17 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
|
||||
initiator, extra_ies, extra_ies_len);
|
||||
if (send_direct) {
|
||||
skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code,
|
||||
dialog_token, status_code,
|
||||
initiator, extra_ies,
|
||||
extra_ies_len, oper_class,
|
||||
chandef);
|
||||
if (!skb) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
}
|
||||
@ -623,9 +829,44 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
|
||||
* Later, if no ACK is returned from peer, we will re-send the teardown
|
||||
* packet through the AP.
|
||||
*/
|
||||
if ((action_code == WLAN_TDLS_TEARDOWN) &&
|
||||
(sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
|
||||
struct sta_info *sta = NULL;
|
||||
bool try_resend; /* Should we keep skb for possible resend */
|
||||
|
||||
/* If not sending directly to peer - no point in keeping skb */
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, peer);
|
||||
try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
|
||||
rcu_read_unlock();
|
||||
|
||||
spin_lock_bh(&sdata->u.mgd.teardown_lock);
|
||||
if (try_resend && !sdata->u.mgd.teardown_skb) {
|
||||
/* Mark it as requiring TX status callback */
|
||||
flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
|
||||
IEEE80211_TX_INTFL_MLME_CONN_TX;
|
||||
|
||||
/*
|
||||
* skb is copied since mac80211 will later set
|
||||
* properties that might not be the same as the AP,
|
||||
* such as encryption, QoS, addresses, etc.
|
||||
*
|
||||
* No problem if skb_copy() fails, so no need to check.
|
||||
*/
|
||||
sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC);
|
||||
sdata->u.mgd.orig_teardown_skb = skb;
|
||||
}
|
||||
spin_unlock_bh(&sdata->u.mgd.teardown_lock);
|
||||
}
|
||||
|
||||
/* disable bottom halves when entering the Tx path */
|
||||
local_bh_disable();
|
||||
ret = ieee80211_subif_start_xmit(skb, dev);
|
||||
__ieee80211_subif_start_xmit(skb, dev, flags);
|
||||
local_bh_enable();
|
||||
|
||||
return ret;
|
||||
@ -676,7 +917,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
|
||||
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code,
|
||||
peer_capability, initiator,
|
||||
extra_ies, extra_ies_len);
|
||||
extra_ies, extra_ies_len, 0,
|
||||
NULL);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
@ -715,7 +957,8 @@ ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
|
||||
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
|
||||
dialog_token, status_code,
|
||||
peer_capability, initiator,
|
||||
extra_ies, extra_ies_len);
|
||||
extra_ies, extra_ies_len, 0,
|
||||
NULL);
|
||||
if (ret < 0)
|
||||
sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
|
||||
ret);
|
||||
@ -785,7 +1028,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
||||
status_code,
|
||||
peer_capability,
|
||||
initiator, extra_ies,
|
||||
extra_ies_len);
|
||||
extra_ies_len, 0, NULL);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
@ -888,3 +1131,480 @@ void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
|
||||
cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_tdls_oper_request);
|
||||
|
||||
static void
|
||||
iee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout)
|
||||
{
|
||||
struct ieee80211_ch_switch_timing *ch_sw;
|
||||
|
||||
*buf++ = WLAN_EID_CHAN_SWITCH_TIMING;
|
||||
*buf++ = sizeof(struct ieee80211_ch_switch_timing);
|
||||
|
||||
ch_sw = (void *)buf;
|
||||
ch_sw->switch_time = cpu_to_le16(switch_time);
|
||||
ch_sw->switch_timeout = cpu_to_le16(switch_timeout);
|
||||
}
|
||||
|
||||
/* find switch timing IE in SKB ready for Tx */
|
||||
static const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tdls_data *tf;
|
||||
const u8 *ie_start;
|
||||
|
||||
/*
|
||||
* Get the offset for the new location of the switch timing IE.
|
||||
* The SKB network header will now point to the "payload_type"
|
||||
* element of the TDLS data frame struct.
|
||||
*/
|
||||
tf = container_of(skb->data + skb_network_offset(skb),
|
||||
struct ieee80211_tdls_data, payload_type);
|
||||
ie_start = tf->u.chan_switch_req.variable;
|
||||
return cfg80211_find_ie(WLAN_EID_CHAN_SWITCH_TIMING, ie_start,
|
||||
skb->len - (ie_start - skb->data));
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
u32 *ch_sw_tm_ie_offset)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
u8 extra_ies[2 + sizeof(struct ieee80211_sec_chan_offs_ie) +
|
||||
2 + sizeof(struct ieee80211_ch_switch_timing)];
|
||||
int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing);
|
||||
u8 *pos = extra_ies;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/*
|
||||
* if chandef points to a wide channel add a Secondary-Channel
|
||||
* Offset information element
|
||||
*/
|
||||
if (chandef->width == NL80211_CHAN_WIDTH_40) {
|
||||
struct ieee80211_sec_chan_offs_ie *sec_chan_ie;
|
||||
bool ht40plus;
|
||||
|
||||
*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
|
||||
*pos++ = sizeof(*sec_chan_ie);
|
||||
sec_chan_ie = (void *)pos;
|
||||
|
||||
ht40plus = cfg80211_get_chandef_type(chandef) ==
|
||||
NL80211_CHAN_HT40PLUS;
|
||||
sec_chan_ie->sec_chan_offs = ht40plus ?
|
||||
IEEE80211_HT_PARAM_CHA_SEC_ABOVE :
|
||||
IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
||||
pos += sizeof(*sec_chan_ie);
|
||||
|
||||
extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie);
|
||||
}
|
||||
|
||||
/* just set the values to 0, this is a template */
|
||||
iee80211_tdls_add_ch_switch_timing(pos, 0, 0);
|
||||
|
||||
skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
|
||||
WLAN_TDLS_CHANNEL_SWITCH_REQUEST,
|
||||
0, 0, !sta->sta.tdls_initiator,
|
||||
extra_ies, extra_ies_len,
|
||||
oper_class, chandef);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
skb = ieee80211_build_data_template(sdata, skb, 0);
|
||||
if (IS_ERR(skb)) {
|
||||
tdls_dbg(sdata, "Failed building TDLS channel switch frame\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ch_sw_tm_ie_offset) {
|
||||
const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
|
||||
|
||||
if (!tm_ie) {
|
||||
tdls_dbg(sdata, "No switch timing IE in TDLS switch\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ch_sw_tm_ie_offset = tm_ie - skb->data;
|
||||
}
|
||||
|
||||
tdls_dbg(sdata,
|
||||
"TDLS channel switch request template for %pM ch %d width %d\n",
|
||||
sta->sta.addr, chandef->chan->center_freq, chandef->width);
|
||||
return skb;
|
||||
}
|
||||
|
||||
int
|
||||
ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *addr, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta;
|
||||
struct sk_buff *skb = NULL;
|
||||
u32 ch_sw_tm_ie;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get(sdata, addr);
|
||||
if (!sta) {
|
||||
tdls_dbg(sdata,
|
||||
"Invalid TDLS peer %pM for channel switch request\n",
|
||||
addr);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!test_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH)) {
|
||||
tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n",
|
||||
addr);
|
||||
ret = -ENOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef,
|
||||
&ch_sw_tm_ie);
|
||||
if (!skb) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = drv_tdls_channel_switch(local, sdata, &sta->sta, oper_class,
|
||||
chandef, skb, ch_sw_tm_ie);
|
||||
if (!ret)
|
||||
set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
|
||||
|
||||
out:
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
const u8 *addr)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta;
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get(sdata, addr);
|
||||
if (!sta) {
|
||||
tdls_dbg(sdata,
|
||||
"Invalid TDLS peer %pM for channel switch cancel\n",
|
||||
addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
|
||||
tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n",
|
||||
addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
|
||||
clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
|
||||
|
||||
out:
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
}
|
||||
|
||||
static struct sk_buff *
|
||||
ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta,
|
||||
u32 *ch_sw_tm_ie_offset)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct sk_buff *skb;
|
||||
u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)];
|
||||
|
||||
/* initial timing are always zero in the template */
|
||||
iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0);
|
||||
|
||||
skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
|
||||
WLAN_TDLS_CHANNEL_SWITCH_RESPONSE,
|
||||
0, 0, !sta->sta.tdls_initiator,
|
||||
extra_ies, sizeof(extra_ies), 0, NULL);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
skb = ieee80211_build_data_template(sdata, skb, 0);
|
||||
if (IS_ERR(skb)) {
|
||||
tdls_dbg(sdata,
|
||||
"Failed building TDLS channel switch resp frame\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ch_sw_tm_ie_offset) {
|
||||
const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
|
||||
|
||||
if (!tm_ie) {
|
||||
tdls_dbg(sdata,
|
||||
"No switch timing IE in TDLS switch resp\n");
|
||||
dev_kfree_skb_any(skb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*ch_sw_tm_ie_offset = tm_ie - skb->data;
|
||||
}
|
||||
|
||||
tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n",
|
||||
sta->sta.addr);
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee802_11_elems elems;
|
||||
struct sta_info *sta;
|
||||
struct ieee80211_tdls_data *tf = (void *)skb->data;
|
||||
bool local_initiator;
|
||||
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
|
||||
int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable);
|
||||
struct ieee80211_tdls_ch_sw_params params = {};
|
||||
int ret;
|
||||
|
||||
params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
|
||||
params.timestamp = rx_status->device_timestamp;
|
||||
|
||||
if (skb->len < baselen) {
|
||||
tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n",
|
||||
skb->len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get(sdata, tf->sa);
|
||||
if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
|
||||
tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
|
||||
tf->sa);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
params.sta = &sta->sta;
|
||||
params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code);
|
||||
if (params.status != 0) {
|
||||
ret = 0;
|
||||
goto call_drv;
|
||||
}
|
||||
|
||||
ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
|
||||
skb->len - baselen, false, &elems);
|
||||
if (elems.parse_error) {
|
||||
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!elems.ch_sw_timing || !elems.lnk_id) {
|
||||
tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* validate the initiator is set correctly */
|
||||
local_initiator =
|
||||
!memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
|
||||
if (local_initiator == sta->sta.tdls_initiator) {
|
||||
tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
|
||||
params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
|
||||
|
||||
params.tmpl_skb =
|
||||
ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ¶ms.ch_sw_tm_ie);
|
||||
if (!params.tmpl_skb) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
call_drv:
|
||||
drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms);
|
||||
|
||||
tdls_dbg(sdata,
|
||||
"TDLS channel switch response received from %pM status %d\n",
|
||||
tf->sa, params.status);
|
||||
|
||||
out:
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
dev_kfree_skb_any(params.tmpl_skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee802_11_elems elems;
|
||||
struct cfg80211_chan_def chandef;
|
||||
struct ieee80211_channel *chan;
|
||||
enum nl80211_channel_type chan_type;
|
||||
int freq;
|
||||
u8 target_channel, oper_class;
|
||||
bool local_initiator;
|
||||
struct sta_info *sta;
|
||||
enum ieee80211_band band;
|
||||
struct ieee80211_tdls_data *tf = (void *)skb->data;
|
||||
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
|
||||
int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable);
|
||||
struct ieee80211_tdls_ch_sw_params params = {};
|
||||
int ret = 0;
|
||||
|
||||
params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
|
||||
params.timestamp = rx_status->device_timestamp;
|
||||
|
||||
if (skb->len < baselen) {
|
||||
tdls_dbg(sdata, "TDLS channel switch req too short: %d\n",
|
||||
skb->len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
target_channel = tf->u.chan_switch_req.target_channel;
|
||||
oper_class = tf->u.chan_switch_req.oper_class;
|
||||
|
||||
/*
|
||||
* We can't easily infer the channel band. The operating class is
|
||||
* ambiguous - there are multiple tables (US/Europe/JP/Global). The
|
||||
* solution here is to treat channels with number >14 as 5GHz ones,
|
||||
* and specifically check for the (oper_class, channel) combinations
|
||||
* where this doesn't hold. These are thankfully unique according to
|
||||
* IEEE802.11-2012.
|
||||
* We consider only the 2GHz and 5GHz bands and 20MHz+ channels as
|
||||
* valid here.
|
||||
*/
|
||||
if ((oper_class == 112 || oper_class == 2 || oper_class == 3 ||
|
||||
oper_class == 4 || oper_class == 5 || oper_class == 6) &&
|
||||
target_channel < 14)
|
||||
band = IEEE80211_BAND_5GHZ;
|
||||
else
|
||||
band = target_channel < 14 ? IEEE80211_BAND_2GHZ :
|
||||
IEEE80211_BAND_5GHZ;
|
||||
|
||||
freq = ieee80211_channel_to_frequency(target_channel, band);
|
||||
if (freq == 0) {
|
||||
tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n",
|
||||
target_channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
|
||||
if (!chan) {
|
||||
tdls_dbg(sdata,
|
||||
"Unsupported channel for TDLS chan switch: %d\n",
|
||||
target_channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
|
||||
skb->len - baselen, false, &elems);
|
||||
if (elems.parse_error) {
|
||||
tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!elems.ch_sw_timing || !elems.lnk_id) {
|
||||
tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&local->sta_mtx);
|
||||
sta = sta_info_get(sdata, tf->sa);
|
||||
if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
|
||||
tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
|
||||
tf->sa);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
params.sta = &sta->sta;
|
||||
|
||||
/* validate the initiator is set correctly */
|
||||
local_initiator =
|
||||
!memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
|
||||
if (local_initiator == sta->sta.tdls_initiator) {
|
||||
tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!sta->sta.ht_cap.ht_supported) {
|
||||
chan_type = NL80211_CHAN_NO_HT;
|
||||
} else if (!elems.sec_chan_offs) {
|
||||
chan_type = NL80211_CHAN_HT20;
|
||||
} else {
|
||||
switch (elems.sec_chan_offs->sec_chan_offs) {
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
chan_type = NL80211_CHAN_HT40PLUS;
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
chan_type = NL80211_CHAN_HT40MINUS;
|
||||
break;
|
||||
default:
|
||||
chan_type = NL80211_CHAN_HT20;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cfg80211_chandef_create(&chandef, chan, chan_type);
|
||||
params.chandef = &chandef;
|
||||
|
||||
params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
|
||||
params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
|
||||
|
||||
params.tmpl_skb =
|
||||
ieee80211_tdls_ch_sw_resp_tmpl_get(sta,
|
||||
¶ms.ch_sw_tm_ie);
|
||||
if (!params.tmpl_skb) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms);
|
||||
|
||||
tdls_dbg(sdata,
|
||||
"TDLS ch switch request received from %pM ch %d width %d\n",
|
||||
tf->sa, params.chandef->chan->center_freq,
|
||||
params.chandef->width);
|
||||
out:
|
||||
mutex_unlock(&local->sta_mtx);
|
||||
dev_kfree_skb_any(params.tmpl_skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tdls_data *tf = (void *)skb->data;
|
||||
struct wiphy *wiphy = sdata->local->hw.wiphy;
|
||||
|
||||
/* make sure the driver supports it */
|
||||
if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
|
||||
return;
|
||||
|
||||
/* we want to access the entire packet */
|
||||
if (skb_linearize(skb))
|
||||
return;
|
||||
/*
|
||||
* The packet/size was already validated by mac80211 Rx path, only look
|
||||
* at the action type.
|
||||
*/
|
||||
switch (tf->action_code) {
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
|
||||
ieee80211_process_tdls_channel_switch_req(sdata, skb);
|
||||
break;
|
||||
case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
|
||||
ieee80211_process_tdls_channel_switch_resp(sdata, skb);
|
||||
break;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#define STA_ENTRY __array(char, sta_addr, ETH_ALEN)
|
||||
#define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN))
|
||||
#define STA_NAMED_ASSIGN(s) memcpy(__entry->sta_addr, (s)->addr, ETH_ALEN)
|
||||
#define STA_PR_FMT " sta:%pM"
|
||||
#define STA_PR_ARG __entry->sta_addr
|
||||
|
||||
@ -595,14 +596,33 @@ DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop,
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, drv_sw_scan_start,
|
||||
TP_PROTO(struct ieee80211_local *local),
|
||||
TP_ARGS(local)
|
||||
TRACE_EVENT(drv_sw_scan_start,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *mac_addr),
|
||||
|
||||
TP_ARGS(local, sdata, mac_addr),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
__array(char, mac_addr, ETH_ALEN)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
memcpy(__entry->mac_addr, mac_addr, ETH_ALEN);
|
||||
),
|
||||
|
||||
TP_printk(LOCAL_PR_FMT ", " VIF_PR_FMT ", addr:%pM",
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, __entry->mac_addr)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, drv_sw_scan_complete,
|
||||
TP_PROTO(struct ieee80211_local *local),
|
||||
TP_ARGS(local)
|
||||
DEFINE_EVENT(local_sdata_evt, drv_sw_scan_complete,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_get_stats,
|
||||
@ -826,6 +846,13 @@ DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove,
|
||||
TP_ARGS(local, sdata, sta)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(sta_event, drv_sta_rate_tbl_update,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta *sta),
|
||||
TP_ARGS(local, sdata, sta)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_conf_tx,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
@ -2140,6 +2167,7 @@ TRACE_EVENT(drv_pre_channel_switch,
|
||||
VIF_ENTRY
|
||||
CHANDEF_ENTRY
|
||||
__field(u64, timestamp)
|
||||
__field(u32, device_timestamp)
|
||||
__field(bool, block_tx)
|
||||
__field(u8, count)
|
||||
),
|
||||
@ -2149,6 +2177,7 @@ TRACE_EVENT(drv_pre_channel_switch,
|
||||
VIF_ASSIGN;
|
||||
CHANDEF_ASSIGN(&ch_switch->chandef)
|
||||
__entry->timestamp = ch_switch->timestamp;
|
||||
__entry->device_timestamp = ch_switch->device_timestamp;
|
||||
__entry->block_tx = ch_switch->block_tx;
|
||||
__entry->count = ch_switch->count;
|
||||
),
|
||||
@ -2194,6 +2223,107 @@ TRACE_EVENT(drv_get_txpower,
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_tdls_channel_switch,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta *sta, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef),
|
||||
|
||||
TP_ARGS(local, sdata, sta, oper_class, chandef),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
STA_ENTRY
|
||||
__field(u8, oper_class)
|
||||
CHANDEF_ENTRY
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
STA_ASSIGN;
|
||||
__entry->oper_class = oper_class;
|
||||
CHANDEF_ASSIGN(chandef)
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT " tdls channel switch to"
|
||||
CHANDEF_PR_FMT " oper_class:%d " STA_PR_FMT,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->oper_class,
|
||||
STA_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_tdls_cancel_channel_switch,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_sta *sta),
|
||||
|
||||
TP_ARGS(local, sdata, sta),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
STA_ENTRY
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
STA_ASSIGN;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT
|
||||
" tdls cancel channel switch with " STA_PR_FMT,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
TRACE_EVENT(drv_tdls_recv_channel_switch,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_tdls_ch_sw_params *params),
|
||||
|
||||
TP_ARGS(local, sdata, params),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
__field(u8, action_code)
|
||||
STA_ENTRY
|
||||
CHANDEF_ENTRY
|
||||
__field(u32, status)
|
||||
__field(bool, peer_initiator)
|
||||
__field(u32, timestamp)
|
||||
__field(u16, switch_time)
|
||||
__field(u16, switch_timeout)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
STA_NAMED_ASSIGN(params->sta);
|
||||
CHANDEF_ASSIGN(params->chandef)
|
||||
__entry->peer_initiator = params->sta->tdls_initiator;
|
||||
__entry->action_code = params->action_code;
|
||||
__entry->status = params->status;
|
||||
__entry->timestamp = params->timestamp;
|
||||
__entry->switch_time = params->switch_time;
|
||||
__entry->switch_timeout = params->switch_timeout;
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT " received tdls channel switch packet"
|
||||
" action:%d status:%d time:%d switch time:%d switch"
|
||||
" timeout:%d initiator: %d chan:" CHANDEF_PR_FMT STA_PR_FMT,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG, __entry->action_code, __entry->status,
|
||||
__entry->timestamp, __entry->switch_time,
|
||||
__entry->switch_timeout, __entry->peer_initiator,
|
||||
CHANDEF_PR_ARG, STA_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
|
||||
#undef TRACE_SYSTEM
|
||||
|
@ -1426,8 +1426,7 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
|
||||
* Returns false if the frame couldn't be transmitted but was queued instead.
|
||||
*/
|
||||
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, bool txpending,
|
||||
enum ieee80211_band band)
|
||||
struct sk_buff *skb, bool txpending)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_tx_data tx;
|
||||
@ -1452,8 +1451,6 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
|
||||
return true;
|
||||
}
|
||||
|
||||
info->band = band;
|
||||
|
||||
/* set up hw_queue value early */
|
||||
if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
|
||||
!(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
|
||||
@ -1501,8 +1498,7 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
|
||||
enum ieee80211_band band)
|
||||
void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
@ -1537,7 +1533,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
|
||||
}
|
||||
|
||||
ieee80211_set_qos_hdr(sdata, skb);
|
||||
ieee80211_tx(sdata, skb, false, band);
|
||||
ieee80211_tx(sdata, skb, false);
|
||||
}
|
||||
|
||||
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
|
||||
@ -1757,7 +1753,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
||||
sdata->vif.type))
|
||||
goto fail_rcu;
|
||||
|
||||
ieee80211_xmit(sdata, skb, chandef->chan->band);
|
||||
info->band = chandef->chan->band;
|
||||
ieee80211_xmit(sdata, skb);
|
||||
rcu_read_unlock();
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
@ -1787,23 +1784,26 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
|
||||
* subinterfaces (wlan#, WDS, and VLAN interfaces)
|
||||
* @skb: packet to be sent
|
||||
* @dev: incoming interface
|
||||
* ieee80211_build_hdr - build 802.11 header in the given frame
|
||||
* @sdata: virtual interface to build the header for
|
||||
* @skb: the skb to build the header in
|
||||
* @info_flags: skb flags to set
|
||||
*
|
||||
* Returns: NETDEV_TX_OK both on success and on failure. On failure skb will
|
||||
* be freed.
|
||||
* This function takes the skb with 802.3 header and reformats the header to
|
||||
* the appropriate IEEE 802.11 header based on which interface the packet is
|
||||
* being transmitted on.
|
||||
*
|
||||
* This function takes in an Ethernet header and encapsulates it with suitable
|
||||
* IEEE 802.11 header based on which interface the packet is coming in. The
|
||||
* encapsulated packet will then be passed to master interface, wlan#.11, for
|
||||
* transmission (through low-level driver).
|
||||
* Note that this function also takes care of the TX status request and
|
||||
* potential unsharing of the SKB - this needs to be interleaved with the
|
||||
* header building.
|
||||
*
|
||||
* The function requires the read-side RCU lock held
|
||||
*
|
||||
* Returns: the (possibly reallocated) skb or an ERR_PTR() code
|
||||
*/
|
||||
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u32 info_flags)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_tx_info *info;
|
||||
int head_need;
|
||||
@ -1819,25 +1819,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
bool wme_sta = false, authorized = false, tdls_auth = false;
|
||||
bool tdls_peer = false, tdls_setup_frame = false;
|
||||
bool multicast;
|
||||
u32 info_flags = 0;
|
||||
u16 info_id = 0;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_sub_if_data *ap_sdata;
|
||||
enum ieee80211_band band;
|
||||
|
||||
if (unlikely(skb->len < ETH_HLEN))
|
||||
goto fail;
|
||||
int ret;
|
||||
|
||||
/* convert Ethernet header to proper 802.11 header (based on
|
||||
* operation mode) */
|
||||
ethertype = (skb->data[12] << 8) | skb->data[13];
|
||||
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* Measure frame arrival for Tx latency statistics calculation */
|
||||
ieee80211_tx_latency_start_msrmnt(local, skb);
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
sta = rcu_dereference(sdata->u.vlan.sta);
|
||||
@ -1855,8 +1847,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
|
||||
u.ap);
|
||||
chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto fail_rcu;
|
||||
if (!chanctx_conf) {
|
||||
ret = -ENOTCONN;
|
||||
goto free;
|
||||
}
|
||||
band = chanctx_conf->def.chan->band;
|
||||
if (sta)
|
||||
break;
|
||||
@ -1864,8 +1858,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
case NL80211_IFTYPE_AP:
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto fail_rcu;
|
||||
if (!chanctx_conf) {
|
||||
ret = -ENOTCONN;
|
||||
goto free;
|
||||
}
|
||||
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
|
||||
/* DA BSSID SA */
|
||||
memcpy(hdr.addr1, skb->data, ETH_ALEN);
|
||||
@ -1952,8 +1948,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
|
||||
}
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto fail_rcu;
|
||||
if (!chanctx_conf) {
|
||||
ret = -ENOTCONN;
|
||||
goto free;
|
||||
}
|
||||
band = chanctx_conf->def.chan->band;
|
||||
break;
|
||||
#endif
|
||||
@ -1983,8 +1981,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
* of a link teardown after a TDLS sta is removed due to being
|
||||
* unreachable.
|
||||
*/
|
||||
if (tdls_peer && !tdls_auth && !tdls_setup_frame)
|
||||
goto fail_rcu;
|
||||
if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
|
||||
ret = -EINVAL;
|
||||
goto free;
|
||||
}
|
||||
|
||||
/* send direct packets to authorized TDLS peers */
|
||||
if (tdls_peer && tdls_auth) {
|
||||
@ -2012,8 +2012,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
hdrlen = 24;
|
||||
}
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto fail_rcu;
|
||||
if (!chanctx_conf) {
|
||||
ret = -ENOTCONN;
|
||||
goto free;
|
||||
}
|
||||
band = chanctx_conf->def.chan->band;
|
||||
break;
|
||||
case NL80211_IFTYPE_OCB:
|
||||
@ -2023,8 +2025,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
eth_broadcast_addr(hdr.addr3);
|
||||
hdrlen = 24;
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto fail_rcu;
|
||||
if (!chanctx_conf) {
|
||||
ret = -ENOTCONN;
|
||||
goto free;
|
||||
}
|
||||
band = chanctx_conf->def.chan->band;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
@ -2034,12 +2038,15 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
|
||||
hdrlen = 24;
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
goto fail_rcu;
|
||||
if (!chanctx_conf) {
|
||||
ret = -ENOTCONN;
|
||||
goto free;
|
||||
}
|
||||
band = chanctx_conf->def.chan->band;
|
||||
break;
|
||||
default:
|
||||
goto fail_rcu;
|
||||
ret = -EINVAL;
|
||||
goto free;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2077,12 +2084,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n",
|
||||
dev->name, hdr.addr1);
|
||||
sdata->name, hdr.addr1);
|
||||
#endif
|
||||
|
||||
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
|
||||
|
||||
goto fail_rcu;
|
||||
ret = -EPERM;
|
||||
goto free;
|
||||
}
|
||||
|
||||
if (unlikely(!multicast && skb->sk &&
|
||||
@ -2119,8 +2127,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
skb = skb_clone(skb, GFP_ATOMIC);
|
||||
kfree_skb(tmp_skb);
|
||||
|
||||
if (!skb)
|
||||
goto fail_rcu;
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
|
||||
hdr.frame_control = fc;
|
||||
@ -2169,7 +2179,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
|
||||
ieee80211_free_txskb(&local->hw, skb);
|
||||
skb = NULL;
|
||||
goto fail_rcu;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2203,9 +2213,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
nh_pos += hdrlen;
|
||||
h_pos += hdrlen;
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
|
||||
/* Update skb pointers to various headers since this modified frame
|
||||
* is going to go through Linux networking code that may potentially
|
||||
* need things like pointer to IP header. */
|
||||
@ -2216,23 +2223,90 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
dev->trans_start = jiffies;
|
||||
|
||||
info->flags = info_flags;
|
||||
info->ack_frame_id = info_id;
|
||||
info->band = band;
|
||||
|
||||
ieee80211_xmit(sdata, skb, band);
|
||||
return skb;
|
||||
free:
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev,
|
||||
u32 info_flags)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
if (unlikely(skb->len < ETH_HLEN)) {
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* Measure frame arrival for Tx latency statistics calculation */
|
||||
ieee80211_tx_latency_start_msrmnt(local, skb);
|
||||
|
||||
skb = ieee80211_build_hdr(sdata, skb, info_flags);
|
||||
if (IS_ERR(skb))
|
||||
goto out;
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
dev->trans_start = jiffies;
|
||||
|
||||
ieee80211_xmit(sdata, skb);
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
fail_rcu:
|
||||
rcu_read_unlock();
|
||||
fail:
|
||||
dev_kfree_skb(skb);
|
||||
/**
|
||||
* ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
|
||||
* @skb: packet to be sent
|
||||
* @dev: incoming interface
|
||||
*
|
||||
* On failure skb will be freed.
|
||||
*/
|
||||
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
__ieee80211_subif_start_xmit(skb, dev, 0);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
struct sk_buff *
|
||||
ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, u32 info_flags)
|
||||
{
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct ieee80211_tx_data tx = {
|
||||
.local = sdata->local,
|
||||
.sdata = sdata,
|
||||
};
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
skb = ieee80211_build_hdr(sdata, skb, info_flags);
|
||||
if (IS_ERR(skb))
|
||||
goto out;
|
||||
|
||||
hdr = (void *)skb->data;
|
||||
tx.sta = sta_info_get(sdata, hdr->addr1);
|
||||
tx.skb = skb;
|
||||
|
||||
if (ieee80211_tx_h_select_key(&tx) != TX_CONTINUE) {
|
||||
rcu_read_unlock();
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
return skb;
|
||||
}
|
||||
|
||||
/*
|
||||
* ieee80211_clear_tx_pending may not be called in a context where
|
||||
@ -2272,8 +2346,8 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
|
||||
dev_kfree_skb(skb);
|
||||
return true;
|
||||
}
|
||||
result = ieee80211_tx(sdata, skb, true,
|
||||
chanctx_conf->def.chan->band);
|
||||
info->band = chanctx_conf->def.chan->band;
|
||||
result = ieee80211_tx(sdata, skb, true);
|
||||
} else {
|
||||
struct sk_buff_head skbs;
|
||||
|
||||
@ -2887,19 +2961,16 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
|
||||
EXPORT_SYMBOL(ieee80211_nullfunc_get);
|
||||
|
||||
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
const u8 *src_addr,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
size_t tailroom)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct ieee80211_local *local;
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_hdr_3addr *hdr;
|
||||
struct sk_buff *skb;
|
||||
size_t ie_ssid_len;
|
||||
u8 *pos;
|
||||
|
||||
sdata = vif_to_sdata(vif);
|
||||
local = sdata->local;
|
||||
ie_ssid_len = 2 + ssid_len;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
|
||||
@ -2914,7 +2985,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
|
||||
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_PROBE_REQ);
|
||||
eth_broadcast_addr(hdr->addr1);
|
||||
memcpy(hdr->addr2, vif->addr, ETH_ALEN);
|
||||
memcpy(hdr->addr2, src_addr, ETH_ALEN);
|
||||
eth_broadcast_addr(hdr->addr3);
|
||||
|
||||
pos = skb_put(skb, ie_ssid_len);
|
||||
@ -3033,6 +3104,97 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
|
||||
|
||||
int ieee80211_reserve_tid(struct ieee80211_sta *pubsta, u8 tid)
|
||||
{
|
||||
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int ret;
|
||||
u32 queues;
|
||||
|
||||
lockdep_assert_held(&local->sta_mtx);
|
||||
|
||||
/* only some cases are supported right now */
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (WARN_ON(tid >= IEEE80211_NUM_UPS))
|
||||
return -EINVAL;
|
||||
|
||||
if (sta->reserved_tid == tid) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sta->reserved_tid != IEEE80211_TID_UNRESERVED) {
|
||||
sdata_err(sdata, "TID reservation already active\n");
|
||||
ret = -EALREADY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ieee80211_stop_vif_queues(sdata->local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
|
||||
|
||||
synchronize_net();
|
||||
|
||||
/* Tear down BA sessions so we stop aggregating on this TID */
|
||||
if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) {
|
||||
set_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
||||
__ieee80211_stop_tx_ba_session(sta, tid,
|
||||
AGG_STOP_LOCAL_REQUEST);
|
||||
}
|
||||
|
||||
queues = BIT(sdata->vif.hw_queue[ieee802_1d_to_ac[tid]]);
|
||||
__ieee80211_flush_queues(local, sdata, queues);
|
||||
|
||||
sta->reserved_tid = tid;
|
||||
|
||||
ieee80211_wake_vif_queues(local, sdata,
|
||||
IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
|
||||
|
||||
if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
|
||||
clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_reserve_tid);
|
||||
|
||||
void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
|
||||
{
|
||||
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
|
||||
lockdep_assert_held(&sdata->local->sta_mtx);
|
||||
|
||||
/* only some cases are supported right now */
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tid != sta->reserved_tid) {
|
||||
sdata_err(sdata, "TID to unreserve (%d) isn't reserved\n", tid);
|
||||
return;
|
||||
}
|
||||
|
||||
sta->reserved_tid = IEEE80211_TID_UNRESERVED;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_unreserve_tid);
|
||||
|
||||
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb, int tid,
|
||||
enum ieee80211_band band)
|
||||
@ -3054,6 +3216,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
|
||||
* requirements are that we do not come into tx with bhs on.
|
||||
*/
|
||||
local_bh_disable();
|
||||
ieee80211_xmit(sdata, skb, band);
|
||||
IEEE80211_SKB_CB(skb)->band = band;
|
||||
ieee80211_xmit(sdata, skb);
|
||||
local_bh_enable();
|
||||
}
|
||||
|
@ -576,15 +576,19 @@ ieee80211_get_vif_queues(struct ieee80211_local *local,
|
||||
return queues;
|
||||
}
|
||||
|
||||
void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
void __ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
unsigned int queues)
|
||||
{
|
||||
unsigned int queues;
|
||||
|
||||
if (!local->ops->flush)
|
||||
return;
|
||||
|
||||
queues = ieee80211_get_vif_queues(local, sdata);
|
||||
/*
|
||||
* If no queue was set, or if the HW doesn't support
|
||||
* IEEE80211_HW_QUEUE_CONTROL - flush all queues
|
||||
*/
|
||||
if (!queues || !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
|
||||
queues = ieee80211_get_vif_queues(local, sdata);
|
||||
|
||||
ieee80211_stop_queues_by_reason(&local->hw, queues,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH,
|
||||
@ -597,6 +601,12 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
false);
|
||||
}
|
||||
|
||||
void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
__ieee80211_flush_queues(local, sdata, 0);
|
||||
}
|
||||
|
||||
void ieee80211_stop_vif_queues(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
enum queue_stop_reason reason)
|
||||
@ -831,6 +841,9 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
|
||||
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
|
||||
case WLAN_EID_CHAN_SWITCH_PARAM:
|
||||
case WLAN_EID_EXT_CAPABILITY:
|
||||
case WLAN_EID_CHAN_SWITCH_TIMING:
|
||||
case WLAN_EID_LINK_ID:
|
||||
/*
|
||||
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
|
||||
* that if the content gets bigger it might be needed more than once
|
||||
@ -850,6 +863,24 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
|
||||
elem_parse_failed = false;
|
||||
|
||||
switch (id) {
|
||||
case WLAN_EID_LINK_ID:
|
||||
if (elen + 2 != sizeof(struct ieee80211_tdls_lnkie)) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
elems->lnk_id = (void *)(pos - 2);
|
||||
break;
|
||||
case WLAN_EID_CHAN_SWITCH_TIMING:
|
||||
if (elen != sizeof(struct ieee80211_ch_switch_timing)) {
|
||||
elem_parse_failed = true;
|
||||
break;
|
||||
}
|
||||
elems->ch_sw_timing = (void *)pos;
|
||||
break;
|
||||
case WLAN_EID_EXT_CAPABILITY:
|
||||
elems->ext_capab = pos;
|
||||
elems->ext_capab_len = elen;
|
||||
break;
|
||||
case WLAN_EID_SSID:
|
||||
elems->ssid = pos;
|
||||
elems->ssid_len = elen;
|
||||
@ -1492,7 +1523,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
|
||||
};
|
||||
|
||||
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
u8 *dst, u32 ratemask,
|
||||
const u8 *src, const u8 *dst,
|
||||
u32 ratemask,
|
||||
struct ieee80211_channel *chan,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len,
|
||||
@ -1517,8 +1549,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
else
|
||||
chandef.chan = chan;
|
||||
|
||||
skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
|
||||
ssid, ssid_len, 100 + ie_len);
|
||||
skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len,
|
||||
100 + ie_len);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
@ -1540,7 +1572,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
return skb;
|
||||
}
|
||||
|
||||
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
|
||||
const u8 *src, const u8 *dst,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len,
|
||||
u32 ratemask, bool directed, u32 tx_flags,
|
||||
@ -1548,7 +1581,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel,
|
||||
skb = ieee80211_build_probe_req(sdata, src, dst, ratemask, channel,
|
||||
ssid, ssid_len,
|
||||
ie, ie_len, directed);
|
||||
if (skb) {
|
||||
@ -1690,6 +1723,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
int res, i;
|
||||
bool reconfig_due_to_wowlan = false;
|
||||
struct ieee80211_sub_if_data *sched_scan_sdata;
|
||||
struct cfg80211_sched_scan_request *sched_scan_req;
|
||||
bool sched_scan_stopped = false;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -1980,13 +2014,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
||||
mutex_lock(&local->mtx);
|
||||
sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
|
||||
lockdep_is_held(&local->mtx));
|
||||
if (sched_scan_sdata && local->sched_scan_req)
|
||||
sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
|
||||
lockdep_is_held(&local->mtx));
|
||||
if (sched_scan_sdata && sched_scan_req)
|
||||
/*
|
||||
* Sched scan stopped, but we don't want to report it. Instead,
|
||||
* we're trying to reschedule.
|
||||
*/
|
||||
if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
|
||||
local->sched_scan_req))
|
||||
sched_scan_req))
|
||||
sched_scan_stopped = true;
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
|
@ -287,6 +287,8 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
bw = IEEE80211_STA_RX_BW_20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
|
||||
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
|
||||
|
@ -53,6 +53,36 @@ static int wme_downgrade_ac(struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ieee80211_fix_reserved_tid - return the TID to use if this one is reserved
|
||||
* @tid: the assumed-reserved TID
|
||||
*
|
||||
* Returns: the alternative TID to use, or 0 on error
|
||||
*/
|
||||
static inline u8 ieee80211_fix_reserved_tid(u8 tid)
|
||||
{
|
||||
switch (tid) {
|
||||
case 0:
|
||||
return 3;
|
||||
case 1:
|
||||
return 2;
|
||||
case 2:
|
||||
return 1;
|
||||
case 3:
|
||||
return 0;
|
||||
case 4:
|
||||
return 5;
|
||||
case 5:
|
||||
return 4;
|
||||
case 6:
|
||||
return 7;
|
||||
case 7:
|
||||
return 6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
|
||||
struct sta_info *sta, struct sk_buff *skb)
|
||||
{
|
||||
@ -77,6 +107,10 @@ static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to see if this is a reserved TID */
|
||||
if (sta && sta->reserved_tid == skb->priority)
|
||||
skb->priority = ieee80211_fix_reserved_tid(skb->priority);
|
||||
|
||||
/* look up which queue to use for frames with this 1d tag */
|
||||
return ieee802_1d_to_ac[skb->priority];
|
||||
}
|
||||
@ -143,6 +177,11 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
break;
|
||||
#endif
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* might be a TDLS station */
|
||||
sta = sta_info_get(sdata, skb->data);
|
||||
if (sta)
|
||||
qos = sta->sta.wme;
|
||||
|
||||
ra = sdata->u.mgd.bssid;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
|
@ -541,6 +541,10 @@ int wiphy_register(struct wiphy *wiphy)
|
||||
!wiphy->wowlan->tcp))
|
||||
return -EINVAL;
|
||||
#endif
|
||||
if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
|
||||
(!rdev->ops->tdls_channel_switch ||
|
||||
!rdev->ops->tdls_cancel_channel_switch)))
|
||||
return -EINVAL;
|
||||
|
||||
if (WARN_ON(wiphy->coalesce &&
|
||||
(!wiphy->coalesce->n_rules ||
|
||||
|
@ -111,6 +111,7 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
|
||||
rdev->wiphy.wowlan_config->tcp->sock)
|
||||
sock_release(rdev->wiphy.wowlan_config->tcp->sock);
|
||||
kfree(rdev->wiphy.wowlan_config->tcp);
|
||||
kfree(rdev->wiphy.wowlan_config->nd_config);
|
||||
kfree(rdev->wiphy.wowlan_config);
|
||||
#endif
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
|
||||
}
|
||||
|
||||
/* policy for the attributes */
|
||||
static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
||||
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
||||
.len = 20-1 },
|
||||
@ -388,13 +388,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
||||
[NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
|
||||
[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
|
||||
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
|
||||
[NL80211_ATTR_TSID] = { .type = NLA_U8 },
|
||||
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
|
||||
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
|
||||
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
|
||||
[NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@ -428,6 +429,7 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
|
||||
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
|
||||
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
|
||||
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
|
||||
[NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
static const struct nla_policy
|
||||
@ -1088,6 +1090,8 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
|
||||
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
|
||||
return -ENOBUFS;
|
||||
|
||||
/* TODO: send wowlan net detect */
|
||||
|
||||
nla_nest_end(msg, nl_wowlan);
|
||||
|
||||
return 0;
|
||||
@ -2341,12 +2345,16 @@ static int nl80211_send_chandef(struct sk_buff *msg,
|
||||
|
||||
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
|
||||
struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev)
|
||||
struct wireless_dev *wdev, bool removal)
|
||||
{
|
||||
struct net_device *dev = wdev->netdev;
|
||||
u8 cmd = NL80211_CMD_NEW_INTERFACE;
|
||||
void *hdr;
|
||||
|
||||
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
|
||||
if (removal)
|
||||
cmd = NL80211_CMD_DEL_INTERFACE;
|
||||
|
||||
hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
|
||||
if (!hdr)
|
||||
return -1;
|
||||
|
||||
@ -2413,7 +2421,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
|
||||
}
|
||||
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
|
||||
cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
||||
rdev, wdev) < 0) {
|
||||
rdev, wdev, false) < 0) {
|
||||
goto out;
|
||||
}
|
||||
if_idx++;
|
||||
@ -2441,7 +2449,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
return -ENOMEM;
|
||||
|
||||
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
||||
rdev, wdev) < 0) {
|
||||
rdev, wdev, false) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
@ -2587,7 +2595,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct vif_params params;
|
||||
struct wireless_dev *wdev;
|
||||
struct sk_buff *msg;
|
||||
struct sk_buff *msg, *event;
|
||||
int err;
|
||||
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
|
||||
u32 flags;
|
||||
@ -2641,12 +2649,15 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
wdev = rdev_add_virtual_intf(rdev,
|
||||
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
|
||||
type, err ? NULL : &flags, ¶ms);
|
||||
if (IS_ERR(wdev)) {
|
||||
if (WARN_ON(!wdev)) {
|
||||
nlmsg_free(msg);
|
||||
return -EPROTO;
|
||||
} else if (IS_ERR(wdev)) {
|
||||
nlmsg_free(msg);
|
||||
return PTR_ERR(wdev);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
|
||||
if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
|
||||
wdev->owner_nlportid = info->snd_portid;
|
||||
|
||||
switch (type) {
|
||||
@ -2682,11 +2693,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
}
|
||||
|
||||
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
|
||||
rdev, wdev) < 0) {
|
||||
rdev, wdev, false) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (event) {
|
||||
if (nl80211_send_iface(event, 0, 0, 0,
|
||||
rdev, wdev, false) < 0) {
|
||||
nlmsg_free(event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
|
||||
event, 0, NL80211_MCGRP_CONFIG,
|
||||
GFP_KERNEL);
|
||||
}
|
||||
|
||||
out:
|
||||
return genlmsg_reply(msg, info);
|
||||
}
|
||||
|
||||
@ -2694,10 +2719,18 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct wireless_dev *wdev = info->user_ptr[1];
|
||||
struct sk_buff *msg;
|
||||
int status;
|
||||
|
||||
if (!rdev->ops->del_virtual_intf)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) {
|
||||
nlmsg_free(msg);
|
||||
msg = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we remove a wireless device without a netdev then clear
|
||||
* user_ptr[1] so that nl80211_post_doit won't dereference it
|
||||
@ -2708,7 +2741,15 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!wdev->netdev)
|
||||
info->user_ptr[1] = NULL;
|
||||
|
||||
return rdev_del_virtual_intf(rdev, wdev);
|
||||
status = rdev_del_virtual_intf(rdev, wdev);
|
||||
if (status >= 0 && msg)
|
||||
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
|
||||
msg, 0, NL80211_MCGRP_CONFIG,
|
||||
GFP_KERNEL);
|
||||
else
|
||||
nlmsg_free(msg);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
|
||||
@ -4453,7 +4494,7 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
|
||||
void *hdr;
|
||||
struct nlattr *pinfoattr;
|
||||
|
||||
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
|
||||
hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_MPATH);
|
||||
if (!hdr)
|
||||
return -1;
|
||||
|
||||
@ -5478,6 +5519,43 @@ static int validate_scan_freqs(struct nlattr *freqs)
|
||||
return n_channels;
|
||||
}
|
||||
|
||||
static int nl80211_parse_random_mac(struct nlattr **attrs,
|
||||
u8 *mac_addr, u8 *mac_addr_mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
|
||||
memset(mac_addr, 0, ETH_ALEN);
|
||||
memset(mac_addr_mask, 0, ETH_ALEN);
|
||||
mac_addr[0] = 0x2;
|
||||
mac_addr_mask[0] = 0x3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* need both or none */
|
||||
if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK])
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN);
|
||||
memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN);
|
||||
|
||||
/* don't allow or configure an mcast address */
|
||||
if (!is_multicast_ether_addr(mac_addr_mask) ||
|
||||
is_multicast_ether_addr(mac_addr))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* allow users to pass a MAC address that has bits set outside
|
||||
* of the mask, but don't bother drivers with having to deal
|
||||
* with such bits
|
||||
*/
|
||||
for (i = 0; i < ETH_ALEN; i++)
|
||||
mac_addr[i] &= mac_addr_mask[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@ -5655,6 +5733,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
|
||||
if (!(wiphy->features &
|
||||
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (wdev->current_bss) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
err = nl80211_parse_random_mac(info->attrs,
|
||||
request->mac_addr,
|
||||
request->mac_addr_mask);
|
||||
if (err)
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
request->no_cck =
|
||||
@ -5681,14 +5778,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
static struct cfg80211_sched_scan_request *
|
||||
nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
struct nlattr **attrs)
|
||||
{
|
||||
struct cfg80211_sched_scan_request *request;
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct nlattr *attr;
|
||||
struct wiphy *wiphy;
|
||||
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
|
||||
u32 interval;
|
||||
enum ieee80211_band band;
|
||||
@ -5696,38 +5791,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
|
||||
s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
|
||||
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
||||
!rdev->ops->sched_scan_start)
|
||||
return -EOPNOTSUPP;
|
||||
if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
|
||||
return -EINVAL;
|
||||
if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
|
||||
return -EINVAL;
|
||||
|
||||
interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
|
||||
interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
|
||||
if (interval == 0)
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
wiphy = &rdev->wiphy;
|
||||
|
||||
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
||||
if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
||||
n_channels = validate_scan_freqs(
|
||||
info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
|
||||
attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
|
||||
if (!n_channels)
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
} else {
|
||||
n_channels = ieee80211_get_num_supported_channels(wiphy);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
|
||||
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
||||
if (attrs[NL80211_ATTR_SCAN_SSIDS])
|
||||
nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
|
||||
tmp)
|
||||
n_ssids++;
|
||||
|
||||
if (n_ssids > wiphy->max_sched_scan_ssids)
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/*
|
||||
* First, count the number of 'real' matchsets. Due to an issue with
|
||||
@ -5738,9 +5827,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
* older userspace that treated a matchset with only the RSSI as the
|
||||
* global RSSI for all other matchsets - if there are other matchsets.
|
||||
*/
|
||||
if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
||||
if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
||||
nla_for_each_nested(attr,
|
||||
info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
||||
attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
||||
tmp) {
|
||||
struct nlattr *rssi;
|
||||
|
||||
@ -5748,7 +5837,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
nla_data(attr), nla_len(attr),
|
||||
nl80211_match_policy);
|
||||
if (err)
|
||||
return err;
|
||||
return ERR_PTR(err);
|
||||
/* add other standalone attributes here */
|
||||
if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
|
||||
n_match_sets++;
|
||||
@ -5765,30 +5854,23 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
n_match_sets = 1;
|
||||
|
||||
if (n_match_sets > wiphy->max_match_sets)
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (info->attrs[NL80211_ATTR_IE])
|
||||
ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
|
||||
if (attrs[NL80211_ATTR_IE])
|
||||
ie_len = nla_len(attrs[NL80211_ATTR_IE]);
|
||||
else
|
||||
ie_len = 0;
|
||||
|
||||
if (ie_len > wiphy->max_sched_scan_ie_len)
|
||||
return -EINVAL;
|
||||
|
||||
if (rdev->sched_scan_req) {
|
||||
err = -EINPROGRESS;
|
||||
goto out;
|
||||
}
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
request = kzalloc(sizeof(*request)
|
||||
+ sizeof(*request->ssids) * n_ssids
|
||||
+ sizeof(*request->match_sets) * n_match_sets
|
||||
+ sizeof(*request->channels) * n_channels
|
||||
+ ie_len, GFP_KERNEL);
|
||||
if (!request) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!request)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (n_ssids)
|
||||
request->ssids = (void *)&request->channels[n_channels];
|
||||
@ -5813,10 +5895,10 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
request->n_match_sets = n_match_sets;
|
||||
|
||||
i = 0;
|
||||
if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
||||
if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
|
||||
/* user specified, bail out if channel not found */
|
||||
nla_for_each_nested(attr,
|
||||
info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
|
||||
attrs[NL80211_ATTR_SCAN_FREQUENCIES],
|
||||
tmp) {
|
||||
struct ieee80211_channel *chan;
|
||||
|
||||
@ -5862,8 +5944,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
request->n_channels = i;
|
||||
|
||||
i = 0;
|
||||
if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
|
||||
nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
|
||||
if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
|
||||
nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
|
||||
tmp) {
|
||||
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
|
||||
err = -EINVAL;
|
||||
@ -5877,9 +5959,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
}
|
||||
|
||||
i = 0;
|
||||
if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
||||
if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
|
||||
nla_for_each_nested(attr,
|
||||
info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
||||
attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
|
||||
tmp) {
|
||||
struct nlattr *ssid, *rssi;
|
||||
|
||||
@ -5934,36 +6016,88 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
if (ie_len) {
|
||||
request->ie_len = ie_len;
|
||||
memcpy((void *)request->ie,
|
||||
nla_data(info->attrs[NL80211_ATTR_IE]),
|
||||
nla_data(attrs[NL80211_ATTR_IE]),
|
||||
request->ie_len);
|
||||
}
|
||||
|
||||
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
|
||||
if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
|
||||
request->flags = nla_get_u32(
|
||||
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
|
||||
attrs[NL80211_ATTR_SCAN_FLAGS]);
|
||||
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
|
||||
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
|
||||
u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
|
||||
|
||||
if (!wdev) /* must be net-detect */
|
||||
flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
|
||||
|
||||
if (!(wiphy->features & flg)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (wdev && wdev->current_bss) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
err = nl80211_parse_random_mac(attrs, request->mac_addr,
|
||||
request->mac_addr_mask);
|
||||
if (err)
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
request->dev = dev;
|
||||
request->wiphy = &rdev->wiphy;
|
||||
request->interval = interval;
|
||||
request->scan_start = jiffies;
|
||||
|
||||
err = rdev_sched_scan_start(rdev, dev, request);
|
||||
if (!err) {
|
||||
rdev->sched_scan_req = request;
|
||||
nl80211_send_sched_scan(rdev, dev,
|
||||
NL80211_CMD_START_SCHED_SCAN);
|
||||
goto out;
|
||||
}
|
||||
return request;
|
||||
|
||||
out_free:
|
||||
kfree(request);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int nl80211_start_sched_scan(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
int err;
|
||||
|
||||
if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
|
||||
!rdev->ops->sched_scan_start)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (rdev->sched_scan_req)
|
||||
return -EINPROGRESS;
|
||||
|
||||
rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
|
||||
info->attrs);
|
||||
err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
rdev->sched_scan_req->dev = dev;
|
||||
rdev->sched_scan_req->wiphy = &rdev->wiphy;
|
||||
|
||||
nl80211_send_sched_scan(rdev, dev,
|
||||
NL80211_CMD_START_SCHED_SCAN);
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
kfree(rdev->sched_scan_req);
|
||||
out_err:
|
||||
rdev->sched_scan_req = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -8681,6 +8815,39 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev,
|
||||
const struct wiphy_wowlan_support *wowlan,
|
||||
struct nlattr *attr,
|
||||
struct cfg80211_wowlan *trig)
|
||||
{
|
||||
struct nlattr **tb;
|
||||
int err;
|
||||
|
||||
tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL);
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nla_parse(tb, NL80211_ATTR_MAX,
|
||||
nla_data(attr), nla_len(attr),
|
||||
nl80211_policy);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, NULL, tb);
|
||||
err = PTR_ERR_OR_ZERO(trig->nd_config);
|
||||
if (err)
|
||||
trig->nd_config = NULL;
|
||||
|
||||
out:
|
||||
kfree(tb);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
@ -8826,6 +8993,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
|
||||
err = nl80211_parse_wowlan_nd(
|
||||
rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
|
||||
&new_triggers);
|
||||
if (err)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
|
||||
if (!ntrig) {
|
||||
err = -ENOMEM;
|
||||
@ -9658,6 +9833,98 @@ static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_tdls_channel_switch(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_chan_def chandef = {};
|
||||
const u8 *addr;
|
||||
u8 oper_class;
|
||||
int err;
|
||||
|
||||
if (!rdev->ops->tdls_channel_switch ||
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (dev->ieee80211_ptr->iftype) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_MAC] ||
|
||||
!info->attrs[NL80211_ATTR_OPER_CLASS])
|
||||
return -EINVAL;
|
||||
|
||||
err = nl80211_parse_chandef(rdev, info, &chandef);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012
|
||||
* section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the
|
||||
* specification is not defined for them.
|
||||
*/
|
||||
if (chandef.chan->band == IEEE80211_BAND_2GHZ &&
|
||||
chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
|
||||
chandef.width != NL80211_CHAN_WIDTH_20)
|
||||
return -EINVAL;
|
||||
|
||||
/* we will be active on the TDLS link */
|
||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, wdev->iftype))
|
||||
return -EINVAL;
|
||||
|
||||
/* don't allow switching to DFS channels */
|
||||
if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype))
|
||||
return -EINVAL;
|
||||
|
||||
addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
||||
oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]);
|
||||
|
||||
wdev_lock(wdev);
|
||||
err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
const u8 *addr;
|
||||
|
||||
if (!rdev->ops->tdls_channel_switch ||
|
||||
!rdev->ops->tdls_cancel_channel_switch ||
|
||||
!(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (dev->ieee80211_ptr->iftype) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
case NL80211_IFTYPE_P2P_CLIENT:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!info->attrs[NL80211_ATTR_MAC])
|
||||
return -EINVAL;
|
||||
|
||||
addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
||||
|
||||
wdev_lock(wdev);
|
||||
rdev_tdls_cancel_channel_switch(rdev, dev, addr);
|
||||
wdev_unlock(wdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define NL80211_FLAG_NEED_WIPHY 0x01
|
||||
#define NL80211_FLAG_NEED_NETDEV 0x02
|
||||
#define NL80211_FLAG_NEED_RTNL 0x04
|
||||
@ -10456,6 +10723,22 @@ static const struct genl_ops nl80211_ops[] = {
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
|
||||
.doit = nl80211_tdls_channel_switch,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
|
||||
.doit = nl80211_tdls_cancel_channel_switch,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
};
|
||||
|
||||
/* notification functions */
|
||||
@ -11653,7 +11936,9 @@ EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
|
||||
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
gfp_t gfp)
|
||||
gfp_t gfp,
|
||||
enum nl80211_commands notif,
|
||||
u8 count)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
@ -11662,7 +11947,7 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, notif);
|
||||
if (!hdr) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
@ -11674,6 +11959,10 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
|
||||
if (nl80211_send_chandef(msg, chandef))
|
||||
goto nla_put_failure;
|
||||
|
||||
if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) &&
|
||||
(nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count)))
|
||||
goto nla_put_failure;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
|
||||
@ -11696,18 +11985,28 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
|
||||
|
||||
trace_cfg80211_ch_switch_notify(dev, chandef);
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
||||
wdev->iftype != NL80211_IFTYPE_ADHOC &&
|
||||
wdev->iftype != NL80211_IFTYPE_MESH_POINT))
|
||||
return;
|
||||
|
||||
wdev->chandef = *chandef;
|
||||
wdev->preset_chandef = *chandef;
|
||||
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
|
||||
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
|
||||
NL80211_CMD_CH_SWITCH_NOTIFY, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
|
||||
|
||||
void cfg80211_ch_switch_started_notify(struct net_device *dev,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
u8 count)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
|
||||
trace_cfg80211_ch_switch_started_notify(dev, chandef);
|
||||
|
||||
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
|
||||
NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
|
||||
|
||||
void cfg80211_cqm_txe_notify(struct net_device *dev,
|
||||
const u8 *peer, u32 num_packets,
|
||||
u32 rate, u32 intvl, gfp_t gfp)
|
||||
@ -11944,6 +12243,67 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
|
||||
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int cfg80211_net_detect_results(struct sk_buff *msg,
|
||||
struct cfg80211_wowlan_wakeup *wakeup)
|
||||
{
|
||||
struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect;
|
||||
struct nlattr *nl_results, *nl_match, *nl_freqs;
|
||||
int i, j;
|
||||
|
||||
nl_results = nla_nest_start(
|
||||
msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS);
|
||||
if (!nl_results)
|
||||
return -EMSGSIZE;
|
||||
|
||||
for (i = 0; i < nd->n_matches; i++) {
|
||||
struct cfg80211_wowlan_nd_match *match = nd->matches[i];
|
||||
|
||||
nl_match = nla_nest_start(msg, i);
|
||||
if (!nl_match)
|
||||
break;
|
||||
|
||||
/* The SSID attribute is optional in nl80211, but for
|
||||
* simplicity reasons it's always present in the
|
||||
* cfg80211 structure. If a driver can't pass the
|
||||
* SSID, that needs to be changed. A zero length SSID
|
||||
* is still a valid SSID (wildcard), so it cannot be
|
||||
* used for this purpose.
|
||||
*/
|
||||
if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len,
|
||||
match->ssid.ssid)) {
|
||||
nla_nest_cancel(msg, nl_match);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (match->n_channels) {
|
||||
nl_freqs = nla_nest_start(
|
||||
msg, NL80211_ATTR_SCAN_FREQUENCIES);
|
||||
if (!nl_freqs) {
|
||||
nla_nest_cancel(msg, nl_match);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (j = 0; j < match->n_channels; j++) {
|
||||
if (nla_put_u32(msg,
|
||||
NL80211_ATTR_WIPHY_FREQ,
|
||||
match->channels[j])) {
|
||||
nla_nest_cancel(msg, nl_freqs);
|
||||
nla_nest_cancel(msg, nl_match);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
nla_nest_end(msg, nl_freqs);
|
||||
}
|
||||
|
||||
nla_nest_end(msg, nl_match);
|
||||
}
|
||||
|
||||
out:
|
||||
nla_nest_end(msg, nl_results);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
||||
struct cfg80211_wowlan_wakeup *wakeup,
|
||||
gfp_t gfp)
|
||||
@ -12038,6 +12398,10 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
|
||||
goto free_msg;
|
||||
}
|
||||
|
||||
if (wakeup->net_detect &&
|
||||
cfg80211_net_detect_results(msg, wakeup))
|
||||
goto free_msg;
|
||||
|
||||
nla_nest_end(msg, reasons);
|
||||
}
|
||||
|
||||
|
@ -993,4 +993,28 @@ rdev_del_tx_ts(struct cfg80211_registered_device *rdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_tdls_channel_switch(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *addr,
|
||||
u8 oper_class, struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
int ret;
|
||||
|
||||
trace_rdev_tdls_channel_switch(&rdev->wiphy, dev, addr, oper_class,
|
||||
chandef);
|
||||
ret = rdev->ops->tdls_channel_switch(&rdev->wiphy, dev, addr,
|
||||
oper_class, chandef);
|
||||
trace_rdev_return_int(&rdev->wiphy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *dev, const u8 *addr)
|
||||
{
|
||||
trace_rdev_tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
|
||||
rdev->ops->tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
|
||||
trace_rdev_return_void(&rdev->wiphy);
|
||||
}
|
||||
|
||||
#endif /* __CFG80211_RDEV_OPS */
|
||||
|
@ -573,8 +573,9 @@ static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy)
|
||||
return get_cfg80211_regdom();
|
||||
}
|
||||
|
||||
unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
|
||||
const struct ieee80211_reg_rule *rule)
|
||||
static unsigned int
|
||||
reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd,
|
||||
const struct ieee80211_reg_rule *rule)
|
||||
{
|
||||
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
|
||||
const struct ieee80211_freq_range *freq_range_tmp;
|
||||
@ -622,6 +623,27 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
|
||||
return end_freq - start_freq;
|
||||
}
|
||||
|
||||
unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
|
||||
const struct ieee80211_reg_rule *rule)
|
||||
{
|
||||
unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule);
|
||||
|
||||
if (rule->flags & NL80211_RRF_NO_160MHZ)
|
||||
bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80));
|
||||
if (rule->flags & NL80211_RRF_NO_80MHZ)
|
||||
bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40));
|
||||
|
||||
/*
|
||||
* HT40+/HT40- limits are handled per-channel. Only limit BW if both
|
||||
* are not allowed.
|
||||
*/
|
||||
if (rule->flags & NL80211_RRF_NO_HT40MINUS &&
|
||||
rule->flags & NL80211_RRF_NO_HT40PLUS)
|
||||
bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20));
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
/* Sanity check on a regulatory rule */
|
||||
static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
|
||||
{
|
||||
@ -946,6 +968,16 @@ static u32 map_regdom_flags(u32 rd_flags)
|
||||
channel_flags |= IEEE80211_CHAN_NO_OFDM;
|
||||
if (rd_flags & NL80211_RRF_NO_OUTDOOR)
|
||||
channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;
|
||||
if (rd_flags & NL80211_RRF_GO_CONCURRENT)
|
||||
channel_flags |= IEEE80211_CHAN_GO_CONCURRENT;
|
||||
if (rd_flags & NL80211_RRF_NO_HT40MINUS)
|
||||
channel_flags |= IEEE80211_CHAN_NO_HT40MINUS;
|
||||
if (rd_flags & NL80211_RRF_NO_HT40PLUS)
|
||||
channel_flags |= IEEE80211_CHAN_NO_HT40PLUS;
|
||||
if (rd_flags & NL80211_RRF_NO_80MHZ)
|
||||
channel_flags |= IEEE80211_CHAN_NO_80MHZ;
|
||||
if (rd_flags & NL80211_RRF_NO_160MHZ)
|
||||
channel_flags |= IEEE80211_CHAN_NO_160MHZ;
|
||||
return channel_flags;
|
||||
}
|
||||
|
||||
@ -1565,10 +1597,23 @@ static void handle_channel_custom(struct wiphy *wiphy,
|
||||
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
|
||||
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
|
||||
|
||||
chan->dfs_state_entered = jiffies;
|
||||
chan->dfs_state = NL80211_DFS_USABLE;
|
||||
|
||||
chan->beacon_found = false;
|
||||
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
|
||||
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
|
||||
chan->max_reg_power = chan->max_power =
|
||||
(int) MBM_TO_DBM(power_rule->max_eirp);
|
||||
|
||||
if (chan->flags & IEEE80211_CHAN_RADAR) {
|
||||
if (reg_rule->dfs_cac_ms)
|
||||
chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
|
||||
else
|
||||
chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
|
||||
}
|
||||
|
||||
chan->max_power = chan->max_reg_power;
|
||||
}
|
||||
|
||||
static void handle_band_custom(struct wiphy *wiphy,
|
||||
|
@ -2032,6 +2032,48 @@ TRACE_EVENT(rdev_del_tx_ts,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_tdls_channel_switch,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
const u8 *addr, u8 oper_class,
|
||||
struct cfg80211_chan_def *chandef),
|
||||
TP_ARGS(wiphy, netdev, addr, oper_class, chandef),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
MAC_ENTRY(addr)
|
||||
__field(u8, oper_class)
|
||||
CHAN_DEF_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(addr, addr);
|
||||
CHAN_DEF_ASSIGN(chandef);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT
|
||||
" oper class %d, " CHAN_DEF_PR_FMT,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr),
|
||||
__entry->oper_class, CHAN_DEF_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(rdev_tdls_cancel_channel_switch,
|
||||
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
|
||||
const u8 *addr),
|
||||
TP_ARGS(wiphy, netdev, addr),
|
||||
TP_STRUCT__entry(
|
||||
WIPHY_ENTRY
|
||||
NETDEV_ENTRY
|
||||
MAC_ENTRY(addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
WIPHY_ASSIGN;
|
||||
NETDEV_ASSIGN;
|
||||
MAC_ASSIGN(addr, addr);
|
||||
),
|
||||
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
|
||||
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr))
|
||||
);
|
||||
|
||||
/*************************************************************
|
||||
* cfg80211 exported functions traces *
|
||||
*************************************************************/
|
||||
@ -2355,6 +2397,22 @@ TRACE_EVENT(cfg80211_ch_switch_notify,
|
||||
NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_ch_switch_started_notify,
|
||||
TP_PROTO(struct net_device *netdev,
|
||||
struct cfg80211_chan_def *chandef),
|
||||
TP_ARGS(netdev, chandef),
|
||||
TP_STRUCT__entry(
|
||||
NETDEV_ENTRY
|
||||
CHAN_DEF_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
NETDEV_ASSIGN;
|
||||
CHAN_DEF_ASSIGN(chandef);
|
||||
),
|
||||
TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
|
||||
NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfg80211_radar_event,
|
||||
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
|
||||
TP_ARGS(wiphy, chandef),
|
||||
|
Loading…
Reference in New Issue
Block a user