mac80211: add hardware restart function
[deliverable/linux.git] / net / mac80211 / util.c
index e0431a1d218b1a299c092560c6d07b4f6c181780..b361e2acfce9e23ce5998ca4e44f41b5acc6b19c 100644 (file)
@@ -28,6 +28,7 @@
 #include "rate.h"
 #include "mesh.h"
 #include "wme.h"
+#include "led.h"
 
 /* privid for wiphys to determine whether they belong to us or not */
 void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
@@ -166,18 +167,13 @@ int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
 
 void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx)
 {
-       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx->skb->data;
+       struct sk_buff *skb = tx->skb;
+       struct ieee80211_hdr *hdr;
 
-       hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-       if (tx->extra_frag) {
-               struct ieee80211_hdr *fhdr;
-               int i;
-               for (i = 0; i < tx->num_extra_frag; i++) {
-                       fhdr = (struct ieee80211_hdr *)
-                               tx->extra_frag[i]->data;
-                       fhdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
-               }
-       }
+       do {
+               hdr = (struct ieee80211_hdr *) skb->data;
+               hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+       } while ((skb = skb->next));
 }
 
 int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
@@ -344,42 +340,21 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       if (queue >= hw->queues) {
-               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
-                       return;
-
-               /*
-                * for virtual aggregation queues, we need to refcount the
-                * internal mac80211 disable (multiple times!), keep track of
-                * driver disable _and_ make sure the regular queue is
-                * actually enabled.
-                */
-               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
-                       local->amdpu_ac_stop_refcnt[queue - hw->queues]--;
-               else
-                       __clear_bit(reason, &local->queue_stop_reasons[queue]);
-
-               if (local->queue_stop_reasons[queue] ||
-                   local->amdpu_ac_stop_refcnt[queue - hw->queues])
-                       return;
-
-               /* now go on to treat the corresponding regular queue */
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
-       }
+       if (WARN_ON(queue >= hw->queues))
+               return;
 
        __clear_bit(reason, &local->queue_stop_reasons[queue]);
 
+       if (!skb_queue_empty(&local->pending[queue]) &&
+           local->queue_stop_reasons[queue] ==
+                               BIT(IEEE80211_QUEUE_STOP_REASON_PENDING))
+               tasklet_schedule(&local->tx_pending_tasklet);
+
        if (local->queue_stop_reasons[queue] != 0)
                /* someone still has this queue stopped */
                return;
 
-       if (test_bit(queue, local->queues_pending)) {
-               set_bit(queue, local->queues_pending_run);
-               tasklet_schedule(&local->tx_pending_tasklet);
-       } else {
-               netif_wake_subqueue(local->mdev, queue);
-       }
+       netif_wake_subqueue(local->mdev, queue);
 }
 
 void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -405,29 +380,18 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       if (queue >= hw->queues) {
-               if (local->ampdu_ac_queue[queue - hw->queues] < 0)
-                       return;
-
-               /*
-                * for virtual aggregation queues, we need to refcount the
-                * internal mac80211 disable (multiple times!), keep track of
-                * driver disable _and_ make sure the regular queue is
-                * actually enabled.
-                */
-               if (reason == IEEE80211_QUEUE_STOP_REASON_AGGREGATION)
-                       local->amdpu_ac_stop_refcnt[queue - hw->queues]++;
-               else
-                       __set_bit(reason, &local->queue_stop_reasons[queue]);
+       if (WARN_ON(queue >= hw->queues))
+               return;
 
-               /* now go on to treat the corresponding regular queue */
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               reason = IEEE80211_QUEUE_STOP_REASON_AGGREGATION;
-       }
+       /*
+        * Only stop if it was previously running, this is necessary
+        * for correct pending packets handling because there we may
+        * start (but not wake) the queue and rely on that.
+        */
+       if (!local->queue_stop_reasons[queue])
+               netif_stop_subqueue(local->mdev, queue);
 
        __set_bit(reason, &local->queue_stop_reasons[queue]);
-
-       netif_stop_subqueue(local->mdev, queue);
 }
 
 void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -473,15 +437,9 @@ EXPORT_SYMBOL(ieee80211_stop_queues);
 int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
 {
        struct ieee80211_local *local = hw_to_local(hw);
-       unsigned long flags;
 
-       if (queue >= hw->queues) {
-               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
-               queue = local->ampdu_ac_queue[queue - hw->queues];
-               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-               if (queue < 0)
-                       return true;
-       }
+       if (WARN_ON(queue >= hw->queues))
+               return true;
 
        return __netif_subqueue_stopped(local->mdev, queue);
 }
@@ -496,7 +454,7 @@ void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
 
        spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 
-       for (i = 0; i < hw->queues + hw->ampdu_queues; i++)
+       for (i = 0; i < hw->queues; i++)
                __ieee80211_wake_queue(hw, i, reason);
 
        spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
