Merge remote-tracking branch 'net-next/master' into mac80211-next
[cascardo/linux.git] / net / mac80211 / cfg.c
index ff347a0..1d01190 100644 (file)
@@ -2,7 +2,7 @@
  * mac80211 configuration hooks for cfg80211
  *
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2013-2014  Intel Mobile Communications GmbH
+ * Copyright 2013-2015  Intel Mobile Communications GmbH
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -137,6 +137,9 @@ static int ieee80211_set_noack_map(struct wiphy *wiphy,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
        sdata->noack_map = noack_map;
+
+       ieee80211_check_fast_xmit_iface(sdata);
+
        return 0;
 }
 
@@ -309,6 +312,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
        u32 iv32;
        u16 iv16;
        int err = -ENOENT;
+       struct ieee80211_key_seq kseq = {};
 
        sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 
@@ -339,10 +343,12 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                iv32 = key->u.tkip.tx.iv32;
                iv16 = key->u.tkip.tx.iv16;
 
-               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
-                       drv_get_tkip_seq(sdata->local,
-                                        key->conf.hw_key_idx,
-                                        &iv32, &iv16);
+               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
+                   !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
+                       drv_get_key_seq(sdata->local, key, &kseq);
+                       iv32 = kseq.tkip.iv32;
+                       iv16 = kseq.tkip.iv16;
+               }
 
                seq[0] = iv16 & 0xff;
                seq[1] = (iv16 >> 8) & 0xff;
@@ -355,52 +361,44 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_CCMP_256:
-               pn64 = atomic64_read(&key->u.ccmp.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
        case WLAN_CIPHER_SUITE_BIP_CMAC_256:
-               pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), aes_cmac));
        case WLAN_CIPHER_SUITE_BIP_GMAC_128:
        case WLAN_CIPHER_SUITE_BIP_GMAC_256:
-               pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
-               params.seq = seq;
-               params.seq_len = 6;
-               break;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), aes_gmac));
        case WLAN_CIPHER_SUITE_GCMP:
        case WLAN_CIPHER_SUITE_GCMP_256:
-               pn64 = atomic64_read(&key->u.gcmp.tx_pn);
-               seq[0] = pn64;
-               seq[1] = pn64 >> 8;
-               seq[2] = pn64 >> 16;
-               seq[3] = pn64 >> 24;
-               seq[4] = pn64 >> 32;
-               seq[5] = pn64 >> 40;
+               BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
+                            offsetof(typeof(kseq), gcmp));
+
+               if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
+                   !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
+                       drv_get_key_seq(sdata->local, key, &kseq);
+                       memcpy(seq, kseq.ccmp.pn, 6);
+               } else {
+                       pn64 = atomic64_read(&key->conf.tx_pn);
+                       seq[0] = pn64;
+                       seq[1] = pn64 >> 8;
+                       seq[2] = pn64 >> 16;
+                       seq[3] = pn64 >> 24;
+                       seq[4] = pn64 >> 32;
+                       seq[5] = pn64 >> 40;
+               }
                params.seq = seq;
                params.seq_len = 6;
                break;
