mac80211: let drivers wake but not start queues
[deliverable/linux.git] / net / mac80211 / main.c
index 9ad4e3631b6bd4b5ac012f53457abcaa41e2ec47..9761d9bd5a79338006600a2a43863a53155da8a6 100644 (file)
@@ -35,8 +35,6 @@
 #include "debugfs.h"
 #include "debugfs_netdev.h"
 
-#define SUPP_MCS_SET_LEN 16
-
 /*
  * For seeing transmitted packets on monitor interfaces
  * we have a radiotap header too.
@@ -112,7 +110,13 @@ static int ieee80211_master_open(struct net_device *dev)
                        break;
                }
        }
-       return res;
+
+       if (res)
+               return res;
+
+       netif_start_queue(local->mdev);
+
+       return 0;
 }
 
 static int ieee80211_master_stop(struct net_device *dev)
@@ -346,6 +350,7 @@ static int ieee80211_open(struct net_device *dev)
                        goto err_del_interface;
                }
 
+               /* no locking required since STA is not live yet */
                sta->flags |= WLAN_STA_AUTHORIZED;
 
                res = sta_info_insert(sta);
@@ -385,8 +390,8 @@ static int ieee80211_open(struct net_device *dev)
         * yet be effective. Trigger execution of ieee80211_sta_work
         * to fix this.
         */
-       if(sdata->vif.type == IEEE80211_IF_TYPE_STA ||
-          sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+       if (sdata->vif.type == IEEE80211_IF_TYPE_STA ||
+           sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
                struct ieee80211_if_sta *ifsta = &sdata->u.sta;
                queue_work(local->hw.workqueue, &ifsta->work);
        }
@@ -588,7 +593,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                return -ENOENT;
        }
 
-       spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_lock_bh(&sta->lock);
 
        /* we have tried too many times, receiver does not want A-MPDU */
        if (sta->ampdu_mlme.addba_req_num[tid] > HT_AGG_MAX_RETRIES) {
@@ -691,7 +696,7 @@ start_ba_err:
        spin_unlock_bh(&local->mdev->queue_lock);
        ret = -EBUSY;
 start_ba_exit:
-       spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_unlock_bh(&sta->lock);
        rcu_read_unlock();
        return ret;
 }
@@ -719,7 +724,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
 
        /* check if the TID is in aggregation */
        state = &sta->ampdu_mlme.tid_state_tx[tid];
-       spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_lock_bh(&sta->lock);
 
        if (*state != HT_AGG_STATE_OPERATIONAL) {
                ret = -ENOENT;
@@ -749,7 +754,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
        }
 
 stop_BA_exit:
-       spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_unlock_bh(&sta->lock);
        rcu_read_unlock();
        return ret;
 }
@@ -778,12 +783,12 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
        }
 
        state = &sta->ampdu_mlme.tid_state_tx[tid];
-       spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_lock_bh(&sta->lock);
 
        if (!(*state & HT_ADDBA_REQUESTED_MSK)) {
                printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
                                *state);
-               spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+               spin_unlock_bh(&sta->lock);
                rcu_read_unlock();
                return;
        }
@@ -796,7 +801,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
                printk(KERN_DEBUG "Aggregation is on for tid %d \n", tid);
                ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
        }
-       spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_unlock_bh(&sta->lock);
        rcu_read_unlock();
 }
 EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
@@ -830,10 +835,10 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
        }
        state = &sta->ampdu_mlme.tid_state_tx[tid];
 
-       spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_lock_bh(&sta->lock);
        if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
                printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
-               spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+               spin_unlock_bh(&sta->lock);
                rcu_read_unlock();
                return;
        }
@@ -860,7 +865,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
        sta->ampdu_mlme.addba_req_num[tid] = 0;
        kfree(sta->ampdu_mlme.tid_tx[tid]);
        sta->ampdu_mlme.tid_tx[tid] = NULL;
-       spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
+       spin_unlock_bh(&sta->lock);
 
        rcu_read_unlock();
 }
@@ -1067,56 +1072,84 @@ u32 ieee80211_handle_ht(struct ieee80211_local *local, int enable_ht,
        struct ieee80211_supported_band *sband;
        struct ieee80211_ht_info ht_conf;
        struct ieee80211_ht_bss_info ht_bss_conf;
-       int i;
        u32 changed = 0;
+       int i;
+       u8 max_tx_streams = IEEE80211_HT_CAP_MAX_STREAMS;
+       u8 tx_mcs_set_cap;
 
        sband = local->hw.wiphy->bands[conf->channel->band];
 
+       memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info));
+       memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info));
+
        /* HT is not supported */
        if (!sband->ht_info.ht_supported) {
                conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
-               return 0;
+               goto out;
        }
 