@@ -846,16 +804,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       const u8 *ie_auth = NULL;
-       int ie_auth_len = 0;
-
-       if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-               ie_auth_len = sdata->u.mgd.ie_auth_len;
-               ie_auth = sdata->u.mgd.ie_auth;
-       }
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                           sizeof(*mgmt) + 6 + extra_len + ie_auth_len);
+                           sizeof(*mgmt) + 6 + extra_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
                       "frame\n", sdata->dev->name);
@@ -877,34 +828,80 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        mgmt->u.auth.status_code = cpu_to_le16(0);
        if (extra)
                memcpy(skb_put(skb, extra_len), extra, extra_len);
-       if (ie_auth)
-               memcpy(skb_put(skb, ie_auth_len), ie_auth, ie_auth_len);
 
        ieee80211_tx_skb(sdata, skb, encrypt);
 }
 
+int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
+                            const u8 *ie, size_t ie_len)
+{
+       struct ieee80211_supported_band *sband;
+       u8 *pos, *supp_rates_len, *esupp_rates_len = NULL;
+       int i;
+
+       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+       pos = buffer;
+
+       *pos++ = WLAN_EID_SUPP_RATES;
+       supp_rates_len = pos;
+       *pos++ = 0;
+
+       for (i = 0; i < sband->n_bitrates; i++) {
+               struct ieee80211_rate *rate = &sband->bitrates[i];
+
+               if (esupp_rates_len) {
+                       *esupp_rates_len += 1;
+               } else if (*supp_rates_len == 8) {
+                       *pos++ = WLAN_EID_EXT_SUPP_RATES;
+                       esupp_rates_len = pos;
+                       *pos++ = 1;
+               } else
+                       *supp_rates_len += 1;
+
+               *pos++ = rate->bitrate / 5;
+       }
+
+       if (sband->ht_cap.ht_supported) {
+               __le16 tmp = cpu_to_le16(sband->ht_cap.cap);
+
+               *pos++ = WLAN_EID_HT_CAPABILITY;
+               *pos++ = sizeof(struct ieee80211_ht_cap);
+               memset(pos, 0, sizeof(struct ieee80211_ht_cap));
+               memcpy(pos, &tmp, sizeof(u16));
+               pos += sizeof(u16);
+               /* TODO: needs a define here for << 2 */
+               *pos++ = sband->ht_cap.ampdu_factor |
+                        (sband->ht_cap.ampdu_density << 2);
+               memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
+               pos += sizeof(sband->ht_cap.mcs);
+               pos += 2 + 4 + 1; /* ext info, BF cap, antsel */
+       }
+
+       /*
+        * If adding more here, adjust code in main.c
+        * that calculates local->scan_ies_len.
+        */
+
+       if (ie) {
+               memcpy(pos, ie, ie_len);
+               pos += ie_len;
+       }
+
+       return pos - buffer;
+}
+
 void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
-                             u8 *ssid, size_t ssid_len,
-                             u8 *ie, size_t ie_len)
+                             const u8 *ssid, size_t ssid_len,
+                             const u8 *ie, size_t ie_len)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_supported_band *sband;
        struct sk_buff *skb;
        struct ieee80211_mgmt *mgmt;
-       u8 *pos, *supp_rates, *esupp_rates = NULL, *extra_preq_ie = NULL;
-       int i, extra_preq_ie_len = 0;
-
-       switch (sdata->vif.type) {
-       case NL80211_IFTYPE_STATION:
-               extra_preq_ie_len = sdata->u.mgd.ie_probereq_len;
-               extra_preq_ie = sdata->u.mgd.ie_probereq;
-               break;
-       default:
-               break;
-       }
+       u8 *pos;
 
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
-                           ie_len + extra_preq_ie_len);
+                           ie_len);
        if (!skb) {
                printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
                       "request\n", sdata->dev->name);
@@ -928,34 +925,9 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
        *pos++ = WLAN_EID_SSID;
        *pos++ = ssid_len;
        memcpy(pos, ssid, ssid_len);
+       pos += ssid_len;
 
-       supp_rates = skb_put(skb, 2);
-       supp_rates[0] = WLAN_EID_SUPP_RATES;
-       supp_rates[1] = 0;
-       sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
-
-       for (i = 0; i < sband->n_bitrates; i++) {
-               struct ieee80211_rate *rate = &sband->bitrates[i];
-               if (esupp_rates) {
-                       pos = skb_put(skb, 1);
-                       esupp_rates[1]++;
-               } else if (supp_rates[1] == 8) {
-                       esupp_rates = skb_put(skb, 3);
-                       esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
-                       esupp_rates[1] = 1;
-                       pos = &esupp_rates[2];
-               } else {
-                       pos = skb_put(skb, 1);
-                       supp_rates[1]++;
-               }
-               *pos = rate->bitrate / 5;
-       }
-
-       if (ie)
-               memcpy(skb_put(skb, ie_len), ie, ie_len);
-       if (extra_preq_ie)
-               memcpy(skb_put(skb, extra_preq_ie_len), extra_preq_ie,
-                      extra_preq_ie_len);
+       skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len));
 
        ieee80211_tx_skb(sdata, skb, 0);
 }