+       default:
+               if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
+                       break;
+               if (WARN_ON(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
+                       break;
+               drv_get_key_seq(sdata->local, key, &kseq);
+               params.seq = kseq.hw.seq;
+               params.seq_len = kseq.hw.seq_len;
+               break;
        }
 
        params.key = key->conf.key;
@@ -1372,6 +1370,7 @@ static int ieee80211_change_station(struct wiphy *wiphy,
                }
 
                sta->sdata = vlansdata;
+               ieee80211_check_fast_xmit(sta);
 
                if (sta->sta_state == IEEE80211_STA_AUTHORIZED &&
                    prev_4addr != new_4addr) {
@@ -2099,10 +2098,14 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
        int err;
 
        if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
+               ieee80211_check_fast_xmit_all(local);
+
                err = drv_set_frag_threshold(local, wiphy->frag_threshold);
 
-               if (err)
+               if (err) {
+                       ieee80211_check_fast_xmit_all(local);
                        return err;
+               }
        }
 
        if ((changed & WIPHY_PARAM_COVERAGE_CLASS) ||
@@ -2514,6 +2517,19 @@ static bool ieee80211_coalesce_started_roc(struct ieee80211_local *local,
        return true;
 }
 
+static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
+{
+       lockdep_assert_held(&local->mtx);
+
+       local->roc_cookie_counter++;
+
+       /* wow, you wrapped 64 bits ... more likely a bug */
+       if (WARN_ON(local->roc_cookie_counter == 0))
+               local->roc_cookie_counter++;
+
+       return local->roc_cookie_counter;
+}
+
 static int ieee80211_start_roc_work(struct ieee80211_local *local,
                                    struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_channel *channel,
@@ -2551,7 +2567,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        roc->req_duration = duration;
        roc->frame = txskb;
        roc->type = type;
-       roc->mgmt_tx_cookie = (unsigned long)txskb;
        roc->sdata = sdata;
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
        INIT_LIST_HEAD(&roc->dependents);
@@ -2561,17 +2576,10 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
         * or the SKB (for mgmt TX)
         */
        if (!txskb) {
-               /* local->mtx protects this */
-               local->roc_cookie_counter++;
-               roc->cookie = local->roc_cookie_counter;
-               /* wow, you wrapped 64 bits ... more likely a bug */
-               if (WARN_ON(roc->cookie == 0)) {
-                       roc->cookie = 1;
-                       local->roc_cookie_counter++;
-               }
+               roc->cookie = ieee80211_mgmt_tx_cookie(local);
                *cookie = roc->cookie;
        } else {
-               *cookie = (unsigned long)txskb;
+               roc->mgmt_tx_cookie = *cookie;
        }
 
        /* if there's one pending or we're scanning, queue this one */
@@ -3244,13 +3252,43 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
        return err;
 }
 
+static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
+                                             struct sk_buff *skb, u64 *cookie,
+                                             gfp_t gfp)
+{
+       unsigned long spin_flags;
+       struct sk_buff *ack_skb;
+       int id;
+
+       ack_skb = skb_copy(skb, gfp);
+       if (!ack_skb)
+               return ERR_PTR(-ENOMEM);
+
+       spin_lock_irqsave(&local->ack_status_lock, spin_flags);
+       id = idr_alloc(&local->ack_status_frames, ack_skb,
+                      1, 0x10000, GFP_ATOMIC);
+       spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
+
+       if (id < 0) {
+               kfree_skb(ack_skb);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       IEEE80211_SKB_CB(skb)->ack_frame_id = id;
+
+       *cookie = ieee80211_mgmt_tx_cookie(local);
+       IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
+
+       return ack_skb;
+}
+
 static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct cfg80211_mgmt_tx_params *params,
                             u64 *cookie)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = sdata->local;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *ack_skb;
        struct sta_info *sta;
        const struct ieee80211_mgmt *mgmt = (void *)params->buf;
        bool need_offchan = false;
@@ -3299,8 +3337,14 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                break;
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_P2P_CLIENT:
-               if (!sdata->u.mgd.associated)
+               sdata_lock(sdata);
+               if (!sdata->u.mgd.associated ||
+                   (params->offchan && params->wait &&
+                    local->ops->remain_on_channel &&
+                    memcmp(sdata->u.mgd.associated->bssid,
+                           mgmt->bssid, ETH_ALEN)))
                        need_offchan = true;
+               sdata_unlock(sdata);
                break;
        case NL80211_IFTYPE_P2P_DEVICE:
                need_offchan = true;
@@ -3356,6 +3400,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
        /* Update CSA counters */
        if (sdata->vif.csa_active &&
            (sdata->vif.type == NL80211_IFTYPE_AP ||
+            sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
             sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            params->n_csa_offsets) {
                int i;
@@ -3382,8 +3427,23 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 
        skb->dev = sdata->dev;
 
+       if (!params->dont_wait_for_ack) {
+               /* make a copy to preserve the frame contents
+                * in case of encryption.
+                */
+               ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
+                                                GFP_KERNEL);
+               if (IS_ERR(ack_skb)) {
+                       ret = PTR_ERR(ack_skb);
+                       kfree_skb(skb);
+                       goto out_unlock;
+               }
+       } else {
+               /* for cookie below */
+               ack_skb = skb;
+       }
+
        if (!need_offchan) {
-               *cookie = (unsigned long) skb;
                ieee80211_tx_skb(sdata, skb);
                ret = 0;
                goto out_unlock;
@@ -3476,7 +3536,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_qos_hdr *nullfunc;
-       struct sk_buff *skb;
+       struct sk_buff *skb, *ack_skb;
        int size = sizeof(*nullfunc);
        __le16 fc;
        bool qos;
@@ -3484,20 +3544,24 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum ieee80211_band band;
+       int ret;
+
+       /* the lock is needed to assign the cookie later */
+       mutex_lock(&local->mtx);
 
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
        if (WARN_ON(!chanctx_conf)) {
-               rcu_read_unlock();
-               return -EINVAL;
+               ret = -EINVAL;
+               goto unlock;
        }
        band = chanctx_conf->def.chan->band;
        sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = sta->sta.wme;
        } else {
-               rcu_read_unlock();
-               return -ENOLINK;
+               ret = -ENOLINK;
+               goto unlock;
        }
 
        if (qos) {
@@ -3513,8 +3577,8 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
        if (!skb) {
-               rcu_read_unlock();
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto unlock;
        }
 
        skb->dev = dev;
@@ -3540,13 +3604,23 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
        if (qos)
                nullfunc->qos_ctrl = cpu_to_le16(7);
 
+       ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
+       if (IS_ERR(ack_skb)) {
+               kfree_skb(skb);
+               ret = PTR_ERR(ack_skb);
+               goto unlock;
+       }
+
        local_bh_disable();
        ieee80211_xmit(sdata, sta, skb);
        local_bh_enable();
+
+       ret = 0;
+unlock:
        rcu_read_unlock();
+       mutex_unlock(&local->mtx);
 
-       *cookie = (unsigned long) skb;
-       return 0;
+       return ret;
 }
 
 static int ieee80211_cfg_get_channel(struct wiphy *wiphy,