wlcore: ROC on AP channel before auth reply
authorArik Nemtsov <arik@wizery.com>
Tue, 17 Sep 2013 15:41:20 +0000 (18:41 +0300)
committerLuciano Coelho <luciano.coelho@intel.com>
Mon, 30 Sep 2013 18:12:17 +0000 (21:12 +0300)
Start a ROC on the AP channel beforing sending the authentication reply
to a connecting STA. This ROC is held up to 1 second via a timer. If the
station is authorized and added by mac80211, the ROC is extended until
the station is fully authorized.
We make sure not to ROC twice when several stations are connecting in
parallel and to only release the ROC when both the pending-reply timer
and the STA-state callbacks do not require it.

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h

index c30e1f1..3c7b8a4 100644 (file)
@@ -2008,6 +2008,47 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
+static void wlcore_pending_auth_complete_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct wl12xx_vif *wlvif;
+       unsigned long time_spare;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif,
+                            pending_auth_complete_work);
+       wl = wlvif->wl;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /*
+        * Make sure a second really passed since the last auth reply. Maybe
+        * a second auth reply arrived while we were stuck on the mutex.
+        * Check for a little less than the timeout to protect from scheduler
+        * irregularities.
+        */
+       time_spare = jiffies +
+                       msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50);
+       if (!time_after(time_spare, wlvif->pending_auth_reply_time))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       /* cancel the ROC if active */
+       wlcore_update_inconn_sta(wl, wlvif, NULL, false);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
 {
        u8 policy = find_first_zero_bit(wl->rate_policies_map,
@@ -2159,6 +2200,8 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                          wlcore_channel_switch_work);
        INIT_DELAYED_WORK(&wlvif->connection_loss_work,
                          wlcore_connection_loss_work);
+       INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work,
+                         wlcore_pending_auth_complete_work);
        INIT_LIST_HEAD(&wlvif->list);
 
        setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
@@ -2590,6 +2633,7 @@ unlock:
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
        cancel_delayed_work_sync(&wlvif->connection_loss_work);
        cancel_delayed_work_sync(&wlvif->channel_switch_work);
+       cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -3969,6 +4013,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
                        }
                } else {
                        if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+                               /*
+                                * AP might be in ROC in case we have just
+                                * sent auth reply. handle it.
+                                */
+                               if (test_bit(wlvif->role_id, wl->roc_map))
+                                       wl12xx_croc(wl, wlvif->role_id);
+
                                ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
                                if (ret < 0)
                                        goto out;
@@ -4656,29 +4707,49 @@ static void wlcore_roc_if_possible(struct wl1271 *wl,
        wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
 }
 
-static void wlcore_update_inconn_sta(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif,
-                                    struct wl1271_station *wl_sta,
-                                    bool in_connection)
+/*
+ * when wl_sta is NULL, we treat this call as if coming from a
+ * pending auth reply.
+ * wl->mutex must be taken and the FW must be awake when the call
+ * takes place.
+ */
+void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct wl1271_station *wl_sta, bool in_conn)
 {
-       if (in_connection) {
-               if (WARN_ON(wl_sta->in_connection))
+       if (in_conn) {
+               if (WARN_ON(wl_sta && wl_sta->in_connection))
                        return;
-               wl_sta->in_connection = true;
-               if (!wlvif->inconn_count++)
+
+               if (!wlvif->ap_pending_auth_reply &&
+                   !wlvif->inconn_count)
                        wlcore_roc_if_possible(wl, wlvif);
+
+               if (wl_sta) {
+                       wl_sta->in_connection = true;
+                       wlvif->inconn_count++;
+               } else {
+                       wlvif->ap_pending_auth_reply = true;
+               }
        } else {
-               if (!wl_sta->in_connection)
+               if (wl_sta && !wl_sta->in_connection)
+                       return;
+
+               if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply))
                        return;
 
-               wl_sta->in_connection = false;
-               wlvif->inconn_count--;
-               if (WARN_ON(wlvif->inconn_count < 0))
+               if (WARN_ON(wl_sta && !wlvif->inconn_count))
                        return;
 
-               if (!wlvif->inconn_count)
-                       if (test_bit(wlvif->role_id, wl->roc_map))
-                               wl12xx_croc(wl, wlvif->role_id);
+               if (wl_sta) {
+                       wl_sta->in_connection = false;
+                       wlvif->inconn_count--;
+               } else {
+                       wlvif->ap_pending_auth_reply = false;
+               }
+
+               if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply &&
+                   test_bit(wlvif->role_id, wl->roc_map))
+                       wl12xx_croc(wl, wlvif->role_id);
        }
 }
 