@@ -995,3 +967,120 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
        }
        return supp_rates;
 }
+
+int ieee80211_reconfig(struct ieee80211_local *local)
+{
+       struct ieee80211_hw *hw = &local->hw;
+       struct ieee80211_sub_if_data *sdata;
+       struct ieee80211_if_init_conf conf;
+       struct sta_info *sta;
+       unsigned long flags;
+       int res;
+
+       /* restart hardware */
+       if (local->open_count) {
+               res = local->ops->start(hw);
+
+               ieee80211_led_radio(local, hw->conf.radio_enabled);
+       }
+
+       /* add interfaces */
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+                   sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+                   netif_running(sdata->dev)) {
+                       conf.vif = &sdata->vif;
+                       conf.type = sdata->vif.type;
+                       conf.mac_addr = sdata->dev->dev_addr;
+                       res = local->ops->add_interface(hw, &conf);
+               }
+       }
+
+       /* add STAs back */
+       if (local->ops->sta_notify) {
+               spin_lock_irqsave(&local->sta_lock, flags);
+               list_for_each_entry(sta, &local->sta_list, list) {
+                       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                               sdata = container_of(sdata->bss,
+                                            struct ieee80211_sub_if_data,
+                                            u.ap);
+
+                       local->ops->sta_notify(hw, &sdata->vif,
+                               STA_NOTIFY_ADD, &sta->sta);
+               }
+               spin_unlock_irqrestore(&local->sta_lock, flags);
+       }
+
+       /* Clear Suspend state so that ADDBA requests can be processed */
+
+       rcu_read_lock();
+
+       if (hw->flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+               list_for_each_entry_rcu(sta, &local->sta_list, list) {
+                       clear_sta_flags(sta, WLAN_STA_SUSPEND);
+               }
+       }
+
+       rcu_read_unlock();
+
+       /* setup RTS threshold */
+       if (local->ops->set_rts_threshold)
+               local->ops->set_rts_threshold(hw, local->rts_threshold);
+
+       /* reconfigure hardware */
+       ieee80211_hw_config(local, ~0);
+
+       netif_addr_lock_bh(local->mdev);
+       ieee80211_configure_filter(local);
+       netif_addr_unlock_bh(local->mdev);
+
+       /* Finally also reconfigure all the BSS information */
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               u32 changed = ~0;
+               if (!netif_running(sdata->dev))
+                       continue;
+               switch (sdata->vif.type) {
+               case NL80211_IFTYPE_STATION:
+                       /* disable beacon change bits */
+                       changed &= ~IEEE80211_IFCC_BEACON;
+                       /* fall through */
+               case NL80211_IFTYPE_ADHOC:
+               case NL80211_IFTYPE_AP:
+               case NL80211_IFTYPE_MESH_POINT:
+                       /*
+                        * Driver's config_interface can fail if rfkill is
+                        * enabled. Accommodate this return code.
+                        * FIXME: When mac80211 has knowledge of rfkill
+                        * state the code below can change back to:
+                        *   WARN(ieee80211_if_config(sdata, changed));
+                        *   ieee80211_bss_info_change_notify(sdata, ~0);
+                        */
+                       if (ieee80211_if_config(sdata, changed))
+                               printk(KERN_DEBUG "%s: failed to configure interface during resume\n",
+                                      sdata->dev->name);
+                       else
+                               ieee80211_bss_info_change_notify(sdata, ~0);
+                       break;
+               case NL80211_IFTYPE_WDS:
+                       break;
+               case NL80211_IFTYPE_AP_VLAN:
+               case NL80211_IFTYPE_MONITOR:
+                       /* ignore virtual */
+                       break;
+               case NL80211_IFTYPE_UNSPECIFIED:
+               case __NL80211_IFTYPE_AFTER_LAST:
+                       WARN_ON(1);
+                       break;
+               }
+       }
+
+       /* add back keys */
+       list_for_each_entry(sdata, &local->interfaces, list)
+               if (netif_running(sdata->dev))
+                       ieee80211_enable_keys(sdata);
+
+       ieee80211_wake_queues_by_reason(hw,
+                       IEEE80211_QUEUE_STOP_REASON_SUSPEND);
+
+       return 0;
+}
This page took 0.032146 seconds and 5 git commands to generate.