mac80211: implement scan supported rates
[cascardo/linux.git] / net / mac80211 / mlme.c
index 64d92d5..fee706d 100644 (file)
@@ -90,20 +90,11 @@ enum rx_mgmt_action {
        /* no action required */
        RX_MGMT_NONE,
 
-       /* caller must call cfg80211_send_rx_auth() */
-       RX_MGMT_CFG80211_AUTH,
-
-       /* caller must call cfg80211_send_rx_assoc() */
-       RX_MGMT_CFG80211_ASSOC,
-
        /* caller must call cfg80211_send_deauth() */
        RX_MGMT_CFG80211_DEAUTH,
 
        /* caller must call cfg80211_send_disassoc() */
        RX_MGMT_CFG80211_DISASSOC,
-
-       /* caller must tell cfg80211 about internal error */
-       RX_MGMT_CFG80211_ASSOC_ERROR,
 };
 
 /* utils */
@@ -758,27 +749,61 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                container_of(work, struct ieee80211_local,
                             dynamic_ps_enable_work);
        struct ieee80211_sub_if_data *sdata = local->ps_sdata;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_if_managed *ifmgd;
+       unsigned long flags;
+       int q;
 
        /* can only happen when PS was just disabled anyway */
        if (!sdata)
                return;
 
+       ifmgd = &sdata->u.mgd;
+
        if (local->hw.conf.flags & IEEE80211_CONF_PS)
                return;
 
+       if (!local->disable_dynamic_ps &&
+           local->hw.conf.dynamic_ps_timeout > 0) {
+               /* don't enter PS if TX frames are pending */
+               if (drv_tx_frames_pending(local)) {
+                       mod_timer(&local->dynamic_ps_timer, jiffies +
+                                 msecs_to_jiffies(
+                                 local->hw.conf.dynamic_ps_timeout));
+                       return;
+               }
+
+               /*
+                * transmission can be stopped by others which leads to
+                * dynamic_ps_timer expiry. Postpone the ps timer if it
+                * is not the actual idle state.
+                */
+               spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+               for (q = 0; q < local->hw.queues; q++) {
+                       if (local->queue_stop_reasons[q]) {
+                               spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+                                                      flags);
+                               mod_timer(&local->dynamic_ps_timer, jiffies +
+                                         msecs_to_jiffies(
+                                         local->hw.conf.dynamic_ps_timeout));
+                               return;
+                       }
+               }
+               spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+       }
+
        if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
            (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) {
                netif_tx_stop_all_queues(sdata->dev);
-               /*
-                * Flush all the frames queued in the driver before
-                * going to power save
-                */
-               drv_flush(local, false);
-               ieee80211_send_nullfunc(local, sdata, 1);
 
-               /* Flush once again to get the tx status of nullfunc frame */
-               drv_flush(local, false);
+               if (drv_tx_frames_pending(local))
+                       mod_timer(&local->dynamic_ps_timer, jiffies +
+                                 msecs_to_jiffies(
+                                 local->hw.conf.dynamic_ps_timeout));
+               else {
+                       ieee80211_send_nullfunc(local, sdata, 1);
+                       /* Flush to get the tx status of nullfunc frame */
+                       drv_flush(local, false);
+               }
        }
 
        if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
@@ -789,7 +814,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
                ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
        }
 
-       netif_tx_start_all_queues(sdata->dev);
+       if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
+               netif_tx_wake_all_queues(sdata->dev);
 }
 
 void ieee80211_dynamic_ps_timer(unsigned long data)
@@ -891,6 +917,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                            params.aifs, params.cw_min, params.cw_max,
                            params.txop, params.uapsd);
 #endif
+               local->tx_conf[queue] = params;
                if (drv_conf_tx(local, queue, &params))
                        wiphy_debug(local->hw.wiphy,
                                    "failed to set TX queue parameters for queue %d\n",
@@ -1077,6 +1104,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
                local->hw.conf.flags &= ~IEEE80211_CONF_PS;
                config_changed |= IEEE80211_CONF_CHANGE_PS;
        }
+       local->ps_sdata = NULL;
 
        ieee80211_hw_config(local, config_changed);
 
@@ -1191,7 +1219,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
                ieee80211_send_nullfunc(sdata->local, sdata, 0);
        } else {
                ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
-               ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0);
+               ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
+                                        (u32) -1, true);
        }
 
        ifmgd->probe_send_count++;
@@ -1276,7 +1305,8 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
 
        ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
        skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
