wl12xx: replace dummy_join with ROC/CROC commands
authorEliad Peller <eliad@wizery.com>
Sun, 14 Aug 2011 10:17:17 +0000 (13:17 +0300)
committerLuciano Coelho <coelho@ti.com>
Mon, 22 Aug 2011 09:35:26 +0000 (12:35 +0300)
The ROC command asks the fw stay on the channel of the given
hlid. it currently has 2 primary functions:

1. Allow tx/rx from the device role.

In order to tx/rx packets while the stations is not associated
(e.g. auth req/resp), the device role has to be used, along
with ROC on its link.

Keep the logic similiar to the one used in dummy_join. However,
since we can't scan while we ROC, we add CROC before starting
a scan, and ROC again (if needed) on scan complete.

2. Keeping the antenna for a specific link.

We ROC until the connection was completed (after EAPOLs exchange)
in order to prevent BT coex operations from taking the antenna
and failing the connection (after this stage, psm can be used).

During association, we ROC on the station role, and then CROC
the device role, thus assuring being ROC during all the connection
process.

Delete the WL1271_FLAG_JOINED flag, and use a roc bitmap
to indicate what roles are currently ROCed.

Add wl12xx_roc/croc functions in order to wrap the roc/croc
commands while taking care of the roc bitmap.

The current ROC/CROC state-machine is a bit complicated. In
the future we'll probably want to use wpa_supplicant to control
the ROC during connection.

Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/wl12xx/cmd.c
drivers/net/wireless/wl12xx/cmd.h
drivers/net/wireless/wl12xx/main.c
drivers/net/wireless/wl12xx/scan.c
drivers/net/wireless/wl12xx/tx.c
drivers/net/wireless/wl12xx/wl12xx.h

index 1b04102144e60755c5009dc3524378ac7a7f099f..c620a9d4939c80a21c605b67e3d057e618de120b 100644 (file)
@@ -1554,3 +1554,42 @@ out_free:
 out:
        return ret;
 }
+
+int wl12xx_roc(struct wl1271 *wl, u8 role_id)
+{
+       int ret = 0;
+
+       if (WARN_ON(test_bit(role_id, wl->roc_map)))
+               return 0;
+
+       ret = wl12xx_cmd_roc(wl, role_id);
+       if (ret < 0)
+               goto out;
+
+       ret = wl1271_cmd_wait_for_event(wl,
+                                       REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID);
+       if (ret < 0) {
+               wl1271_error("cmd roc event completion error");
+               goto out;
+       }
+
+       __set_bit(role_id, wl->roc_map);
+out:
+       return ret;
+}
+
+int wl12xx_croc(struct wl1271 *wl, u8 role_id)
+{
+       int ret = 0;
+
+       if (WARN_ON(!test_bit(role_id, wl->roc_map)))
+               return 0;
+
+       ret = wl12xx_cmd_croc(wl, role_id);
+       if (ret < 0)
+               goto out;
+
+       __clear_bit(role_id, wl->roc_map);
+out:
+       return ret;
+}
index 80b02ed0e3f287e453ee91f9b0892fafa8099668..5cf92e256641ffb352c513fc06e67e22f7f4a9d0 100644 (file)
@@ -71,6 +71,8 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
                          u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
                          u16 tx_seq_16);
 int wl12xx_cmd_set_peer_state(struct wl1271 *wl);
+int wl12xx_roc(struct wl1271 *wl, u8 role_id);
+int wl12xx_croc(struct wl1271 *wl, u8 role_id);
 int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
 int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
 int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
index e4081e2221842d3d5fc98778790405a5cc2e22b9..57b10e98730eb44ca5cd29ab48a455e20edcfc59 100644 (file)
@@ -440,6 +440,8 @@ static int wl1271_check_operstate(struct wl1271 *wl, unsigned char operstate)
        if (ret < 0)
                return ret;
 
+       wl12xx_croc(wl, wl->role_id);
+
        wl1271_info("Association completed.");
        return 0;
 }
@@ -2068,6 +2070,7 @@ deinit:
        wl->dev_role_id = WL12XX_INVALID_ROLE_ID;
        memset(wl->roles_map, 0, sizeof(wl->roles_map));
        memset(wl->links_map, 0, sizeof(wl->links_map));
+       memset(wl->roc_map, 0, sizeof(wl->roc_map));
 
        /* The system link is always allocated */
        __set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
@@ -2110,25 +2113,6 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
        cancel_work_sync(&wl->recovery_work);
 }
 
