cfg80211/mac80211: allow per-station GTKs
[pandora-kernel.git] / net / mac80211 / tx.c
index 680bcb7..96c5943 100644 (file)
@@ -273,6 +273,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
                 */
                return TX_DROP;
 
+       if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
+               return TX_CONTINUE;
+
        if (tx->sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
                return TX_CONTINUE;
 
@@ -351,8 +354,8 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
 
        local->total_ps_buffered = total;
 #ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
-       printk(KERN_DEBUG "%s: PS buffers full - purged %d frames\n",
-              wiphy_name(local->hw.wiphy), purged);
+       wiphy_debug(local->hw.wiphy, "PS buffers full - purged %d frames\n",
+                   purged);
 #endif
 }
 
@@ -508,6 +511,18 @@ ieee80211_tx_h_ps_buf(struct ieee80211_tx_data *tx)
                return ieee80211_tx_h_multicast_ps_buf(tx);
 }
 
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_check_control_port_protocol(struct ieee80211_tx_data *tx)
+{
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
+       if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol &&
+                    tx->sdata->control_port_no_encrypt))
+               info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+       return TX_CONTINUE;
+}
+
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
 {
@@ -517,7 +532,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
 
        if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
                tx->key = NULL;
-       else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
+       else if (tx->sta && (key = rcu_dereference(tx->sta->ptk)))
                tx->key = key;
        else if (ieee80211_is_mgmt(hdr->frame_control) &&
                 is_multicast_ether_addr(hdr->addr1) &&
@@ -527,7 +542,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
        else if ((key = rcu_dereference(tx->sdata->default_key)))
                tx->key = key;
        else if (tx->sdata->drop_unencrypted &&
-                (tx->skb->protocol != cpu_to_be16(ETH_P_PAE)) &&
+                (tx->skb->protocol != tx->sdata->control_port_protocol) &&
                 !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
                 (!ieee80211_is_robust_mgmt_frame(hdr) ||
                  (ieee80211_is_action(hdr->frame_control) &&
@@ -543,15 +558,16 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                tx->key->tx_rx_count++;
                /* TODO: add threshold stuff again */
 
-               switch (tx->key->conf.alg) {
-               case ALG_WEP:
+               switch (tx->key->conf.cipher) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
                        if (ieee80211_is_auth(hdr->frame_control))
                                break;
-               case ALG_TKIP:
+               case WLAN_CIPHER_SUITE_TKIP:
                        if (!ieee80211_is_data_present(hdr->frame_control))
                                tx->key = NULL;
                        break;
-               case ALG_CCMP:
+               case WLAN_CIPHER_SUITE_CCMP:
                        if (!ieee80211_is_data_present(hdr->frame_control) &&
                            !ieee80211_use_mfp(hdr->frame_control, tx->sta,
                                               tx->skb))
@@ -561,7 +577,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                                           IEEE80211_KEY_FLAG_SW_MGMT) &&
                                        ieee80211_is_mgmt(hdr->frame_control);
                        break;
-               case ALG_AES_CMAC:
+               case WLAN_CIPHER_SUITE_AES_CMAC:
                        if (!ieee80211_is_mgmt(hdr->frame_control))
                                tx->key = NULL;
                        break;
@@ -575,17 +591,6 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
        return TX_CONTINUE;
 }
 
-static ieee80211_tx_result debug_noinline
-ieee80211_tx_h_sta(struct ieee80211_tx_data *tx)
-{
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-
-       if (tx->sta && tx->sta->uploaded)
-               info->control.sta = &tx->sta->sta;
-
-       return TX_CONTINUE;
-}
-
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
 {
@@ -957,22 +962,31 @@ ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
 {
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
+
        if (!tx->key)
                return TX_CONTINUE;
 
-       switch (tx->key->conf.alg) {
-       case ALG_WEP:
+       switch (tx->key->conf.cipher) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
                return ieee80211_crypto_wep_encrypt(tx);
-       case ALG_TKIP:
+       case WLAN_CIPHER_SUITE_TKIP:
                return ieee80211_crypto_tkip_encrypt(tx);
-       case ALG_CCMP:
+       case WLAN_CIPHER_SUITE_CCMP:
                return ieee80211_crypto_ccmp_encrypt(tx);
-       case ALG_AES_CMAC:
+       case WLAN_CIPHER_SUITE_AES_CMAC:
                return ieee80211_crypto_aes_cmac_encrypt(tx);
+       default:
+               /* handle hw-only algorithm */
+               if (info->control.hw_key) {
+                       ieee80211_tx_set_protected(tx);
+                       return TX_CONTINUE;
+               }
+               break;
+
        }
 
-       /* not reached */
-       WARN_ON(1);
        return TX_DROP;
 }
 
@@ -1092,6 +1106,59 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
        return true;
 }
 
+static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
+                                 struct sk_buff *skb,
+                                 struct ieee80211_tx_info *info,
+                                 struct tid_ampdu_tx *tid_tx,
+                                 int tid)
+{
+       bool queued = false;
+
+       if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
+               info->flags |= IEEE80211_TX_CTL_AMPDU;
+       } else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
+               /*
+                * nothing -- this aggregation session is being started
+                * but that might still fail with the driver
+                */
+       } else {
+               spin_lock(&tx->sta->lock);
+               /*
+                * Need to re-check now, because we may get here
+                *
+                *  1) in the window during which the setup is actually
+                *     already done, but not marked yet because not all
+                *     packets are spliced over to the driver pending
+                *     queue yet -- if this happened we acquire the lock
+                *     either before or after the splice happens, but
+                *     need to recheck which of these cases happened.
+                *
+                *  2) during session teardown, if the OPERATIONAL bit
+                *     was cleared due to the teardown but the pointer
+                *     hasn't been assigned NULL yet (or we loaded it
+                *     before it was assigned) -- in this case it may
+                *     now be NULL which means we should just let the
+                *     packet pass through because splicing the frames
+                *     back is already done.
+                */
+               tid_tx = tx->sta->ampdu_mlme.tid_tx[tid];
+
+               if (!tid_tx) {
+                       /* do nothing, let packet pass through */
+               } else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
+                       info->flags |= IEEE80211_TX_CTL_AMPDU;
+               } else {
+                       queued = true;
+                       info->control.vif = &tx->sdata->vif;
+                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+                       __skb_queue_tail(&tid_tx->pending, skb);
+               }
+               spin_unlock(&tx->sta->lock);
+       }
+
+       return queued;
+}
+
 /*
  * initialises @tx
  */