-                                       ssid + 2, ssid[1], NULL, 0);
+                                       (u32) -1, ssid + 2, ssid[1],
+                                       NULL, 0, true);
 
        return skb;
 }
@@ -1735,6 +1765,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                ifmgd->ave_beacon_signal = rx_status->signal * 16;
                ifmgd->last_cqm_event_signal = 0;
                ifmgd->count_beacon_signal = 1;
+               ifmgd->last_ave_beacon_signal = 0;
        } else {
                ifmgd->ave_beacon_signal =
                        (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
@@ -1742,6 +1773,28 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                         ifmgd->ave_beacon_signal) / 16;
                ifmgd->count_beacon_signal++;
        }
+
+       if (ifmgd->rssi_min_thold != ifmgd->rssi_max_thold &&
+           ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) {
+               int sig = ifmgd->ave_beacon_signal;
+               int last_sig = ifmgd->last_ave_beacon_signal;
+
+               /*
+                * if signal crosses either of the boundaries, invoke callback
+                * with appropriate parameters
+                */
+               if (sig > ifmgd->rssi_max_thold &&
+                   (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) {
+                       ifmgd->last_ave_beacon_signal = sig;
+                       drv_rssi_callback(local, RSSI_EVENT_HIGH);
+               } else if (sig < ifmgd->rssi_min_thold &&
+                          (last_sig >= ifmgd->rssi_max_thold ||
+                          last_sig == 0)) {
+                       ifmgd->last_ave_beacon_signal = sig;
+                       drv_rssi_callback(local, RSSI_EVENT_LOW);
+               }
+       }
+
        if (bss_conf->cqm_rssi_thold &&
            ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT &&
            !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
@@ -2001,7 +2054,7 @@ static void ieee80211_sta_timer(unsigned long data)
 }
 
 static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
-                                         u8 *bssid)
+                                         u8 *bssid, u8 reason)
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -2019,8 +2072,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
         * but that's not a problem.
         */
        ieee80211_send_deauth_disassoc(sdata, bssid,
-                       IEEE80211_STYPE_DEAUTH,
-                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+                       IEEE80211_STYPE_DEAUTH, reason,
                        NULL, true);
        mutex_lock(&ifmgd->mtx);
 }
@@ -2066,7 +2118,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                                            " AP %pM, disconnecting.\n",
                                            sdata->name, bssid);
 #endif
-                               ieee80211_sta_connection_lost(sdata, bssid);
+                               ieee80211_sta_connection_lost(sdata, bssid,
+                                       WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
                        }
                } else if (time_is_after_jiffies(ifmgd->probe_timeout))
                        run_again(ifmgd, ifmgd->probe_timeout);
@@ -2078,7 +2131,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                                    sdata->name,
                                    bssid, probe_wait_ms);
 #endif
-                       ieee80211_sta_connection_lost(sdata, bssid);
+                       ieee80211_sta_connection_lost(sdata, bssid,
+                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
                } else if (ifmgd->probe_send_count < max_tries) {
 #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
                        wiphy_debug(local->hw.wiphy,
@@ -2100,7 +2154,8 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
                                    sdata->name,
                                    bssid, probe_wait_ms);
 
-                       ieee80211_sta_connection_lost(sdata, bssid);
+                       ieee80211_sta_connection_lost(sdata, bssid,
+                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
                }
        }
 
@@ -2187,12 +2242,34 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
 
+       if (!ifmgd->associated)
+               return;
+
+       if (sdata->flags & IEEE80211_SDATA_DISCONNECT_RESUME) {
+               sdata->flags &= ~IEEE80211_SDATA_DISCONNECT_RESUME;
+               mutex_lock(&ifmgd->mtx);
+               if (ifmgd->associated) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+                       wiphy_debug(sdata->local->hw.wiphy,
+                                   "%s: driver requested disconnect after resume.\n",
+                                   sdata->name);
+#endif
+                       ieee80211_sta_connection_lost(sdata,
+                               ifmgd->associated->bssid,
+                               WLAN_REASON_UNSPECIFIED);
+                       mutex_unlock(&ifmgd->mtx);
+                       return;
+               }
+               mutex_unlock(&ifmgd->mtx);
+       }
+
        if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running))
                add_timer(&ifmgd->timer);
        if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running))
                add_timer(&ifmgd->chswitch_timer);
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_restart_sta_timer(sdata);
+       ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work);
 }
 #endif
 
@@ -2639,3 +2716,10 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
        cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
 }
 EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+unsigned char ieee80211_get_operstate(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       return sdata->dev->operstate;
+}
+EXPORT_SYMBOL(ieee80211_get_operstate);