mac80211: allow station add/remove to sleep
[pandora-kernel.git] / net / mac80211 / tx.c
index 055b45b..cbe53ed 100644 (file)
@@ -180,6 +180,71 @@ static int inline is_ieee80211_device(struct ieee80211_local *local,
 }
 
 /* tx handlers */
+static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx)
+{
+       struct ieee80211_local *local = tx->local;
+       struct ieee80211_if_managed *ifmgd;
+
+       /* driver doesn't support power save */
+       if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
+               return TX_CONTINUE;
+
+       /* hardware does dynamic power save */
+       if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+               return TX_CONTINUE;
+
+       /* dynamic power save disabled */
+       if (local->hw.conf.dynamic_ps_timeout <= 0)
+               return TX_CONTINUE;
+
+       /* we are scanning, don't enable power save */
+       if (local->scanning)
+               return TX_CONTINUE;
+
+       if (!local->ps_sdata)
+               return TX_CONTINUE;
+
+       /* No point if we're going to suspend */
+       if (local->quiescing)
+               return TX_CONTINUE;
+
+       /* dynamic ps is supported only in managed mode */
+       if (tx->sdata->vif.type != NL80211_IFTYPE_STATION)
+               return TX_CONTINUE;
+
+       ifmgd = &tx->sdata->u.mgd;
+
+       /*
+        * Don't wakeup from power save if u-apsd is enabled, voip ac has
+        * u-apsd enabled and the frame is in voip class. This effectively
+        * means that even if all access categories have u-apsd enabled, in
+        * practise u-apsd is only used with the voip ac. This is a
+        * workaround for the case when received voip class packets do not
+        * have correct qos tag for some reason, due the network or the
+        * peer application.
+        *
+        * Note: local->uapsd_queues access is racy here. If the value is
+        * changed via debugfs, user needs to reassociate manually to have
+        * everything in sync.
+        */
+       if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED)
+           && (local->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
+           && skb_get_queue_mapping(tx->skb) == 0)
+               return TX_CONTINUE;
+
+       if (local->hw.conf.flags & IEEE80211_CONF_PS) {
+               ieee80211_stop_queues_by_reason(&local->hw,
+                                               IEEE80211_QUEUE_STOP_REASON_PS);
+               ieee80211_queue_work(&local->hw,
+                                    &local->dynamic_ps_disable_work);
+       }
+
+       mod_timer(&local->dynamic_ps_timer, jiffies +
+                 msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
+
+       return TX_CONTINUE;
+}
 
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
@@ -464,6 +529,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                tx->key = NULL;
 
        if (tx->key) {
+               bool skip_hw = false;
+
                tx->key->tx_rx_count++;
                /* TODO: add threshold stuff again */
 
@@ -480,16 +547,32 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                            !ieee80211_use_mfp(hdr->frame_control, tx->sta,
                                               tx->skb))
                                tx->key = NULL;
+                       else
+                               skip_hw = (tx->key->conf.flags &
+                                          IEEE80211_KEY_FLAG_SW_MGMT) &&
+                                       ieee80211_is_mgmt(hdr->frame_control);
                        break;
                case ALG_AES_CMAC:
                        if (!ieee80211_is_mgmt(hdr->frame_control))
                                tx->key = NULL;
                        break;
                }
+
+               if (!skip_hw && tx->key &&
+                   tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)
+                       info->control.hw_key = &tx->key->conf;
        }
 
-       if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-               info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+       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;
 }
@@ -668,17 +751,6 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
        return TX_CONTINUE;
 }
 
-static ieee80211_tx_result debug_noinline
-ieee80211_tx_h_misc(struct ieee80211_tx_data *tx)
-{
-       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
-
-       if (tx->sta)
-               info->control.sta = &tx->sta->sta;
-
-       return TX_CONTINUE;
-}
-
 static ieee80211_tx_result debug_noinline
 ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
 {
@@ -938,7 +1010,8 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
                (struct ieee80211_radiotap_header *) skb->data;
        struct ieee80211_supported_band *sband;
        struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-       int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
+       int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len,
+                                                  NULL);
 
        sband = tx->local->hw.wiphy->bands[tx->channel->band];
 
@@ -974,7 +1047,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
                                 * because it will be recomputed and added
                                 * on transmission
                                 */
-                               if (skb->len < (iterator.max_length + FCS_LEN))
+                               if (skb->len < (iterator._max_length + FCS_LEN))
                                        return false;
 
                                skb_trim(skb, skb->len - FCS_LEN);
@@ -1001,10 +1074,10 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
 
        /*
         * remove the radiotap header
-        * iterator->max_length was sanity-checked against
+        * iterator->_max_length was sanity-checked against
         * skb->len by iterator init
         */
-       skb_pull(skb, iterator.max_length);
+       skb_pull(skb, iterator._max_length);
 
        return true;
 }
@@ -1036,7 +1109,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
        tx->flags |= IEEE80211_TX_FRAGMENTED;
 
        /* process and remove the injection radiotap header */
