Merge tag 'mac80211-next-for-john-2014-11-20' of git://git.kernel.org/pub/scm/linux...
authorJohn W. Linville <linville@tuxdriver.com>
Thu, 20 Nov 2014 21:09:30 +0000 (16:09 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 20 Nov 2014 21:09:30 +0000 (16:09 -0500)
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>
48 files changed:
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath9k/channel.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/wcn36xx/main.c
drivers/net/wireless/b43/main.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/cw1200/scan.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mac80211_hwsim.h
drivers/net/wireless/mwl8k.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/staging/vt6656/main_usb.c
include/linux/ieee80211.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/debugfs_sta.c
net/mac80211/driver-ops.h
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/rate.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tdls.c
net/mac80211/trace.h
net/mac80211/tx.c
net/mac80211/util.c
net/mac80211/vht.c
net/mac80211/wme.c
net/wireless/core.c
net/wireless/core.h
net/wireless/nl80211.c
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/trace.h

index ab2709a..19eab2a 100644 (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 ?
index 794d520..2066650 100644 (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;
index c7d12ef..92d5a6c 100644 (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);
index f95283f..7c63976 100644 (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);
index b71d2b3..267c35d 100644 (file)
@@ -494,7 +494,9 @@ out:
        return ret;
 }
 
-static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
+static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif,
+                                 const u8 *mac_addr)
 {
        struct wcn36xx *wcn = hw->priv;
 
@@ -502,7 +504,8 @@ static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw)
        wcn36xx_smd_start_scan(wcn);
 }
 
-static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw)
+static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif)
 {
        struct wcn36xx *wcn = hw->priv;
 
index 5d4173e..47731cb 100644 (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;
index 43c71bf..f95b524 100644 (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);
index b2fb6c6..f2e276f 100644 (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;
index 77fbf30..2371d11 100644 (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)
index f08debd..66e1c73 100644 (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)
index ef11044..b8d1e04 100644 (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;
index d13f25c..1ff81af 100644 (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,
index ad6e5a8..cb40245 100644 (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);
index 07dae0d..af52f0b 100644 (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));
index 3823485..0b30a7b 100644 (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;
index dd2e448..b826619 100644 (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;
index 2fbff90..dbc311c 100644 (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;
 
index f65b544..4f4eea8 100644 (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))
index 5c3acd0..bb748c4 100644 (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
  *
index 5f203a6..cff3a26 100644 (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
  *
index 9b3025e..d775245 100644 (file)
  * @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
  * @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,
 };
 
 /**
index 9242c60..a360c15 100644 (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;
index 0618594..e75d5c5 100644 (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
index bafe489..94c7009 100644 (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);
 }
index 9759dd1..2ebc9ea 100644 (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 */
index 842e066..cc6e964 100644 (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;
 
index 9df26ad..538fe4e 100644 (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;
index 282a4f3..6ab99da 100644 (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;
index 0d166e7..ba06cd0 100644 (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);
 }
index 6081329..08ab7d6 100644 (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);
index c50fd94..62ff7cf 100644 (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);
 }
 
index a726bb1..49c23bd 100644 (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);
index af0d094..ae84267 100644 (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 @@ out:
        if (ret) {
                /* Clean in case of failure after HW restart or upon resume. */
                RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
-               local->sched_scan_req = NULL;
+               RCU_INIT_POINTER(local->sched_scan_req, NULL);
        }
 
        return ret;
@@ -1090,7 +1137,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
        }
 
        /* We don't want to restart sched scan anymore. */
-       local->sched_scan_req = NULL;
+       RCU_INIT_POINTER(local->sched_scan_req, NULL);
 
        if (rcu_access_pointer(local->sched_scan_sdata)) {
                ret = drv_sched_scan_stop(local, sdata);
@@ -1125,7 +1172,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local)
        RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
 
        /* If sched scan was aborted by the driver. */
-       local->sched_scan_req = NULL;
+       RCU_INIT_POINTER(local->sched_scan_req, NULL);
 
        mutex_unlock(&local->mtx);
 
index adc2537..a42f5b2 100644 (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();
 }
 
index bcda2ac..4f052bb 100644 (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;
 };
index 9612d89..71de2d3 100644 (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,
index b4f368e..55ddd77 100644 (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;
+       }
+}
index 809a498..85ccfbe 100644 (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
index 3ffd91f..66ddbbe 100644 (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
+ *
+ * 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.
  *
- * Returns: NETDEV_TX_OK both on success and on failure. On failure skb will
- *     be freed.
+ * 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.
  *
- * 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).
+ * 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);
-       rcu_read_unlock();
+       return skb;
+ free:
+       kfree_skb(skb);
+       return ERR_PTR(ret);
+}
 
-       return NETDEV_TX_OK;
+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;
 
- fail_rcu:
+       ieee80211_xmit(sdata, skb);
+ out:
        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();
 }
index f9319a5..bb9664c 100644 (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);
 
index 671ce0d..bc9e8fc 100644 (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;
index fdf52db..9eb0aee 100644 (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:
index a4d2792..4c2e501 100644 (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 ||
index 61ee664..faa5b16 100644 (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
 }
index 1a31736..6e41777 100644 (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(info->attrs[NL80211_ATTR_IE]))
-               return -EINVAL;
+       if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
+               return ERR_PTR(-EINVAL);
 
-       if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
-               return -EINVAL;
+       if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+               return ERR_PTR(-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;
-
-       wiphy = &rdev->wiphy;
+               return ERR_PTR(-EINVAL);
 
-       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);
        }
 
index 1b3864c..35cfb71 100644 (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 */
index b725a31..32d8310 100644 (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,
index 277a85d..ad38910 100644 (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),