@@ -1104,8 +1171,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_hdr *hdr;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        int hdrlen, tid;
-       u8 *qc, *state;
-       bool queued = false;
+       u8 *qc;
 
        memset(tx, 0, sizeof(*tx));
        tx->skb = skb;
@@ -1157,35 +1223,16 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
                qc = ieee80211_get_qos_ctl(hdr);
                tid = *qc & IEEE80211_QOS_CTL_TID_MASK;
 
-               spin_lock(&tx->sta->lock);
-               /*
-                * XXX: This spinlock could be fairly expensive, but see the
-                *      comment in agg-tx.c:ieee80211_agg_tx_operational().
-                *      One way to solve this would be to do something RCU-like
-                *      for managing the tid_tx struct and using atomic bitops
-                *      for the actual state -- by introducing an actual
-                *      'operational' bit that would be possible. It would
-                *      require changing ieee80211_agg_tx_operational() to
-                *      set that bit, and changing the way tid_tx is managed
-                *      everywhere, including races between that bit and
-                *      tid_tx going away (tid_tx being added can be easily
-                *      committed to memory before the 'operational' bit).
-                */
-               tid_tx = tx->sta->ampdu_mlme.tid_tx[tid];
-               state = &tx->sta->ampdu_mlme.tid_state_tx[tid];
-               if (*state == HT_AGG_STATE_OPERATIONAL) {
-                       info->flags |= IEEE80211_TX_CTL_AMPDU;
-               } else if (*state != HT_AGG_STATE_IDLE) {
-                       /* in progress */
-                       queued = true;
-                       info->control.vif = &sdata->vif;
-                       info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
-                       __skb_queue_tail(&tid_tx->pending, skb);
-               }
-               spin_unlock(&tx->sta->lock);
+               tid_tx = rcu_dereference(tx->sta->ampdu_mlme.tid_tx[tid]);
+               if (tid_tx) {
+                       bool queued;
+
+                       queued = ieee80211_tx_prep_agg(tx, skb, info,
+                                                      tid_tx, tid);
 
-               if (unlikely(queued))
-                       return TX_QUEUED;
+                       if (unlikely(queued))
+                               return TX_QUEUED;
+               }
        }
 
        if (is_multicast_ether_addr(hdr->addr1)) {
@@ -1274,6 +1321,11 @@ static int __ieee80211_tx(struct ieee80211_local *local,
                        break;
                }
 
+               if (sta && sta->uploaded)
+                       info->control.sta = &sta->sta;
+               else
+                       info->control.sta = NULL;
+
                ret = drv_tx(local, skb);
                if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
                        dev_kfree_skb(skb);
@@ -1312,8 +1364,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
        CALL_TXH(ieee80211_tx_h_dynamic_ps);
        CALL_TXH(ieee80211_tx_h_check_assoc);
        CALL_TXH(ieee80211_tx_h_ps_buf);
+       CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
        CALL_TXH(ieee80211_tx_h_select_key);
-       CALL_TXH(ieee80211_tx_h_sta);
        if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
                CALL_TXH(ieee80211_tx_h_rate_ctrl);
 
@@ -1485,8 +1537,8 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,
                I802_DEBUG_INC(local->tx_expand_skb_head);
 
        if (pskb_expand_head(skb, head_need, tail_need, GFP_ATOMIC)) {
-               printk(KERN_DEBUG "%s: failed to reallocate TX buffer\n",
-                      wiphy_name(local->hw.wiphy));
+               wiphy_debug(local->hw.wiphy,
+                           "failed to reallocate TX buffer\n");
                return -ENOMEM;
        }
 
@@ -1560,6 +1612,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
                return;
        }
 