-       memset(&ht_conf, 0, sizeof(struct ieee80211_ht_info));
-       memset(&ht_bss_conf, 0, sizeof(struct ieee80211_ht_bss_info));
-
-       if (enable_ht) {
-               if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE))
+       /* disable HT */
+       if (!enable_ht) {
+               if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)
                        changed |= BSS_CHANGED_HT;
+               conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
+               conf->ht_conf.ht_supported = 0;
+               goto out;
+       }
 
-               conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE;
-               ht_conf.ht_supported = 1;
 
-               ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
-               ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS);
-               ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS;
+       if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE))
+               changed |= BSS_CHANGED_HT;
 
-               for (i = 0; i < SUPP_MCS_SET_LEN; i++)
-                       ht_conf.supp_mcs_set[i] =
-                                       sband->ht_info.supp_mcs_set[i] &
-                                       req_ht_cap->supp_mcs_set[i];
+       conf->flags |= IEEE80211_CONF_SUPPORT_HT_MODE;
+       ht_conf.ht_supported = 1;
 
-               ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
-               ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
-               ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
+       ht_conf.cap = req_ht_cap->cap & sband->ht_info.cap;
+       ht_conf.cap &= ~(IEEE80211_HT_CAP_MIMO_PS);
+       ht_conf.cap |= sband->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS;
+       ht_bss_conf.primary_channel = req_bss_cap->primary_channel;
+       ht_bss_conf.bss_cap = req_bss_cap->bss_cap;
+       ht_bss_conf.bss_op_mode = req_bss_cap->bss_op_mode;
 
-               ht_conf.ampdu_factor = req_ht_cap->ampdu_factor;
-               ht_conf.ampdu_density = req_ht_cap->ampdu_density;
+       ht_conf.ampdu_factor = req_ht_cap->ampdu_factor;
+       ht_conf.ampdu_density = req_ht_cap->ampdu_density;
 
-               /* if bss configuration changed store the new one */
-               if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) ||
-                   memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) {
-                       changed |= BSS_CHANGED_HT;
-                       memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf));
-                       memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf));
-               }
-       } else {
-               if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)
-                       changed |= BSS_CHANGED_HT;
-               conf->flags &= ~IEEE80211_CONF_SUPPORT_HT_MODE;
-       }
+       /* Bits 96-100 */
+       tx_mcs_set_cap = sband->ht_info.supp_mcs_set[12];
+
+       /* configure suppoerted Tx MCS according to requested MCS
+        * (based in most cases on Rx capabilities of peer) and self
+        * Tx MCS capabilities (as defined by low level driver HW
+        * Tx capabilities) */
+       if (!(tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_DEFINED))
+               goto check_changed;
+
+       /* Counting from 0 therfore + 1 */
+       if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_RX_DIFF)
+               max_tx_streams = ((tx_mcs_set_cap &
+                               IEEE80211_HT_CAP_MCS_TX_STREAMS) >> 2) + 1;
 
+       for (i = 0; i < max_tx_streams; i++)
+               ht_conf.supp_mcs_set[i] =
+                       sband->ht_info.supp_mcs_set[i] &
+                                       req_ht_cap->supp_mcs_set[i];
+
+       if (tx_mcs_set_cap & IEEE80211_HT_CAP_MCS_TX_UEQM)
+               for (i = IEEE80211_SUPP_MCS_SET_UEQM;
+                    i < IEEE80211_SUPP_MCS_SET_LEN; i++)
+                       ht_conf.supp_mcs_set[i] =
+                               sband->ht_info.supp_mcs_set[i] &
+                                       req_ht_cap->supp_mcs_set[i];
+
+check_changed:
+       /* if bss configuration changed store the new one */
+       if (memcmp(&conf->ht_conf, &ht_conf, sizeof(ht_conf)) ||
+           memcmp(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf))) {
+               changed |= BSS_CHANGED_HT;
+               memcpy(&conf->ht_conf, &ht_conf, sizeof(ht_conf));
+               memcpy(&conf->ht_bss_conf, &ht_bss_conf, sizeof(ht_bss_conf));
+       }
+out:
        return changed;
 }
 
@@ -1315,7 +1348,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
         * packet. If the STA went to power save mode, this will happen
         * happen when it wakes up for the next time.
         */