-static int wl1271_dummy_join(struct wl1271 *wl)
-{
-       int ret = 0;
-       /* we need to use a dummy BSSID for now */
-       static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
-                                                 0xad, 0xbe, 0xef };
-
-       memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
-
-       ret = wl12xx_cmd_role_start_sta(wl);
-       if (ret < 0)
-               goto out;
-
-       set_bit(WL1271_FLAG_JOINED, &wl->flags);
-
-out:
-       return ret;
-}
-
 static int wl1271_join(struct wl1271 *wl, bool set_assoc)
 {
        int ret;
@@ -2152,8 +2136,6 @@ static int wl1271_join(struct wl1271 *wl, bool set_assoc)
        if (ret < 0)
                goto out;
 
-       set_bit(WL1271_FLAG_JOINED, &wl->flags);
-
        if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
                goto out;
 
@@ -2193,7 +2175,6 @@ static int wl1271_unjoin(struct wl1271 *wl)
        if (ret < 0)
                goto out;
 
-       clear_bit(WL1271_FLAG_JOINED, &wl->flags);
        memset(wl->bssid, 0, ETH_ALEN);
 
        /* reset TX security counters on a clean disconnect */
@@ -2212,13 +2193,29 @@ static void wl1271_set_band_rate(struct wl1271 *wl)
                wl->basic_rate_set = wl->conf.tx.basic_rate_5;
 }
 
+static bool wl12xx_is_roc(struct wl1271 *wl)
+{
+       u8 role_id;
+
+       role_id = find_first_bit(wl->roc_map, WL12XX_MAX_ROLES);
+       if (role_id >= WL12XX_MAX_ROLES)
+               return false;
+
+       return true;
+}
+
 static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
 {
        int ret;
 
        if (idle) {
-               if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
-                       ret = wl1271_unjoin(wl);
+               /* no need to croc if we weren't busy (e.g. during boot) */
+               if (wl12xx_is_roc(wl)) {
+                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                       if (ret < 0)
+                               goto out;
+
+                       ret = wl12xx_cmd_role_stop_dev(wl);
                        if (ret < 0)
                                goto out;
                }
@@ -2244,7 +2241,11 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
                        ieee80211_sched_scan_stopped(wl->hw);
                }
 
-               ret = wl1271_dummy_join(wl);
+               ret = wl12xx_cmd_role_start_dev(wl);
+               if (ret < 0)
+                       goto out;
+
+               ret = wl12xx_roc(wl, wl->dev_role_id);
                if (ret < 0)
                        goto out;
                clear_bit(WL1271_FLAG_IDLE, &wl->flags);
@@ -2324,11 +2325,34 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
                                wl1271_warning("rate policy for channel "
                                               "failed %d", ret);
 
-                       if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
+                       if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+                               if (wl12xx_is_roc(wl)) {
+                                       /* roaming */
+                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               goto out_sleep;
+                               }
                                ret = wl1271_join(wl, false);
                                if (ret < 0)
                                        wl1271_warning("cmd join on channel "
                                                       "failed %d", ret);
+                       } else {
+                               /*
+                                * change the ROC channel. do it only if we are
+                                * not idle. otherwise, CROC will be called
+                                * anyway.
+                                */
+                               if (wl12xx_is_roc(wl) &&
+                                   !(conf->flags & IEEE80211_CONF_IDLE)) {
+                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               goto out_sleep;
+
+                                       ret = wl12xx_roc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               wl1271_warning("roc failed %d",
+                                                              ret);
+                               }
                        }
                }
        }
@@ -2784,10 +2808,20 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
-       ret = wl1271_scan(hw->priv, ssid, len, req);
+       /* cancel ROC before scanning */
+       if (wl12xx_is_roc(wl)) {
+               if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
+                       /* don't allow scanning right now */
+                       ret = -EBUSY;
+                       goto out_sleep;
+               }
+               wl12xx_croc(wl, wl->dev_role_id);
+               wl12xx_cmd_role_stop_dev(wl);
+       }
 
+       ret = wl1271_scan(hw->priv, ssid, len, req);
+out_sleep:
        wl1271_ps_elp_sleep(wl);
-
 out:
        mutex_unlock(&wl->mutex);
 
@@ -3311,7 +3345,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        bool was_assoc =
                            !!test_and_clear_bit(WL1271_FLAG_STA_ASSOCIATED,
                                                 &wl->flags);
-                       clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags);
+                       bool was_ifup =
+                           !!test_and_clear_bit(WL1271_FLAG_STA_STATE_SENT,
+                                                &wl->flags);
                        wl->aid = 0;
 
                        /* free probe-request template */
@@ -3338,8 +3374,32 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
 
                        /* restore the bssid filter and go to dummy bssid */
                        if (was_assoc) {
+                               u32 conf_flags = wl->hw->conf.flags;
+                               /*
+                                * we might have to disable roc, if there was
+                                * no IF_OPER_UP notification.
+                                */
+                               if (!was_ifup) {
+                                       ret = wl12xx_croc(wl, wl->role_id);
+                                       if (ret < 0)
+                                               goto out;
+                               }
+                               /*
+                                * (we also need to disable roc in case of
+                                * roaming on the same channel. until we will
+                                * have a better flow...)
+                                */
+                               if (test_bit(wl->dev_role_id, wl->roc_map)) {
+                                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                                       if (ret < 0)
+                                               goto out;
+                               }
+
                                wl1271_unjoin(wl);
-                               wl1271_dummy_join(wl);
+                               if (!(conf_flags & IEEE80211_CONF_IDLE)) {
+                                       wl12xx_cmd_role_start_dev(wl);
+                                       wl12xx_roc(wl, wl->dev_role_id);
+                               }
                        }
                }
        }