+       hdr = (struct ieee80211_hdr *) skb->data;
        info->control.vif = &sdata->vif;
 
        if (ieee80211_vif_is_mesh(&sdata->vif) &&
@@ -1673,7 +1726,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        u16 ethertype, hdrlen,  meshhdrlen = 0;
        __le16 fc;
        struct ieee80211_hdr hdr;
-       struct ieee80211s_hdr mesh_hdr;
+       struct ieee80211s_hdr mesh_hdr __maybe_unused;
        const u8 *encaps_data;
        int encaps_len, skip_header_bytes;
        int nh_pos, h_pos;
@@ -1790,7 +1843,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
 #endif
        case NL80211_IFTYPE_STATION:
                memcpy(hdr.addr1, sdata->u.mgd.bssid, ETH_ALEN);
-               if (sdata->u.mgd.use_4addr && ethertype != ETH_P_PAE) {
+               if (sdata->u.mgd.use_4addr &&
+                   cpu_to_be16(ethertype) != sdata->control_port_protocol) {
                        fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
                        /* RA TA DA SA */
                        memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
@@ -1843,7 +1897,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
        if (!ieee80211_vif_is_mesh(&sdata->vif) &&
                unlikely(!is_multicast_ether_addr(hdr.addr1) &&
                      !(sta_flags & WLAN_STA_AUTHORIZED) &&
-                     !(ethertype == ETH_P_PAE &&
+                     !(cpu_to_be16(ethertype) == sdata->control_port_protocol &&
                       compare_ether_addr(sdata->vif.addr,
                                          skb->data + ETH_ALEN) == 0))) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@@ -1909,11 +1963,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
                h_pos += encaps_len;
        }
 
+#ifdef CONFIG_MAC80211_MESH
        if (meshhdrlen > 0) {
                memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen);
                nh_pos += meshhdrlen;
                h_pos += meshhdrlen;
        }
+#endif
 
        if (ieee80211_is_data_qos(fc)) {
                __le16 *qos_control;
@@ -2040,8 +2096,7 @@ void ieee80211_tx_pending(unsigned long data)
 
                if (skb_queue_empty(&local->pending[i]))
                        list_for_each_entry_rcu(sdata, &local->interfaces, list)
-                               netif_tx_wake_queue(
-                                       netdev_get_tx_queue(sdata->dev, i));
+                               netif_wake_subqueue(sdata->dev, i);
        }
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);