-       sta->flags |= WLAN_STA_CLEAR_PS_FILT;
+       set_sta_flags(sta, WLAN_STA_CLEAR_PS_FILT);
 
        /*
         * This code races in the following way:
@@ -1347,7 +1380,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
         *      can be unknown, for example with different interrupt status
         *      bits.
         */
-       if (sta->flags & WLAN_STA_PS &&
+       if (test_sta_flags(sta, WLAN_STA_PS) &&
            skb_queue_len(&sta->tx_filtered) < STA_MAX_TX_BUFFER) {
                ieee80211_remove_tx_extra(local, sta->key, skb,
                                          &status->control);
@@ -1355,7 +1388,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
                return;
        }
 
-       if (!(sta->flags & WLAN_STA_PS) &&
+       if (!test_sta_flags(sta, WLAN_STA_PS) &&
            !(status->control.flags & IEEE80211_TXCTL_REQUEUE)) {
                /* Software retry the packet once */
                status->control.flags |= IEEE80211_TXCTL_REQUEUE;
@@ -1370,7 +1403,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
                       "queue_len=%d PS=%d @%lu\n",
                       wiphy_name(local->hw.wiphy),
                       skb_queue_len(&sta->tx_filtered),
-                      !!(sta->flags & WLAN_STA_PS), jiffies);
+                      !!test_sta_flags(sta, WLAN_STA_PS), jiffies);
        dev_kfree_skb(skb);
 }
 
@@ -1399,7 +1432,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                struct sta_info *sta;
                sta = sta_info_get(local, hdr->addr1);
                if (sta) {
-                       if (sta->flags & WLAN_STA_PS) {
+                       if (test_sta_flags(sta, WLAN_STA_PS)) {
                                /*
                                 * The STA is in power save mode, so assume
                                 * that this TX packet failed because of that.
@@ -1482,7 +1515,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
                return;
        }
 
-       rthdr = (struct ieee80211_tx_status_rtap_hdr*)
+       rthdr = (struct ieee80211_tx_status_rtap_hdr *)
                                skb_push(skb, sizeof(*rthdr));
 
        memset(rthdr, 0, sizeof(*rthdr));
@@ -1701,13 +1734,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        local->hw.conf.beacon_int = 1000;
 
-       local->wstats_flags |= local->hw.max_rssi ?
-                              IW_QUAL_LEVEL_UPDATED : IW_QUAL_LEVEL_INVALID;
-       local->wstats_flags |= local->hw.max_signal ?
+       local->wstats_flags |= local->hw.flags & (IEEE80211_HW_SIGNAL_UNSPEC |
+                                                 IEEE80211_HW_SIGNAL_DB |
+                                                 IEEE80211_HW_SIGNAL_DBM) ?
                               IW_QUAL_QUAL_UPDATED : IW_QUAL_QUAL_INVALID;
-       local->wstats_flags |= local->hw.max_noise ?
+       local->wstats_flags |= local->hw.flags & IEEE80211_HW_NOISE_DBM ?
                               IW_QUAL_NOISE_UPDATED : IW_QUAL_NOISE_INVALID;
-       if (local->hw.max_rssi < 0 || local->hw.max_noise < 0)
+       if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
                local->wstats_flags |= IW_QUAL_DBM;
 
        result = sta_info_start(local);
@@ -1745,6 +1778,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_wep;
        }
 
+       if (hw->queues > IEEE80211_MAX_QUEUES)
+               hw->queues = IEEE80211_MAX_QUEUES;
+       if (hw->ampdu_queues > IEEE80211_MAX_AMPDU_QUEUES)
+               hw->ampdu_queues = IEEE80211_MAX_AMPDU_QUEUES;
+
        ieee80211_install_qdisc(local->mdev);
 
        /* add one default STA interface */
@@ -1766,6 +1804,7 @@ fail_wep:
 fail_rate:
        ieee80211_debugfs_remove_netdev(IEEE80211_DEV_TO_SUB_IF(local->mdev));
        unregister_netdevice(local->mdev);
+       local->mdev = NULL;
 fail_dev:
        rtnl_unlock();
        sta_info_stop(local);
@@ -1773,8 +1812,10 @@ fail_sta_info:
        debugfs_hw_del(local);
        destroy_workqueue(local->hw.workqueue);
 fail_workqueue:
-       ieee80211_if_free(local->mdev);
-       local->mdev = NULL;
+       if (local->mdev != NULL) {
+               ieee80211_if_free(local->mdev);
+               local->mdev = NULL;
+       }
 fail_mdev_alloc:
        wiphy_unregister(local->hw.wiphy);
        return result;
This page took 0.028396 seconds and 5 git commands to generate.