@@ -3400,7 +3460,29 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                        wl1271_warning("cmd join failed %d", ret);
                        goto out;
                }
-               wl1271_check_operstate(wl, ieee80211_get_operstate(vif));
+
+               /* ROC until connected (after EAPOL exchange) */
+               if (!is_ibss) {
+                       ret = wl12xx_roc(wl, wl->role_id);
+                       if (ret < 0)
+                               goto out;
+
+                       wl1271_check_operstate(wl,
+                                              ieee80211_get_operstate(vif));
+               }
+               /*
+                * stop device role if started (we might already be in
+                * STA role). TODO: make it better.
+                */
+               if (wl->dev_role_id != WL12XX_INVALID_ROLE_ID) {
+                       ret = wl12xx_croc(wl, wl->dev_role_id);
+                       if (ret < 0)
+                               goto out;
+
+                       ret = wl12xx_cmd_role_stop_dev(wl);
+                       if (ret < 0)
+                               goto out;
+               }
        }
 
 out:
index 54a4e75a37fe8de1f41bee557506c9cf76080ddf..653091a38dce3266abe7ab32b029feebedd03fd2 100644 (file)
@@ -33,6 +33,7 @@ void wl1271_scan_complete_work(struct work_struct *work)
 {
        struct delayed_work *dwork;
        struct wl1271 *wl;
+       int ret;
 
        dwork = container_of(work, struct delayed_work, work);
        wl = container_of(dwork, struct wl1271, scan_complete_work);
@@ -50,21 +51,28 @@ void wl1271_scan_complete_work(struct work_struct *work)
        wl->scan.state = WL1271_SCAN_STATE_IDLE;
        memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
        wl->scan.req = NULL;
-       ieee80211_scan_completed(wl->hw, false);
 
-       /* restore hardware connection monitoring template */
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
        if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
-               if (wl1271_ps_elp_wakeup(wl) == 0) {
-                       wl1271_cmd_build_ap_probe_req(wl, wl->probereq);
-                       wl1271_ps_elp_sleep(wl);
-               }
+               /* restore hardware connection monitoring template */
+               wl1271_cmd_build_ap_probe_req(wl, wl->probereq);
+       } else {
+               /* restore remain on channel */
+               wl12xx_cmd_role_start_dev(wl);
+               wl12xx_roc(wl, wl->dev_role_id);
        }
+       wl1271_ps_elp_sleep(wl);
 
        if (wl->scan.failed) {
                wl1271_info("Scan completed due to error.");
                wl12xx_queue_recovery_work(wl);
        }
 
+       ieee80211_scan_completed(wl->hw, false);
+
 out:
        mutex_unlock(&wl->mutex);
 
index a2dbbadd58f03b2ea5f49577668e570e707f7d06..057db6f86bee1b70ddc5dbbcbfa0b45503f2cfb3 100644 (file)
@@ -78,6 +78,7 @@ static int wl1271_tx_update_filters(struct wl1271 *wl,
                                                 struct sk_buff *skb)
 {
        struct ieee80211_hdr *hdr;
+       int ret;
 
        hdr = (struct ieee80211_hdr *)(skb->data +
                                       sizeof(struct wl1271_tx_hw_descr));
@@ -91,6 +92,18 @@ static int wl1271_tx_update_filters(struct wl1271 *wl,
        if (!ieee80211_is_auth(hdr->frame_control))
                return 0;
 
+       if (wl->dev_hlid != WL12XX_INVALID_LINK_ID)
+               goto out;
+
+       wl1271_debug(DEBUG_CMD, "starting device role for roaming");
+       ret = wl12xx_cmd_role_start_dev(wl);
+       if (ret < 0)
+               goto out;
+
+       ret = wl12xx_roc(wl, wl->dev_role_id);
+       if (ret < 0)
+               goto out;
+out:
        return 0;
 }
 
index 089304d874d58d27963be4975bc2d8866a5f2409..a136795352c81155ed23f96b6f1ae36a8a6e67c5 100644 (file)
@@ -319,7 +319,6 @@ struct wl1271_ap_key {
 
 enum wl12xx_flags {
        WL1271_FLAG_STA_ASSOCIATED,
-       WL1271_FLAG_JOINED,
        WL1271_FLAG_GPIO_POWER,
        WL1271_FLAG_TX_QUEUE_STOPPED,
        WL1271_FLAG_TX_PENDING,
@@ -402,6 +401,7 @@ struct wl1271 {
 
        unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
        unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
+       unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
 
        struct wl1271_acx_mem_map *target_mem_map;