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:
John W. Linville 2014-11-20 16:09:30 -05:00
commit 9a638ddfb0
48 changed files with 2952 additions and 385 deletions

View File

@ -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 ?

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -494,7 +494,9 @@ static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
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;

View File

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

View File

@ -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);

View File

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

View File

@ -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, &param);
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)

View File

@ -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)

View File

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

View File

@ -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,

View File

@ -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);

View File

@ -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));

View File

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

View File

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

View File

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

View File

@ -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))

View File

@ -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
*

View File

@ -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
*

View File

@ -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,
};
/**

View File

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

View File

@ -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, &params->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

View File

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

View File

@ -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 */

View File

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

View File

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

View File

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

View File

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

View File

@ -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);

View File

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

View File

@ -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);

View File

@ -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 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
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);

View File

@ -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();
}

View File

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

View File

@ -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,

View File

@ -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, &params.ch_sw_tm_ie);
if (!params.tmpl_skb) {
ret = -ENOENT;
goto out;
}
call_drv:
drv_tdls_recv_channel_switch(sdata->local, sdata, &params);
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,
&params.ch_sw_tm_ie);
if (!params.tmpl_skb) {
ret = -ENOENT;
goto out;
}
drv_tdls_recv_channel_switch(sdata->local, sdata, &params);
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;
}
}

View File

@ -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

View File

@ -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();
}

View File

@ -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);

View File

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

View File

@ -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:

View File

@ -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 ||

View File

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

View File

@ -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, &params);
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);
}

View File

@ -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 */

View File

@ -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,

View File

@ -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),