index 7e93fe6..03249da 100644 (file)
@@ -86,19 +86,34 @@ void wl1271_free_tx_id(struct wl1271 *wl, int id)
 EXPORT_SYMBOL(wl1271_free_tx_id);
 
 static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
+                                                struct wl12xx_vif *wlvif,
                                                 struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr;
 
+       hdr = (struct ieee80211_hdr *)(skb->data +
+                                      sizeof(struct wl1271_tx_hw_descr));
+       if (!ieee80211_is_auth(hdr->frame_control))
+               return;
+
        /*
         * add the station to the known list before transmitting the
         * authentication response. this way it won't get de-authed by FW
         * when transmitting too soon.
         */
-       hdr = (struct ieee80211_hdr *)(skb->data +
-                                      sizeof(struct wl1271_tx_hw_descr));
-       if (ieee80211_is_auth(hdr->frame_control))
-               wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+       wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+
+       /*
+        * ROC for 1 second on the AP channel for completing the connection.
+        * Note the ROC will be continued by the update_sta_state callbacks
+        * once the station reaches the associated state.
+        */
+       wlcore_update_inconn_sta(wl, wlvif, NULL, true);
+       wlvif->pending_auth_reply_time = jiffies;
+       cancel_delayed_work(&wlvif->pending_auth_complete_work);
+       ieee80211_queue_delayed_work(wl->hw,
+                               &wlvif->pending_auth_complete_work,
+                               msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT));
 }
 
 static void wl1271_tx_regulate_link(struct wl1271 *wl,
@@ -404,7 +419,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid);
 
        if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) {
-               wl1271_tx_ap_update_inconnection_sta(wl, skb);
+               wl1271_tx_ap_update_inconnection_sta(wl, wlvif, skb);
                wl1271_tx_regulate_link(wl, wlvif, hlid);
        }
 
index 55aa4ac..35489c3 100644 (file)
@@ -56,6 +56,9 @@
 /* Used for management frames and dummy packets */
 #define WL1271_TID_MGMT 7
 
+/* stop a ROC for pending authentication reply after this time (ms) */
+#define WLCORE_PEND_AUTH_ROC_TIMEOUT     1000
+
 struct wl127x_tx_mem {
        /*
         * Number of extra memory blocks to allocate for this packet
index 0034979..54ce5d5 100644 (file)
@@ -481,6 +481,8 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
                   struct ieee80211_sta *sta,
                   struct ieee80211_key_conf *key_conf);
 void wlcore_regdomain_config(struct wl1271 *wl);
+void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct wl1271_station *wl_sta, bool in_conn);
 
 static inline void
 wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band,
index e5e1464..14fd111 100644 (file)
@@ -456,6 +456,15 @@ struct wl12xx_vif {
         */
        int hw_queue_base;
 
+       /* do we have a pending auth reply? (and ROC) */
+       bool ap_pending_auth_reply;
+
+       /* time when we sent the pending auth reply */
+       unsigned long pending_auth_reply_time;
+
+       /* work for canceling ROC after pending auth reply */
+       struct delayed_work pending_auth_complete_work;
+
        /*
         * This struct must be last!
         * data that has to be saved acrossed reconfigs (e.g. recovery)