wlcore: use new set bandwidth command to adjusting channel BW
authorArik Nemtsov <arik@wizery.com>
Mon, 26 Nov 2012 16:05:41 +0000 (18:05 +0200)
committerLuciano Coelho <coelho@ti.com>
Wed, 28 Nov 2012 09:45:42 +0000 (11:45 +0200)
We support changing the channel BW when we started the STA role on
a 40Mhz bandwidth. Otherwise a reconnection is required.
Save the started channel width and use it when channel width updates
arrive.

Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Luciano Coelho <coelho@ti.com>
drivers/net/wireless/ti/wl18xx/acx.c
drivers/net/wireless/ti/wl18xx/acx.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h

index adff9cd..801d8af 100644 (file)
@@ -109,3 +109,34 @@ out:
        kfree(acx);
        return ret;
 }
+
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide)
+{
+       struct wlcore_peer_ht_operation_mode *acx;
+       int ret;
+
+       wl1271_debug(DEBUG_ACX, "acx peer ht operation mode hlid %d bw %d",
+                    hlid, wide);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       acx->hlid = hlid;
+       acx->bandwidth = wide ? WLCORE_BANDWIDTH_40MHZ : WLCORE_BANDWIDTH_20MHZ;
+
+       ret = wl1271_cmd_configure(wl, ACX_PEER_HT_OPERATION_MODE_CFG, acx,
+                                  sizeof(*acx));
+
+       if (ret < 0) {
+               wl1271_warning("acx peer ht operation mode failed: %d", ret);
+               goto out;
+       }
+
+out:
+       kfree(acx);
+       return ret;
+
+}
index 394d125..b57e348 100644 (file)
@@ -284,10 +284,24 @@ struct wl18xx_acx_clear_statistics {
        struct acx_header header;
 };
 
+enum wlcore_bandwidth {
+       WLCORE_BANDWIDTH_20MHZ,
+       WLCORE_BANDWIDTH_40MHZ,
+};
+
+struct wlcore_peer_ht_operation_mode {
+       struct acx_header header;
+
+       u8 hlid;
+       u8 bandwidth; /* enum wlcore_bandwidth */
+       u8 padding[2];
+};
+
 int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
                                  u32 sdio_blk_size, u32 extra_mem_blks,
                                  u32 len_field_size);
 int wl18xx_acx_set_checksum_state(struct wl1271 *wl);
 int wl18xx_acx_clear_statistics(struct wl1271 *wl);
+int wl18xx_acx_peer_ht_operation_mode(struct wl1271 *wl, u8 hlid, bool wide);
 
 #endif /* __WL18XX_ACX_H__ */
index c015231..c616c21 100644 (file)
@@ -1319,6 +1319,43 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl,
        return buf_offset;
 }
 
+static void wl18xx_sta_rc_update(struct wl1271 *wl,
+                                struct wl12xx_vif *wlvif,
+                                struct ieee80211_sta *sta,
+                                u32 changed)
+{
+       bool wide = sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+
+       wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide);
+
+       if (!(changed & IEEE80211_RC_BW_CHANGED))
+               return;
+
+       mutex_lock(&wl->mutex);
+
+       /* sanity */
+       if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS))
+               goto out;
+
+       /* ignore the change before association */
+       if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+               goto out;
+
+       /*
+        * If we started out as wide, we can change the operation mode. If we
+        * thought this was a 20mhz AP, we have to reconnect
+        */
+       if (wlvif->sta.role_chan_type == NL80211_CHAN_HT40MINUS ||
+           wlvif->sta.role_chan_type == NL80211_CHAN_HT40PLUS)
+               wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide);
+       else
+               ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif));
+
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+
 static int wl18xx_setup(struct wl1271 *wl);
 
 static struct wlcore_ops wl18xx_ops = {
@@ -1354,6 +1391,7 @@ static struct wlcore_ops wl18xx_ops = {
        .set_key        = wl18xx_set_key,
        .channel_switch = wl18xx_cmd_channel_switch,
        .pre_pkt_send   = wl18xx_pre_pkt_send,
+       .sta_rc_update  = wl18xx_sta_rc_update,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
index 599c006..65f071d 100644 (file)
@@ -480,6 +480,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                goto err_hlid;
        }
 
+       wlvif->sta.role_chan_type = wlvif->channel_type;
        goto out_free;
 
 err_hlid:
index 2673d78..0e0b656 100644 (file)
@@ -201,4 +201,12 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len)
        return buf_offset;
 }
 
+static inline void
+wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                       struct ieee80211_sta *sta, u32 changed)
+{
+       if (wl->ops->sta_rc_update)
+               wl->ops->sta_rc_update(wl, wlvif, sta, changed);
+}
+
 #endif
index cce73c4..59ad288 100644 (file)
@@ -4948,6 +4948,17 @@ static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
        return 0;
 }
 
+static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_sta *sta,
+                                   u32 changed)
+{
+       struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       struct wl1271 *wl = hw->priv;
+
+       wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
+}
+
 static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
 {
        struct wl1271 *wl = hw->priv;
@@ -5146,6 +5157,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .change_chanctx = wlcore_op_change_chanctx,
        .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
        .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
+       .sta_rc_update = wlcore_op_sta_rc_update,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
 };
 
index b315897..037c56e 100644 (file)
@@ -101,6 +101,8 @@ struct wlcore_ops {
                              struct wl12xx_vif *wlvif,
                              struct ieee80211_channel_switch *ch_switch);
        u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len);
+       void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct ieee80211_sta *sta, u32 changed);
 };
 
 enum wlcore_partitions {
index 6be1e8e..e3a77aa 100644 (file)
@@ -341,6 +341,8 @@ struct wl12xx_vif {
                        u8 klv_template_id;
 
                        bool qos;
+                       /* channel type we started the STA role with */
+                       enum nl80211_channel_type role_chan_type;
                } sta;
                struct {
                        u8 global_hlid;