-       if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) {
+       if (unlikely(info->flags & IEEE80211_TX_INTFL_HAS_RADIOTAP)) {
                if (!__ieee80211_parse_tx_radiotap(tx, skb))
                        return TX_DROP;
 
@@ -1045,6 +1118,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
                 * the radiotap header that was present and pre-filled
                 * 'tx' with tx control information.
                 */
+               info->flags &= ~IEEE80211_TX_INTFL_HAS_RADIOTAP;
        }
 
        /*
@@ -1056,8 +1130,13 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
 
        hdr = (struct ieee80211_hdr *) skb->data;
 
-       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
                tx->sta = rcu_dereference(sdata->u.vlan.sta);
+               if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr)
+                       return TX_DROP;
+       } else if (info->flags & IEEE80211_TX_CTL_INJECTED) {
+               tx->sta = sta_info_get_bss(sdata, hdr->addr1);
+       }
        if (!tx->sta)
                tx->sta = sta_info_get(sdata, hdr->addr1);
 
@@ -1211,6 +1290,7 @@ static int __ieee80211_tx(struct ieee80211_local *local,
 static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
 {
        struct sk_buff *skb = tx->skb;
+       struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
        ieee80211_tx_result res = TX_DROP;
 
 #define CALL_TXH(txh) \
@@ -1220,13 +1300,18 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
                        goto txh_done;  \
        } while (0)
 
+       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_select_key);
-       CALL_TXH(ieee80211_tx_h_michael_mic_add);
+       CALL_TXH(ieee80211_tx_h_sta);
        if (!(tx->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL))
                CALL_TXH(ieee80211_tx_h_rate_ctrl);
-       CALL_TXH(ieee80211_tx_h_misc);
+
+       if (unlikely(info->flags & IEEE80211_TX_INTFL_RETRANSMISSION))
+               goto txh_done;
+
+       CALL_TXH(ieee80211_tx_h_michael_mic_add);
        CALL_TXH(ieee80211_tx_h_sequence);
        CALL_TXH(ieee80211_tx_h_fragment);
        /* handlers after fragment must be aware of tx info fragmentation! */
@@ -1402,34 +1487,6 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,
        return 0;
 }
 
-static bool need_dynamic_ps(struct ieee80211_local *local)
-{
-       /* driver doesn't support power save */
-       if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
-               return false;
-
-       /* hardware does dynamic power save */
-       if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
-               return false;
-
-       /* dynamic power save disabled */
-       if (local->hw.conf.dynamic_ps_timeout <= 0)
-               return false;
-
-       /* we are scanning, don't enable power save */
-       if (local->scanning)
-               return false;
-
-       if (!local->ps_sdata)
-               return false;
-
-       /* No point if we're going to suspend */
-       if (local->quiescing)
-               return false;
-
-       return true;
-}
-
 static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
                           struct sk_buff *skb)
 {
@@ -1440,25 +1497,14 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
        int headroom;
        bool may_encrypt;
 
-       if (need_dynamic_ps(local)) {
-               if (local->hw.conf.flags & IEEE80211_CONF_PS) {
-                       ieee80211_stop_queues_by_reason(&local->hw,
-                                       IEEE80211_QUEUE_STOP_REASON_PS);
-                       ieee80211_queue_work(&local->hw,
-                                       &local->dynamic_ps_disable_work);
-               }
-
-               mod_timer(&local->dynamic_ps_timer, jiffies +
-                       msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
-       }
-
        rcu_read_lock();
 
        if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) {
                int hdrlen;
                u16 len_rthdr;
 
-               info->flags |= IEEE80211_TX_CTL_INJECTED;
+               info->flags |= IEEE80211_TX_CTL_INJECTED |
+                              IEEE80211_TX_INTFL_HAS_RADIOTAP;
 
                len_rthdr = ieee80211_get_radiotap_len(skb->data);
                hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
@@ -2278,6 +2324,56 @@ 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 *ssid, size_t ssid_len,
+                                      const u8 *ie, size_t ie_len)
+{
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_local *local;
+       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) +
+                           ie_ssid_len + ie_len);
+       if (!skb) {
+               printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
+                      "request template\n", sdata->name);
+               return NULL;
+       }
+
+       skb_reserve(skb, local->hw.extra_tx_headroom);
+
+       hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
+       memset(hdr, 0, sizeof(*hdr));
+       hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+                                        IEEE80211_STYPE_PROBE_REQ);
+       memset(hdr->addr1, 0xff, ETH_ALEN);
+       memcpy(hdr->addr2, vif->addr, ETH_ALEN);
+       memset(hdr->addr3, 0xff, ETH_ALEN);
+
+       pos = skb_put(skb, ie_ssid_len);
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = ssid_len;
+       if (ssid)
+               memcpy(pos, ssid, ssid_len);
+       pos += ssid_len;
+
+       if (ie) {
+               pos = skb_put(skb, ie_len);
+               memcpy(pos, ie, ie_len);
+       }
+
+       return skb;
+}
+EXPORT_SYMBOL(ieee80211_probereq_get);
+
 void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                       const void *frame, size_t frame_len,
                       const struct ieee80211_tx_info *frame_txctl,