Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[pandora-kernel.git] / net / mac80211 / mlme.c
index 41812a1..34e0650 100644 (file)
@@ -177,7 +177,8 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
                sta = sta_info_get(sdata, bssid);
                if (sta)
                        rate_control_rate_update(local, sband, sta,
-                                                IEEE80211_RC_HT_CHANGED);
+                                                IEEE80211_RC_HT_CHANGED,
+                                                local->oper_channel_type);
                rcu_read_unlock();
         }
 
@@ -435,10 +436,12 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
                if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
                        ieee80211_send_nullfunc(local, sdata, 1);
 
-               if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
-                       conf->flags |= IEEE80211_CONF_PS;
-                       ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
-               }
+               if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
+                   (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
+                       return;
+
+               conf->flags |= IEEE80211_CONF_PS;
+               ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
        }
 }
 
@@ -557,7 +560,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work)
            (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)))
                ieee80211_send_nullfunc(local, sdata, 1);
 
-       if (!(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) ||
+       if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) &&
+             (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) ||
            (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) {
                ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
                local->hw.conf.flags |= IEEE80211_CONF_PS;
@@ -749,6 +753,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
        /* And the BSSID changed - we're associated now */
        bss_info_changed |= BSS_CHANGED_BSSID;
 
+       /* Tell the driver to monitor connection quality (if supported) */
+       if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
+           sdata->vif.bss_conf.cqm_rssi_thold)
+               bss_info_changed |= BSS_CHANGED_CQM;
+
        ieee80211_bss_info_change_notify(sdata, bss_info_changed);
 
        mutex_lock(&local->iflist_mtx);
@@ -850,6 +859,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
        if (is_multicast_ether_addr(hdr->addr1))
                return;
 
+       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+               return;
+
        mod_timer(&sdata->u.mgd.conn_mon_timer,
                  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
 }
@@ -927,23 +939,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
        mutex_unlock(&ifmgd->mtx);
 }
 
-void ieee80211_beacon_loss_work(struct work_struct *work)
+static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+       struct ieee80211_local *local = sdata->local;
+       u8 bssid[ETH_ALEN];
+
+       mutex_lock(&ifmgd->mtx);
+       if (!ifmgd->associated) {
+               mutex_unlock(&ifmgd->mtx);
+               return;
+       }
+
+       memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
+
+       printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
+
+       ieee80211_set_disassoc(sdata);
+       ieee80211_recalc_idle(local);
+       mutex_unlock(&ifmgd->mtx);
+       /*
+        * must be outside lock due to cfg80211,
+        * but that's not a problem.
+        */
+       ieee80211_send_deauth_disassoc(sdata, bssid,
+                                      IEEE80211_STYPE_DEAUTH,
+                                      WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+                                      NULL);
+}
+
+void ieee80211_beacon_connection_loss_work(struct work_struct *work)
 {
        struct ieee80211_sub_if_data *sdata =
                container_of(work, struct ieee80211_sub_if_data,
-                            u.mgd.beacon_loss_work);
+                            u.mgd.beacon_connection_loss_work);
 
-       ieee80211_mgd_probe_ap(sdata, true);
+       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
+               __ieee80211_connection_loss(sdata);
+       else
+               ieee80211_mgd_probe_ap(sdata, true);
 }
 
 void ieee80211_beacon_loss(struct ieee80211_vif *vif)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_hw *hw = &sdata->local->hw;
 
-       ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
+       WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
+       ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
 }
 EXPORT_SYMBOL(ieee80211_beacon_loss);
 
+void ieee80211_connection_loss(struct ieee80211_vif *vif)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+       struct ieee80211_hw *hw = &sdata->local->hw;
+
+       WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
+       ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
+}
+EXPORT_SYMBOL(ieee80211_connection_loss);
+
+
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                         struct ieee80211_mgmt *mgmt, size_t len)
@@ -1633,7 +1690,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
        if (local->quiescing)
                return;
 
-       ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
+       ieee80211_queue_work(&sdata->local->hw,
+                            &sdata->u.mgd.beacon_connection_loss_work);
 }
 
 static void ieee80211_sta_conn_mon_timer(unsigned long data)
@@ -1685,7 +1743,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
         */
 
        cancel_work_sync(&ifmgd->work);
-       cancel_work_sync(&ifmgd->beacon_loss_work);
+       cancel_work_sync(&ifmgd->beacon_connection_loss_work);
        if (del_timer_sync(&ifmgd->timer))
                set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
 
@@ -1719,7 +1777,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_WORK(&ifmgd->work, ieee80211_sta_work);
        INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
        INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
-       INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
+       INIT_WORK(&ifmgd->beacon_connection_loss_work,
+                 ieee80211_beacon_connection_loss_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
        setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
@@ -1893,8 +1952,20 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 
        mutex_lock(&ifmgd->mtx);
        if (ifmgd->associated) {
-               mutex_unlock(&ifmgd->mtx);
-               return -EALREADY;
+               if (!req->prev_bssid ||
+                   memcmp(req->prev_bssid, ifmgd->associated->bssid,
+                          ETH_ALEN)) {
+                       /*
+                        * We are already associated and the request was not a
+                        * reassociation request from the current BSS, so
+                        * reject it.
+                        */
+                       mutex_unlock(&ifmgd->mtx);
+                       return -EALREADY;
+               }
+
+               /* Trying to reassociate - clear previous association state */
+               ieee80211_set_disassoc(sdata);
        }
        mutex_unlock(&ifmgd->mtx);
 
@@ -2119,3 +2190,13 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
        *cookie = (unsigned long) skb;
        return 0;
 }
+
+void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
+                              enum nl80211_cqm_rssi_threshold_event rssi_event,
+                              gfp_t gfp)
+{
+       struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+       cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);