2012-11-20 11:20:09

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 00/11] wlcore: prepare to multi-channel

Make the connection flow simpler by using only the sta
role (i.e. no need for dev role).

This is possible now, as we know the AP's BSSID even
before authentication.

This means we no longer start dev role when the device
is non-idle, so now we use the .remain_on_channel()
and start device role only then.

Finally, implement the chanctx callbacks in order to
have a clear per-vif channel (instead of the global
hw->channel)

(this patchset depends on
"mac80211: make remain_on_channel() op pass vif param")

v2: fix some issues pointed out by Arik/Luca (thanks!)
squash some patches together (thanks Johannes and Julian!)

Eliad Peller (11):
wlcore: start sta role on CHANGED_BSSID
wlcore: workaround start_sta problem in wl12xx fw
wlcore: implement .remain_on_channel() callback
wlcore: get channel from bss_conf instead of hw->conf
wlcore: add chanctx implementation
wlcore: initiate ROC/CROC on sta state updates
wlcore: set active psm on association
wlcore: specify correct supported_rates
wlcore: reconfigure rate policy on association
wlcore: refactor CHANGED_HT handling
wlcore: configure the remote rates with our own rates

drivers/net/wireless/ti/wl12xx/main.c | 9 +-
drivers/net/wireless/ti/wlcore/cmd.c | 60 ++-
drivers/net/wireless/ti/wlcore/cmd.h | 6 +-
drivers/net/wireless/ti/wlcore/conf.h | 4 +-
drivers/net/wireless/ti/wlcore/event.c | 7 +
drivers/net/wireless/ti/wlcore/init.c | 2 +-
drivers/net/wireless/ti/wlcore/main.c | 867 ++++++++++++++++++-------------
drivers/net/wireless/ti/wlcore/tx.c | 14 +-
drivers/net/wireless/ti/wlcore/wlcore.h | 6 +
9 files changed, 578 insertions(+), 397 deletions(-)

--
1.7.6.401.g6a319



2012-11-20 11:20:15

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 03/11] wlcore: implement .remain_on_channel() callback

implement the reamin_on_channel() callback by starting
a dev role (already associated with the current vif)
on the requested channel/band.

This channel is usually different from the channel
of the sta role, so pass it to wl12xx_roc() as well,
and notify mac80211 (async) when the fw is ready
on the new channel.

Now, in case of offchannel tx, we should use the dev
role hlid, instead of the sta hlid.

Signed-off-by: Eliad Peller <[email protected]>
---
v2: pass band as well (thanks Arik!)
squash patch [6/15] into this one.

drivers/net/wireless/ti/wlcore/cmd.c | 41 +++-----
drivers/net/wireless/ti/wlcore/cmd.h | 6 +-
drivers/net/wireless/ti/wlcore/event.c | 7 ++
drivers/net/wireless/ti/wlcore/main.c | 154 +++++++++++++++++++++++++++----
drivers/net/wireless/ti/wlcore/tx.c | 8 ++
drivers/net/wireless/ti/wlcore/wlcore.h | 3 +
6 files changed, 173 insertions(+), 46 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index eaef3f4..38fa8ff 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -345,7 +345,9 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
}

static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
- struct wl12xx_vif *wlvif)
+ struct wl12xx_vif *wlvif,
+ enum ieee80211_band band,
+ int channel)
{
struct wl12xx_cmd_role_start *cmd;
int ret;
@@ -359,9 +361,9 @@ static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
wl1271_debug(DEBUG_CMD, "cmd role start dev %d", wlvif->dev_role_id);

cmd->role_id = wlvif->dev_role_id;
- if (wlvif->band == IEEE80211_BAND_5GHZ)
+ if (band == IEEE80211_BAND_5GHZ)
cmd->band = WLCORE_BAND_5GHZ;
- cmd->channel = wlvif->channel;
+ cmd->channel = channel;

if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->dev_hlid);
@@ -1591,12 +1593,12 @@ out:
}

static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- u8 role_id)
+ u8 role_id, enum ieee80211_band band, u8 channel)
{
struct wl12xx_cmd_roc *cmd;
int ret = 0;

- wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", wlvif->channel, role_id);
+ wl1271_debug(DEBUG_CMD, "cmd roc %d (%d)", channel, role_id);

if (WARN_ON(role_id == WL12XX_INVALID_ROLE_ID))
return -EINVAL;
@@ -1608,8 +1610,8 @@ static int wl12xx_cmd_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
}

cmd->role_id = role_id;
- cmd->channel = wlvif->channel;
- switch (wlvif->band) {
+ cmd->channel = channel;
+ switch (band) {
case IEEE80211_BAND_2GHZ:
cmd->band = WLCORE_BAND_2_4GHZ;
break;
@@ -1664,30 +1666,18 @@ out:
return ret;
}

-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id)
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+ enum ieee80211_band band, u8 channel)
{
int ret = 0;
- bool is_first_roc;

if (WARN_ON(test_bit(role_id, wl->roc_map)))
return 0;

- is_first_roc = (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >=
- WL12XX_MAX_ROLES);
-
- ret = wl12xx_cmd_roc(wl, wlvif, role_id);
+ ret = wl12xx_cmd_roc(wl, wlvif, role_id, band, channel);
if (ret < 0)
goto out;

- if (is_first_roc) {
- 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;
@@ -1780,7 +1770,8 @@ out:
}

/* start dev role and roc on its channel */
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ enum ieee80211_band band, int channel)
{
int ret;

@@ -1795,11 +1786,11 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif)
if (ret < 0)
goto out;

- ret = wl12xx_cmd_role_start_dev(wl, wlvif);
+ ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel);
if (ret < 0)
goto out_disable;

- ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id);
+ ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel);
if (ret < 0)
goto out_stop;

diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 2409f3d..3be2e92 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -39,7 +39,8 @@ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_stop_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_cmd_role_start_ibss(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ enum ieee80211_band band, int channel);
int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len);
@@ -76,7 +77,8 @@ int wl1271_cmd_set_ap_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
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, u8 hlid);
-int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id);
+int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
+ enum ieee80211_band band, u8 channel);
int wl12xx_croc(struct wl1271 *wl, u8 role_id);
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_sta *sta, u8 hlid);
diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c
index 4890705..cf45aaf 100644
--- a/drivers/net/wireless/ti/wlcore/event.c
+++ b/drivers/net/wireless/ti/wlcore/event.c
@@ -250,6 +250,13 @@ static int wl1271_event_process(struct wl1271 *wl)
disconnect_sta = true;
}

+ if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) {
+ wl1271_debug(DEBUG_EVENT,
+ "REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID");
+ if (wl->roc_vif)
+ ieee80211_ready_on_channel(wl->hw);
+ }
+
if (disconnect_sta) {
u32 num_packets = wl->conf.tx.max_tx_retries;
struct ieee80211_sta *sta;
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index be60394..e9a8092 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2723,24 +2723,6 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (ret < 0)
wl1271_warning("rate policy for channel "
"failed %d", ret);
-
- /*
- * change the ROC channel. do it only if we are
- * not idle. otherwise, CROC will be called
- * anyway.
- */
- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED,
- &wlvif->flags) &&
- wl12xx_dev_role_started(wlvif) &&
- !(conf->flags & IEEE80211_CONF_IDLE)) {
- ret = wl12xx_stop_dev(wl, wlvif);
- if (ret < 0)
- return ret;
-
- ret = wl12xx_start_dev(wl, wlvif);
- if (ret < 0)
- return ret;
- }
}
}

@@ -4057,7 +4039,8 @@ sta_not_found:

/* ROC until connected (after EAPOL exchange) */
if (!is_ibss) {
- ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
+ ret = wl12xx_roc(wl, wlvif, wlvif->role_id,
+ wlvif->band, wlvif->channel);
if (ret < 0)
goto out;
}
@@ -4692,6 +4675,134 @@ static void wlcore_op_flush(struct ieee80211_hw *hw, bool drop)
wl1271_tx_flush(wl);
}

+static int wlcore_op_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ int duration)
+{
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+ struct wl1271 *wl = hw->priv;
+ int channel, ret = 0;
+
+ channel = ieee80211_frequency_to_channel(chan->center_freq);
+
+ wl1271_debug(DEBUG_MAC80211, "mac80211 roc %d (%d)",
+ channel, wlvif->role_id);
+
+ mutex_lock(&wl->mutex);
+
+ if (unlikely(wl->state != WLCORE_STATE_ON))
+ goto out;
+
+ /* return EBUSY if we can't ROC right now */
+ if (WARN_ON(wl->roc_vif ||
+ find_first_bit(wl->roc_map,
+ WL12XX_MAX_ROLES) < WL12XX_MAX_ROLES)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0)
+ goto out;
+
+ ret = wl12xx_start_dev(wl, wlvif, chan->band, channel);
+ if (ret < 0)
+ goto out_sleep;
+
+ wl->roc_vif = vif;
+ ieee80211_queue_delayed_work(hw, &wl->roc_complete_work,
+ msecs_to_jiffies(duration));
+out_sleep:
+ wl1271_ps_elp_sleep(wl);
+out:
+ mutex_unlock(&wl->mutex);
+ return ret;
+}
+
+static int __wlcore_roc_completed(struct wl1271 *wl)
+{
+ struct wl12xx_vif *wlvif;
+ int ret;
+
+ /* already completed */
+ if (unlikely(!wl->roc_vif))
+ return 0;
+
+ wlvif = wl12xx_vif_to_data(wl->roc_vif);
+
+ if (!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
+ return -EBUSY;
+
+ ret = wl12xx_stop_dev(wl, wlvif);
+ if (ret < 0)
+ return ret;
+
+ wl->roc_vif = NULL;
+
+ return 0;
+}
+
+static int wlcore_roc_completed(struct wl1271 *wl)
+{
+ int ret;
+
+ wl1271_debug(DEBUG_MAC80211, "roc complete");
+
+ mutex_lock(&wl->mutex);
+
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0)
+ goto out;
+
+ ret = __wlcore_roc_completed(wl);
+
+ wl1271_ps_elp_sleep(wl);
+out:
+ mutex_unlock(&wl->mutex);
+
+ return ret;
+}
+
+static void wlcore_roc_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, roc_complete_work);
+
+ ret = wlcore_roc_completed(wl);
+ if (!ret)
+ ieee80211_remain_on_channel_expired(wl->hw);
+}
+
+static int wlcore_op_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+ struct wl1271 *wl = hw->priv;
+
+ wl1271_debug(DEBUG_MAC80211, "mac80211 croc");
+
+ /* TODO: per-vif */
+ wl1271_tx_flush(wl);
+
+ /*
+ * we can't just flush_work here, because it might deadlock
+ * (as we might get called from the same workqueue)
+ */
+ cancel_delayed_work_sync(&wl->roc_complete_work);
+ wlcore_roc_completed(wl);
+
+ return 0;
+}
+
static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
{
struct wl1271 *wl = hw->priv;
@@ -4883,6 +4994,8 @@ static const struct ieee80211_ops wl1271_ops = {
.set_bitrate_mask = wl12xx_set_bitrate_mask,
.channel_switch = wl12xx_op_channel_switch,
.flush = wlcore_op_flush,
+ .remain_on_channel = wlcore_op_remain_on_channel,
+ .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
};

@@ -5279,6 +5392,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
sizeof(struct ieee80211_header);

+ wl->hw->wiphy->max_remain_on_channel_duration = 5000;
+
wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD |
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;

@@ -5377,6 +5492,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
INIT_WORK(&wl->tx_work, wl1271_tx_work);
INIT_WORK(&wl->recovery_work, wl1271_recovery_work);
INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work);
+ INIT_DELAYED_WORK(&wl->roc_complete_work, wlcore_roc_complete_work);
INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work);
INIT_DELAYED_WORK(&wl->connection_loss_work,
wl1271_connection_loss_work);
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index a555a70..cf6dbee 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -155,12 +155,20 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, struct ieee80211_sta *sta)
{
+ struct ieee80211_tx_info *control;
+
if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
return wl->system_hlid;

if (wlvif->bss_type == BSS_TYPE_AP_BSS)
return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta);

+ control = IEEE80211_SKB_CB(skb);
+ if (control->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+ wl1271_debug(DEBUG_TX, "tx offchannel");
+ return wlvif->dev_hlid;
+ }
+
return wlvif->sta.hlid;
}

diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 1030b6b..b636f1d 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -286,6 +286,9 @@ struct wl1271 {
/* Connection loss work */
struct delayed_work connection_loss_work;

+ struct ieee80211_vif *roc_vif;
+ struct delayed_work roc_complete_work;
+
bool sched_scanning;

/* The current band */
--
1.7.6.401.g6a319


2012-11-20 11:20:23

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 07/11] wlcore: set active psm on association

The default ps mode of the fw is auto, while the default
ps mode of mac80211 is active (ps off).
In order to sync them, configure active ps on association.

Signed-off-by: Eliad Peller <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 55dce4a..e393cea 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2614,6 +2614,12 @@ static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (ret < 0)
return ret;

+ /*
+ * The default fw psm configuration is AUTO, while mac80211 default
+ * setting is off (ACTIVE), so sync the fw with the correct value.
+ */
+ ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
+
return ret;
}

--
1.7.6.401.g6a319


2012-11-20 11:20:12

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 01/11] wlcore: start sta role on CHANGED_BSSID

Make the connection flow simpler by starting
sta role on bssid change.

Currently, we start dev role when going idle-off,
and start the sta role only after association
indication. This complicates the connection
flow with some possible intermediate states.

Make it simpler by starting sta role on bssid change,
which now happens *before* auth req get sent.

Update the handling of mac80211's notifications
and change wl1271_join/unjoin accordingly -
* Split wl1271_join() into wlcore_join (tuning on
a channel/bssid) and wlcore_set_assoc (configure
sta after association).
* Rename wl1271_unjoin() to wlcore_unset_assoc(), as
it is no longer the inversion of wl1271_join()
(now it's only used to disconnect associated sta /
joined ibss, without stopping the role).
* Set ssid before starting station role (needed for
start_role(sta)

While on it, split wl1271_bss_info_changed_sta() into
some sub-functions.

since we no longer use dev role in the connection flow,
we now always use the hlid of the sta role.

Signed-off-by: Eliad Peller <[email protected]>
---
v2: take some more code out of wl1271_bss_info_changed_sta (thanks Luca!)
squash patches [2/15, 3/15] into this one (thanks Julian!)

drivers/net/wireless/ti/wlcore/main.c | 444 ++++++++++++++++-----------------
drivers/net/wireless/ti/wlcore/tx.c | 10 +-
2 files changed, 221 insertions(+), 233 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 41ed1d5..2690fe9 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2468,8 +2468,7 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
return ret;
}

-static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- bool set_assoc)
+static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
@@ -2489,18 +2488,96 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
/* clear encryption type */
wlvif->encryption_type = KEY_NONE;

- if (set_assoc)
- set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
-
if (is_ibss)
ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
else
ret = wl12xx_cmd_role_start_sta(wl, wlvif);
+
+ return ret;
+}
+
+static int wl1271_ssid_set(struct wl12xx_vif *wlvif, struct sk_buff *skb,
+ int offset)
+{
+ u8 ssid_len;
+ const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
+ skb->len - offset);
+
+ if (!ptr) {
+ wl1271_error("No SSID in IEs!");
+ return -ENOENT;
+ }
+
+ ssid_len = ptr[1];
+ if (ssid_len > IEEE80211_MAX_SSID_LEN) {
+ wl1271_error("SSID is too long!");
+ return -EINVAL;
+ }
+
+ wlvif->ssid_len = ssid_len;
+ memcpy(wlvif->ssid, ptr+2, ssid_len);
+ return 0;
+}
+
+static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+ struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+ struct sk_buff *skb;
+ int ieoffset;
+
+ /* we currently only support setting the ssid from the ap probe req */
+ if (wlvif->bss_type != BSS_TYPE_STA_BSS)
+ return -EINVAL;
+
+ skb = ieee80211_ap_probereq_get(wl->hw, vif);
+ if (!skb)
+ return -EINVAL;
+
+ ieoffset = offsetof(struct ieee80211_mgmt,
+ u.probe_req.variable);
+ wl1271_ssid_set(wlvif, skb, ieoffset);
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ struct ieee80211_bss_conf *bss_conf)
+{
+ int ieoffset;
+ int ret;
+
+ wlvif->aid = bss_conf->aid;
+ wlvif->channel_type = bss_conf->channel_type;
+ wlvif->beacon_int = bss_conf->beacon_int;
+
+ set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags);
+
+ /*
+ * with wl1271, we don't need to update the
+ * beacon_int and dtim_period, because the firmware
+ * updates it by itself when the first beacon is
+ * received after a join.
+ */
+ ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
if (ret < 0)
- goto out;
+ return ret;

- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
- goto out;
+ /*
+ * Get a template for hardware connection maintenance
+ */
+ dev_kfree_skb(wlvif->probereq);
+ wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
+ wlvif,
+ NULL);
+ ieoffset = offsetof(struct ieee80211_mgmt,
+ u.probe_req.variable);
+ wl1271_ssid_set(wlvif, wlvif->probereq, ieoffset);
+
+ /* enable the connection monitoring feature */
+ ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
+ if (ret < 0)
+ return ret;

/*
* The join command disable the keep-alive mode, shut down its process,
@@ -2510,29 +2587,58 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
*/
ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
if (ret < 0)
- goto out;
+ return ret;

ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
if (ret < 0)
- goto out;
+ return ret;

ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
if (ret < 0)
- goto out;
+ return ret;

ret = wl1271_acx_keep_alive_config(wl, wlvif,
wlvif->sta.klv_template_id,
ACX_KEEP_ALIVE_TPL_VALID);
if (ret < 0)
- goto out;
+ return ret;

-out:
return ret;
}

-static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
int ret;
+ bool sta = wlvif->bss_type == BSS_TYPE_STA_BSS;
+
+ /* make sure we are connected (sta) joined */
+ if (sta &&
+ !test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
+ return false;
+
+ /* make sure we are joined (ibss) */
+ if (!sta &&
+ test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))
+ return false;
+
+ if (sta) {
+ /* use defaults when not associated */
+ wlvif->aid = 0;
+
+ /* free probe-request template */
+ dev_kfree_skb(wlvif->probereq);
+ wlvif->probereq = NULL;
+
+ /* disable connection monitor features */
+ ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
+ if (ret < 0)
+ return ret;
+
+ /* Disable the keep-alive feature */
+ ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+ if (ret < 0)
+ return ret;
+ }

if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
@@ -2546,17 +2652,11 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
wlvif->sta.klv_template_id,
ACX_KEEP_ALIVE_TPL_INVALID);

- /* to stop listening to a channel, we disconnect */
- ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
- if (ret < 0)
- goto out;
-
/* reset TX security counters on a clean disconnect */
wlvif->tx_security_last_seq_lsb = 0;
wlvif->tx_security_seq = 0;

-out:
- return ret;
+ return 0;
}

static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
@@ -2565,45 +2665,6 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
wlvif->rate_set = wlvif->basic_rate_set;
}

-static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- bool idle)
-{
- int ret;
- bool cur_idle = !test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
-
- if (idle == cur_idle)
- return 0;
-
- if (idle) {
- /* no need to croc if we weren't busy (e.g. during boot) */
- if (wl12xx_dev_role_started(wlvif)) {
- ret = wl12xx_stop_dev(wl, wlvif);
- if (ret < 0)
- goto out;
- }
- wlvif->rate_set =
- wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
- ret = wl1271_acx_sta_rate_policies(wl, wlvif);
- if (ret < 0)
- goto out;
- clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
- } else {
- /* The current firmware only supports sched_scan in idle */
- if (wl->sched_scanning) {
- wl1271_scan_sched_scan_stop(wl, wlvif);
- ieee80211_sched_scan_stopped(wl->hw);
- }
-
- ret = wl12xx_start_dev(wl, wlvif);
- if (ret < 0)
- goto out;
- set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
- }
-
-out:
- return ret;
-}
-
static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_conf *conf, u32 changed)
{
@@ -3418,30 +3479,6 @@ out:
return ret;
}

-static int wl1271_ssid_set(struct ieee80211_vif *vif, struct sk_buff *skb,
- int offset)
-{
- struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
- u8 ssid_len;
- const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset,
- skb->len - offset);
-
- if (!ptr) {
- wl1271_error("No SSID in IEs!");
- return -ENOENT;
- }
-
- ssid_len = ptr[1];
- if (ssid_len > IEEE80211_MAX_SSID_LEN) {
- wl1271_error("SSID is too long!");
- return -EINVAL;
- }
-
- wlvif->ssid_len = ssid_len;
- memcpy(wlvif->ssid, ptr+2, ssid_len);
- return 0;
-}
-
static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset)
{
int len;
@@ -3622,7 +3659,7 @@ static int wlcore_set_beacon_template(struct wl1271 *wl,

wl1271_debug(DEBUG_MASTER, "beacon updated");

- ret = wl1271_ssid_set(vif, beacon, ieoffset);
+ ret = wl1271_ssid_set(wlvif, beacon, ieoffset);
if (ret < 0) {
dev_kfree_skb(beacon);
goto out;
@@ -3804,6 +3841,81 @@ out:
return;
}

+static int wlcore_set_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 sta_rate_set)
+{
+ u32 rates;
+ int ret;
+
+ wl1271_debug(DEBUG_MAC80211,
+ "changed_bssid: %pM, aid: %d, bcn_int: %d, brates: 0x%x sta_rate_set: 0x%x",
+ bss_conf->bssid, bss_conf->aid,
+ bss_conf->beacon_int,
+ bss_conf->basic_rates, sta_rate_set);
+
+ wlvif->beacon_int = bss_conf->beacon_int;
+ rates = bss_conf->basic_rates;
+ wlvif->basic_rate_set =
+ wl1271_tx_enabled_rates_get(wl, rates,
+ wlvif->band);
+ wlvif->basic_rate =
+ wl1271_tx_min_rate_get(wl,
+ wlvif->basic_rate_set);
+
+ if (sta_rate_set)
+ wlvif->rate_set =
+ wl1271_tx_enabled_rates_get(wl,
+ sta_rate_set,
+ wlvif->band);
+
+ /* we only support sched_scan while not connected */
+ if (wl->sched_scanning) {
+ wl1271_scan_sched_scan_stop(wl, wlvif);
+ ieee80211_sched_scan_stopped(wl->hw);
+ }
+
+ ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+ if (ret < 0)
+ return ret;
+
+ ret = wl12xx_cmd_build_null_data(wl, wlvif);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_build_qos_null_data(wl, wl12xx_wlvif_to_vif(wlvif));
+ if (ret < 0)
+ return ret;
+
+ wlcore_set_ssid(wl, wlvif);
+
+ set_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+
+ return 0;
+}
+
+static int wlcore_clear_bssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
+{
+ int ret;
+
+ /* revert back to minimum rates for the current band */
+ wl1271_set_band_rate(wl, wlvif);
+ wlvif->basic_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
+
+ ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+ if (ret < 0)
+ return ret;
+
+ if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
+ test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) {
+ ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
+ if (ret < 0)
+ return ret;
+ }
+
+ clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
+ return 0;
+}
/* STA/IBSS mode changes */
static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
struct ieee80211_vif *vif,
@@ -3811,7 +3923,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
u32 changed)
{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
- bool do_join = false, set_assoc = false;
+ bool do_join = false;
bool is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS);
bool ibss_joined = false;
u32 sta_rate_set = 0;
@@ -3832,9 +3944,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
set_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags);
ibss_joined = true;
} else {
- if (test_and_clear_bit(WLVIF_FLAG_IBSS_JOINED,
- &wlvif->flags))
- wl1271_unjoin(wl, wlvif);
+ wlcore_unset_assoc(wl, wlvif);
+ wl12xx_cmd_role_stop_sta(wl, wlvif);
}
}

@@ -3852,12 +3963,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
do_join = true;
}

- if (changed & BSS_CHANGED_IDLE && !is_ibss) {
- ret = wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
- if (ret < 0)
- wl1271_warning("idle mode change failed %d", ret);
- }
-
if ((changed & BSS_CHANGED_CQM)) {
bool enable = false;
if (bss_conf->cqm_rssi_thold)
@@ -3870,18 +3975,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
}

- if (changed & BSS_CHANGED_BSSID)
- if (!is_zero_ether_addr(bss_conf->bssid)) {
- ret = wl12xx_cmd_build_null_data(wl, wlvif);
- if (ret < 0)
- goto out;
-
- ret = wl1271_build_qos_null_data(wl, vif);
- if (ret < 0)
- goto out;
- }
-
- if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) {
+ if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT)) {
rcu_read_lock();
sta = ieee80211_find_sta(vif, bss_conf->bssid);
if (!sta)
@@ -3900,119 +3994,19 @@ sta_not_found:
rcu_read_unlock();
}

- if ((changed & BSS_CHANGED_ASSOC)) {
- if (bss_conf->assoc) {
- u32 rates;
- int ieoffset;
- wlvif->aid = bss_conf->aid;
- wlvif->channel_type = bss_conf->channel_type;
- wlvif->beacon_int = bss_conf->beacon_int;
- do_join = true;
- set_assoc = true;
-
- /*
- * use basic rates from AP, and determine lowest rate
- * to use with control frames.
- */
- rates = bss_conf->basic_rates;
- wlvif->basic_rate_set =
- wl1271_tx_enabled_rates_get(wl, rates,
- wlvif->band);
- wlvif->basic_rate =
- wl1271_tx_min_rate_get(wl,
- wlvif->basic_rate_set);
- if (sta_rate_set)
- wlvif->rate_set =
- wl1271_tx_enabled_rates_get(wl,
- sta_rate_set,
- wlvif->band);
- ret = wl1271_acx_sta_rate_policies(wl, wlvif);
- if (ret < 0)
- goto out;
-
- /*
- * with wl1271, we don't need to update the
- * beacon_int and dtim_period, because the firmware
- * updates it by itself when the first beacon is
- * received after a join.
- */
- ret = wl1271_cmd_build_ps_poll(wl, wlvif, wlvif->aid);
+ if (changed & BSS_CHANGED_BSSID) {
+ if (!is_zero_ether_addr(bss_conf->bssid)) {
+ ret = wlcore_set_bssid(wl, wlvif, bss_conf,
+ sta_rate_set);
if (ret < 0)
goto out;

- /*
- * Get a template for hardware connection maintenance
- */
- dev_kfree_skb(wlvif->probereq);
- wlvif->probereq = wl1271_cmd_build_ap_probe_req(wl,
- wlvif,
- NULL);
- ieoffset = offsetof(struct ieee80211_mgmt,
- u.probe_req.variable);
- wl1271_ssid_set(vif, wlvif->probereq, ieoffset);
-
- /* enable the connection monitoring feature */
- ret = wl1271_acx_conn_monit_params(wl, wlvif, true);
- if (ret < 0)
- goto out;
+ /* Need to update the BSSID (for filtering etc) */
+ do_join = true;
} else {
- /* use defaults when not associated */
- bool was_assoc =
- !!test_and_clear_bit(WLVIF_FLAG_STA_ASSOCIATED,
- &wlvif->flags);
- bool was_ifup =
- !!test_and_clear_bit(WLVIF_FLAG_STA_STATE_SENT,
- &wlvif->flags);
- wlvif->aid = 0;
-
- /* free probe-request template */
- dev_kfree_skb(wlvif->probereq);
- wlvif->probereq = NULL;
-
- /* revert back to minimum rates for the current band */
- wl1271_set_band_rate(wl, wlvif);
- wlvif->basic_rate =
- wl1271_tx_min_rate_get(wl,
- wlvif->basic_rate_set);
- ret = wl1271_acx_sta_rate_policies(wl, wlvif);
- if (ret < 0)
- goto out;
-
- /* disable connection monitor features */
- ret = wl1271_acx_conn_monit_params(wl, wlvif, false);
-
- /* Disable the keep-alive feature */
- ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
+ ret = wlcore_clear_bssid(wl, wlvif);
if (ret < 0)
goto out;
-
- /* restore the bssid filter and go to dummy bssid */
- if (was_assoc) {
- /*
- * we might have to disable roc, if there was
- * no IF_OPER_UP notification.
- */
- if (!was_ifup) {
- ret = wl12xx_croc(wl, wlvif->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(wlvif->dev_role_id, wl->roc_map)) {
- ret = wl12xx_croc(wl,
- wlvif->dev_role_id);
- if (ret < 0)
- goto out;
- }
-
- wl1271_unjoin(wl, wlvif);
- if (!bss_conf->idle)
- wl12xx_start_dev(wl, wlvif);
- }
}
}

@@ -4042,7 +4036,7 @@ sta_not_found:
goto out;

if (do_join) {
- ret = wl1271_join(wl, wlvif, set_assoc);
+ ret = wlcore_join(wl, wlvif);
if (ret < 0) {
wl1271_warning("cmd join failed %d", ret);
goto out;
@@ -4053,18 +4047,19 @@ sta_not_found:
ret = wl12xx_roc(wl, wlvif, wlvif->role_id);
if (ret < 0)
goto out;
-
- if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
- wl12xx_set_authorized(wl, wlvif);
}
- /*
- * stop device role if started (we might already be in
- * STA/IBSS role).
- */
- if (wl12xx_dev_role_started(wlvif)) {
- ret = wl12xx_stop_dev(wl, wlvif);
+ }
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ if (bss_conf->assoc) {
+ ret = wlcore_set_assoc(wl, wlvif, bss_conf);
if (ret < 0)
goto out;
+
+ if (test_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags))
+ wl12xx_set_authorized(wl, wlvif);
+ } else {
+ wlcore_unset_assoc(wl, wlvif);
}
}

@@ -4434,6 +4429,7 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
old_state == IEEE80211_STA_AUTHORIZED &&
new_state == IEEE80211_STA_ASSOC) {
clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
+ clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
return 0;
}

diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index a90d3cd..a555a70 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -155,21 +155,13 @@ static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, struct ieee80211_sta *sta)
{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-
if (!wlvif || wl12xx_is_dummy_packet(wl, skb))
return wl->system_hlid;

if (wlvif->bss_type == BSS_TYPE_AP_BSS)
return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta);

- if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
- test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
- !ieee80211_is_auth(hdr->frame_control) &&
- !ieee80211_is_assoc_req(hdr->frame_control))
- return wlvif->sta.hlid;
- else
- return wlvif->dev_hlid;
+ return wlvif->sta.hlid;
}

unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
--
1.7.6.401.g6a319


2012-11-20 11:46:14

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 05/11] wlcore: add chanctx implementation

On Tue, Nov 20, 2012 at 1:20 PM, Eliad Peller <[email protected]> wrote:
> Add some basic chanctx implementation - debug prints,
> and save the vif's channel/band/type.
>
> After that, we no longer need to handle channel change
> notifications on op_config.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> Signed-off-by: Eliad Peller <[email protected]>
> ---
> v2: take mutex, flush on unassign (thanks Arik!)
> squash patch [09/15] into this one (thanks Johannes!)
>
> drivers/net/wireless/ti/wlcore/main.c | 145 ++++++++++++++++++---------------
> 1 files changed, 79 insertions(+), 66 deletions(-)
>
> diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
> index 18ac8d1..450ff49 100644
> --- a/drivers/net/wireless/ti/wlcore/main.c
> +++ b/drivers/net/wireless/ti/wlcore/main.c
> @@ -2682,49 +2682,7 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
> struct ieee80211_conf *conf, u32 changed)
> {
> bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
> - int channel, ret;
> -
> - channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
> -
> - /* if the channel changes while joined, join again */
> - if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
> - ((wlvif->band != conf->channel->band) ||
> - (wlvif->channel != channel) ||
> - (wlvif->channel_type != conf->channel_type))) {
> - /* send all pending packets */
> - ret = wlcore_tx_work_locked(wl);
> - if (ret < 0)
> - return ret;
> -
> - wlvif->band = conf->channel->band;
> - wlvif->channel = channel;
> - wlvif->channel_type = conf->channel_type;
> -
> - if (is_ap) {
> - wl1271_set_band_rate(wl, wlvif);
> - ret = wl1271_init_ap_rates(wl, wlvif);
> - if (ret < 0)
> - wl1271_error("AP rate policy change failed %d",
> - ret);
> - } else {
> - /*
> - * FIXME: the mac80211 should really provide a fixed
> - * rate to use here. for now, just use the smallest
> - * possible rate for the band as a fixed rate for
> - * association frames and other control messages.
> - */
> - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
> - wl1271_set_band_rate(wl, wlvif);
> -
> - wlvif->basic_rate =
> - wl1271_tx_min_rate_get(wl,
> - wlvif->basic_rate_set);
> - ret = wl1271_acx_sta_rate_policies(wl, wlvif);
> - if (ret < 0)
> - wl1271_warning("rate policy for channel "
> - "failed %d", ret);
> - }
> - }
> + int ret;
>
> if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {
>
> @@ -2779,37 +2737,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
> struct wl1271 *wl = hw->priv;
> struct wl12xx_vif *wlvif;
> struct ieee80211_conf *conf = &hw->conf;
> - int channel, ret = 0;
> -
> - channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
> + int ret = 0;
>
> - wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
> + wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
> " changed 0x%x",
> - channel,
> conf->flags & IEEE80211_CONF_PS ? "on" : "off",
> conf->power_level,
> conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
> changed);
>
> - /*
> - * mac80211 will go to idle nearly immediately after transmitting some
> - * frames, such as the deauth. To make sure those frames reach the air,
> - * wait here until the TX queue is fully flushed.
> - */
> - if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
> - ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
> - (conf->flags & IEEE80211_CONF_IDLE)))
> - wl1271_tx_flush(wl);
> -
> mutex_lock(&wl->mutex);
>
> - /* we support configuring the channel and band even while off */
> - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
> - wl->band = conf->channel->band;
> - wl->channel = channel;
> - wl->channel_type = conf->channel_type;
> - }
> -
> if (changed & IEEE80211_CONF_CHANGE_POWER)
> wl->power_level = conf->power_level;
>
> @@ -3758,7 +3696,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
> struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
> int ret = 0;
>
> - if ((changed & BSS_CHANGED_BASIC_RATES)) {
> + if (changed & BSS_CHANGED_BASIC_RATES) {
> u32 rates = bss_conf->basic_rates;
>
> wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
> @@ -4184,6 +4122,76 @@ out:
> mutex_unlock(&wl->mutex);
> }
>
> +static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
> + struct ieee80211_chanctx_conf *ctx)
> +{
> + wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
> + ieee80211_frequency_to_channel(ctx->channel->center_freq),
> + ctx->channel_type);
> + return 0;
> +}
> +
> +static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
> + struct ieee80211_chanctx_conf *ctx)
> +{
> + wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
> + ieee80211_frequency_to_channel(ctx->channel->center_freq),
> + ctx->channel_type);
> +}
> +
> +static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
> + struct ieee80211_chanctx_conf *ctx,
> + u32 changed)
> +{
> + wl1271_debug(DEBUG_MAC80211,
> + "mac80211 change chanctx %d (type %d) changed 0x%x",
> + ieee80211_frequency_to_channel(ctx->channel->center_freq),
> + ctx->channel_type, changed);
> +}
> +
> +static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_chanctx_conf *ctx)
> +{
> + struct wl1271 *wl = hw->priv;
> + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
> + int channel = ieee80211_frequency_to_channel(
> + ctx->channel->center_freq);
> +
> + wl1271_debug(DEBUG_MAC80211,
> + "mac80211 assign chanctx (role %d) %d (type %d)",
> + wlvif->role_id, channel, ctx->channel_type);
> +
> + mutex_lock(&wl->mutex);
> +
> + wlvif->band = ctx->channel->band;
> + wlvif->channel = channel;
> + wlvif->channel_type = ctx->channel_type;
> +
> + /* update default rates according to the band */
> + wl1271_set_band_rate(wl, wlvif);

set_band_rate should be fixed to also set wl->basic_rate. but this can
be a separate patch..
previously we had some manual code to set the basic_rate, which you removed.

2012-11-20 11:49:22

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 00/11] wlcore: prepare to multi-channel

On Tue, Nov 20, 2012 at 1:20 PM, Eliad Peller <[email protected]> wrote:
> Make the connection flow simpler by using only the sta
> role (i.e. no need for dev role).
>
> This is possible now, as we know the AP's BSSID even
> before authentication.
>
> This means we no longer start dev role when the device
> is non-idle, so now we use the .remain_on_channel()
> and start device role only then.
>
> Finally, implement the chanctx callbacks in order to
> have a clear per-vif channel (instead of the global
> hw->channel)
>
> (this patchset depends on
> "mac80211: make remain_on_channel() op pass vif param")
>
> v2: fix some issues pointed out by Arik/Luca (thanks!)
> squash some patches together (thanks Johannes and Julian!)

You can add my reviewed-by to this patch-set, if you care about this stuff :)

Arik

2012-11-20 13:34:22

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 05/11] wlcore: add chanctx implementation

On Tue, Nov 20, 2012 at 2:04 PM, Eliad Peller <[email protected]> wrote:
> On Tue, Nov 20, 2012 at 1:45 PM, Arik Nemtsov <[email protected]> wrote:
>> On Tue, Nov 20, 2012 at 1:20 PM, Eliad Peller <[email protected]> wrote:
>>> Add some basic chanctx implementation - debug prints,
>>> and save the vif's channel/band/type.
>>>
>>> After that, we no longer need to handle channel change
>>> notifications on op_config.
>>>
>>> Signed-off-by: Arik Nemtsov <[email protected]>
>>> Signed-off-by: Eliad Peller <[email protected]>
>>> ---
>>> + /* update default rates according to the band */
>>> + wl1271_set_band_rate(wl, wlvif);
>>
>> set_band_rate should be fixed to also set wl->basic_rate. but this can
>> be a separate patch..
>> previously we had some manual code to set the basic_rate, which you removed.
>
> as i mentioned before, there is a huge mess in the rates handling.
> for sta - we set wlvif->basic_rate anyway on CHANGED_BSSID
> for ap - we don't use it at all, but use wl1271_tx_min_rate_get(wl,
> wlvif->basic_rate_set)
>
> so it's not really an issue.
> i think we should handle all this in a separate patch.

I agree. I'm not even sure the per-band rates are used right now, I
think we're just reverting to some default rates.

2012-11-20 11:20:19

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 05/11] wlcore: add chanctx implementation

Add some basic chanctx implementation - debug prints,
and save the vif's channel/band/type.

After that, we no longer need to handle channel change
notifications on op_config.

Signed-off-by: Arik Nemtsov <[email protected]>
Signed-off-by: Eliad Peller <[email protected]>
---
v2: take mutex, flush on unassign (thanks Arik!)
squash patch [09/15] into this one (thanks Johannes!)

drivers/net/wireless/ti/wlcore/main.c | 145 ++++++++++++++++++---------------
1 files changed, 79 insertions(+), 66 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 18ac8d1..450ff49 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2682,49 +2682,7 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct ieee80211_conf *conf, u32 changed)
{
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);
- int channel, ret;
-
- channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
-
- /* if the channel changes while joined, join again */
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL &&
- ((wlvif->band != conf->channel->band) ||
- (wlvif->channel != channel) ||
- (wlvif->channel_type != conf->channel_type))) {
- /* send all pending packets */
- ret = wlcore_tx_work_locked(wl);
- if (ret < 0)
- return ret;
-
- wlvif->band = conf->channel->band;
- wlvif->channel = channel;
- wlvif->channel_type = conf->channel_type;
-
- if (is_ap) {
- wl1271_set_band_rate(wl, wlvif);
- ret = wl1271_init_ap_rates(wl, wlvif);
- if (ret < 0)
- wl1271_error("AP rate policy change failed %d",
- ret);
- } else {
- /*
- * FIXME: the mac80211 should really provide a fixed
- * rate to use here. for now, just use the smallest
- * possible rate for the band as a fixed rate for
- * association frames and other control messages.
- */
- if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
- wl1271_set_band_rate(wl, wlvif);
-
- wlvif->basic_rate =
- wl1271_tx_min_rate_get(wl,
- wlvif->basic_rate_set);
- ret = wl1271_acx_sta_rate_policies(wl, wlvif);
- if (ret < 0)
- wl1271_warning("rate policy for channel "
- "failed %d", ret);
- }
- }
+ int ret;

if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) {

@@ -2779,37 +2737,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif;
struct ieee80211_conf *conf = &hw->conf;
- int channel, ret = 0;
-
- channel = ieee80211_frequency_to_channel(conf->channel->center_freq);
+ int ret = 0;

- wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
+ wl1271_debug(DEBUG_MAC80211, "mac80211 config psm %s power %d %s"
" changed 0x%x",
- channel,
conf->flags & IEEE80211_CONF_PS ? "on" : "off",
conf->power_level,
conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
changed);

- /*
- * mac80211 will go to idle nearly immediately after transmitting some
- * frames, such as the deauth. To make sure those frames reach the air,
- * wait here until the TX queue is fully flushed.
- */
- if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) ||
- ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
- (conf->flags & IEEE80211_CONF_IDLE)))
- wl1271_tx_flush(wl);
-
mutex_lock(&wl->mutex);

- /* we support configuring the channel and band even while off */
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- wl->band = conf->channel->band;
- wl->channel = channel;
- wl->channel_type = conf->channel_type;
- }
-
if (changed & IEEE80211_CONF_CHANGE_POWER)
wl->power_level = conf->power_level;

@@ -3758,7 +3696,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret = 0;

- if ((changed & BSS_CHANGED_BASIC_RATES)) {
+ if (changed & BSS_CHANGED_BASIC_RATES) {
u32 rates = bss_conf->basic_rates;

wlvif->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates,
@@ -4184,6 +4122,76 @@ out:
mutex_unlock(&wl->mutex);
}

+static int wlcore_op_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ wl1271_debug(DEBUG_MAC80211, "mac80211 add chanctx %d (type %d)",
+ ieee80211_frequency_to_channel(ctx->channel->center_freq),
+ ctx->channel_type);
+ return 0;
+}
+
+static void wlcore_op_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ wl1271_debug(DEBUG_MAC80211, "mac80211 remove chanctx %d (type %d)",
+ ieee80211_frequency_to_channel(ctx->channel->center_freq),
+ ctx->channel_type);
+}
+
+static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx,
+ u32 changed)
+{
+ wl1271_debug(DEBUG_MAC80211,
+ "mac80211 change chanctx %d (type %d) changed 0x%x",
+ ieee80211_frequency_to_channel(ctx->channel->center_freq),
+ ctx->channel_type, changed);
+}
+
+static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct wl1271 *wl = hw->priv;
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+ int channel = ieee80211_frequency_to_channel(
+ ctx->channel->center_freq);
+
+ wl1271_debug(DEBUG_MAC80211,
+ "mac80211 assign chanctx (role %d) %d (type %d)",
+ wlvif->role_id, channel, ctx->channel_type);
+
+ mutex_lock(&wl->mutex);
+
+ wlvif->band = ctx->channel->band;
+ wlvif->channel = channel;
+ wlvif->channel_type = ctx->channel_type;
+
+ /* update default rates according to the band */
+ wl1271_set_band_rate(wl, wlvif);
+
+ mutex_unlock(&wl->mutex);
+
+ return 0;
+}
+
+static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct wl1271 *wl = hw->priv;
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+
+ wl1271_debug(DEBUG_MAC80211,
+ "mac80211 unassign chanctx (role %d) %d (type %d)",
+ wlvif->role_id,
+ ieee80211_frequency_to_channel(ctx->channel->center_freq),
+ ctx->channel_type);
+
+ wl1271_tx_flush(wl);
+}
+
static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u16 queue,
const struct ieee80211_tx_queue_params *params)
@@ -4996,6 +5004,11 @@ static const struct ieee80211_ops wl1271_ops = {
.flush = wlcore_op_flush,
.remain_on_channel = wlcore_op_remain_on_channel,
.cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel,
+ .add_chanctx = wlcore_op_add_chanctx,
+ .remove_chanctx = wlcore_op_remove_chanctx,
+ .change_chanctx = wlcore_op_change_chanctx,
+ .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
+ .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
};

--
1.7.6.401.g6a319


2012-11-20 11:20:31

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 11/11] wlcore: configure the remote rates with our own rates

With the new connection flow, start_sta is called before
the remote rates where updated. Use our own supported rates
instead to make sure we don't disable any potential rate
(the rate policies will be updated later, but there is
currently no way to update the remote rates)

Signed-off-by: Eliad Peller <[email protected]>
---
drivers/net/wireless/ti/wlcore/cmd.c | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 1cf1225..04ba86d 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -479,7 +479,12 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
}
cmd->sta.hlid = wlvif->sta.hlid;
cmd->sta.session = wl12xx_get_new_session_id(wl, wlvif);
- cmd->sta.remote_rates = cpu_to_le32(wlvif->rate_set);
+ /*
+ * We don't have the correct remote rates in this stage, and there
+ * is no way to update them later, so use our supported rates instead.
+ * The fw will take the configured rate policies into account anyway.
+ */
+ cmd->sta.remote_rates = cpu_to_le32(supported_rates);

wl1271_debug(DEBUG_CMD, "role start: roleid=%d, hlid=%d, session=%d "
"basic_rate_set: 0x%x, remote_rates: 0x%x",
--
1.7.6.401.g6a319


2012-11-20 11:57:52

by Eliad Peller

[permalink] [raw]
Subject: Re: [PATCH v2 01/11] wlcore: start sta role on CHANGED_BSSID

On Tue, Nov 20, 2012 at 1:42 PM, Luciano Coelho <[email protected]> wrote:
> On Tue, 2012-11-20 at 13:20 +0200, Eliad Peller wrote:
>> Make the connection flow simpler by starting
>> sta role on bssid change.
>>
>> Currently, we start dev role when going idle-off,
>> and start the sta role only after association
>> indication. This complicates the connection
>> flow with some possible intermediate states.
>>
>> Make it simpler by starting sta role on bssid change,
>> which now happens *before* auth req get sent.
>>
>> Update the handling of mac80211's notifications
>> and change wl1271_join/unjoin accordingly -
>> * Split wl1271_join() into wlcore_join (tuning on
>> a channel/bssid) and wlcore_set_assoc (configure
>> sta after association).
>> * Rename wl1271_unjoin() to wlcore_unset_assoc(), as
>> it is no longer the inversion of wl1271_join()
>> (now it's only used to disconnect associated sta /
>> joined ibss, without stopping the role).
>> * Set ssid before starting station role (needed for
>> start_role(sta)
>>
>> While on it, split wl1271_bss_info_changed_sta() into
>> some sub-functions.
>>
>> since we no longer use dev role in the connection flow,
>> we now always use the hlid of the sta role.
>>
>> Signed-off-by: Eliad Peller <[email protected]>
>> ---
>> v2: take some more code out of wl1271_bss_info_changed_sta (thanks Luca!)
>> squash patches [2/15, 3/15] into this one (thanks Julian!)
>>
>
> [...]
>
>> diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
>> index 41ed1d5..2690fe9 100644
>> --- a/drivers/net/wireless/ti/wlcore/main.c
>> +++ b/drivers/net/wireless/ti/wlcore/main.c
>
> [...]
>
>> @@ -2510,29 +2587,58 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
>> */
>> ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
>> if (ret < 0)
>> - goto out;
>> + return ret;
>>
>> ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
>> if (ret < 0)
>> - goto out;
>> + return ret;
>>
>> ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
>> if (ret < 0)
>> - goto out;
>> + return ret;
>>
>> ret = wl1271_acx_keep_alive_config(wl, wlvif,
>> wlvif->sta.klv_template_id,
>> ACX_KEEP_ALIVE_TPL_VALID);
>> if (ret < 0)
>> - goto out;
>> + return ret;
>>
>> -out:
>> return ret;
>> }
>
> This is, of course, functionally okay, but seems like an unrelated
> change. Also, this is different than the general style we use in the
> driver (ie. use goto out in most error cases).
>
> Same thing for the other functions you added. It would be nice to be
> consistent with the existing style, at least in error paths.
>
AFAICT, a major part of the functions in the driver use the "goto out"
only when there's cleanup work to do.
this is also the convention in mac80211.

but then again, i don't mind changing it if that's your preference.

Eliad.

2012-11-20 11:20:25

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 08/11] wlcore: specify correct supported_rates

The supported_rates field should contain all our supported
rates, even if the remote peer doesn't support them.

(rename CONF_TX_AP_ENABLED_RATES to CONF_TX_ENABLED_RATES,
as we now use it for both ap and sta)

Signed-off-by: Eliad Peller <[email protected]>
---
v2: rename CONF_TX_AP_ENABLED_RATES (thanks Luca!)

drivers/net/wireless/ti/wlcore/cmd.c | 12 ++++++++++--
drivers/net/wireless/ti/wlcore/conf.h | 4 ++--
drivers/net/wireless/ti/wlcore/init.c | 2 +-
drivers/net/wireless/ti/wlcore/main.c | 6 +++---
4 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 38fa8ff..1cf1225 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -441,6 +441,7 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct wl12xx_cmd_role_start *cmd;
+ u32 supported_rates;
int ret;

cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -461,7 +462,14 @@ int wl12xx_cmd_role_start_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
cmd->sta.ssid_len = wlvif->ssid_len;
memcpy(cmd->sta.ssid, wlvif->ssid, wlvif->ssid_len);
memcpy(cmd->sta.bssid, vif->bss_conf.bssid, ETH_ALEN);
- cmd->sta.local_rates = cpu_to_le32(wlvif->rate_set);
+
+ supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
+ wlcore_hw_sta_get_ap_rate_mask(wl, wlvif);
+ if (wlvif->p2p)
+ supported_rates &= ~CONF_TX_CCK_RATES;
+
+ cmd->sta.local_rates = cpu_to_le32(supported_rates);
+
cmd->channel_type = wlcore_get_native_channel_type(wlvif->channel_type);

if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
@@ -601,7 +609,7 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif)
memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len);
}

- supported_rates = CONF_TX_AP_ENABLED_RATES | CONF_TX_MCS_RATES |
+ supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES |
wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif);

wl1271_debug(DEBUG_CMD, "cmd role start ap with supported_rates 0x%08x",
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index 9e40760..a5d1908 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -415,11 +415,11 @@ struct conf_rx_settings {
#define CONF_TX_RATE_MASK_BASIC_P2P CONF_HW_BIT_RATE_6MBPS

/*
- * Rates supported for data packets when operating as AP. Note the absence
+ * Rates supported for data packets when operating as STA/AP. Note the absence
* of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop
* one. The rate dropped is not mandatory under any operating mode.
*/
-#define CONF_TX_AP_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \
+#define CONF_TX_ENABLED_RATES (CONF_HW_BIT_RATE_1MBPS | \
CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \
CONF_HW_BIT_RATE_6MBPS | CONF_HW_BIT_RATE_9MBPS | \
CONF_HW_BIT_RATE_11MBPS | CONF_HW_BIT_RATE_12MBPS | \
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
index 32d157f..84641b3 100644
--- a/drivers/net/wireless/ti/wlcore/init.c
+++ b/drivers/net/wireless/ti/wlcore/init.c
@@ -463,7 +463,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
supported_rates = CONF_TX_OFDM_RATES;
else
- supported_rates = CONF_TX_AP_ENABLED_RATES;
+ supported_rates = CONF_TX_ENABLED_RATES;

/* unconditionally enable HT rates */
supported_rates |= CONF_TX_MCS_RATES;
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index e393cea..6170e9e 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2035,15 +2035,15 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
for (i = 0; i < CONF_TX_MAX_AC_COUNT; i++)
wl12xx_allocate_rate_policy(wl,
&wlvif->ap.ucast_rate_idx[i]);
- wlvif->basic_rate_set = CONF_TX_AP_ENABLED_RATES;
+ wlvif->basic_rate_set = CONF_TX_ENABLED_RATES;
/*
* TODO: check if basic_rate shouldn't be
* wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
* instead (the same thing for STA above).
*/
- wlvif->basic_rate = CONF_TX_AP_ENABLED_RATES;
+ wlvif->basic_rate = CONF_TX_ENABLED_RATES;
/* TODO: this seems to be used only for STA, check it */
- wlvif->rate_set = CONF_TX_AP_ENABLED_RATES;
+ wlvif->rate_set = CONF_TX_ENABLED_RATES;
}

wlvif->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate;
--
1.7.6.401.g6a319


2012-11-20 11:20:13

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 02/11] wlcore: workaround start_sta problem in wl12xx fw

for some reason, the wl12xx fw is not able to rx/tx
on the first start_sta cmd.
Workaround it by issuing a dummy start_sta + stop_sta
before starting the sta for the final time.

Signed-off-by: Eliad Peller <[email protected]>
---
drivers/net/wireless/ti/wl12xx/main.c | 9 ++++++---
drivers/net/wireless/ti/wlcore/main.c | 15 ++++++++++++++-
drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++
3 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index dadf1db..6c9fba4 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -637,7 +637,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)

wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
WLCORE_QUIRK_DUAL_PROBE_TMPL |
- WLCORE_QUIRK_TKIP_HEADER_SPACE;
+ WLCORE_QUIRK_TKIP_HEADER_SPACE |
+ WLCORE_QUIRK_START_STA_FAILS;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI;
memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -657,7 +658,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)

wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
WLCORE_QUIRK_DUAL_PROBE_TMPL |
- WLCORE_QUIRK_TKIP_HEADER_SPACE;
+ WLCORE_QUIRK_TKIP_HEADER_SPACE |
+ WLCORE_QUIRK_START_STA_FAILS;
wl->plt_fw_name = WL127X_PLT_FW_NAME;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -682,7 +684,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
/* wl128x requires TX blocksize alignment */
wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_DUAL_PROBE_TMPL |
- WLCORE_QUIRK_TKIP_HEADER_SPACE;
+ WLCORE_QUIRK_TKIP_HEADER_SPACE |
+ WLCORE_QUIRK_START_STA_FAILS;

wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER,
WL128X_MAJOR_VER, WL128X_SUBTYPE_VER,
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 2690fe9..be60394 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2490,8 +2490,21 @@ static int wlcore_join(struct wl1271 *wl, struct wl12xx_vif *wlvif)

if (is_ibss)
ret = wl12xx_cmd_role_start_ibss(wl, wlvif);
- else
+ else {
+ if (wl->quirks & WLCORE_QUIRK_START_STA_FAILS) {
+ /*
+ * TODO: this is an ugly workaround for wl12xx fw
+ * bug - we are not able to tx/rx after the first
+ * start_sta, so make dummy start+stop calls,
+ * and then call start_sta again.
+ * this should be fixed in the fw.
+ */
+ wl12xx_cmd_role_start_sta(wl, wlvif);
+ wl12xx_cmd_role_stop_sta(wl, wlvif);
+ }
+
ret = wl12xx_cmd_role_start_sta(wl, wlvif);
+ }

return ret;
}
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 68584aa..1030b6b 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -450,6 +450,9 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
/* Each RX/TX transaction requires an end-of-transaction transfer */
#define WLCORE_QUIRK_END_OF_TRANSACTION BIT(0)

+/* the first start_role(sta) sometimes doesn't work on wl12xx */
+#define WLCORE_QUIRK_START_STA_FAILS BIT(1)
+
/* wl127x and SPI don't support SDIO block size alignment */
#define WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN BIT(2)

--
1.7.6.401.g6a319


2012-11-20 11:20:27

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 09/11] wlcore: reconfigure rate policy on association

When first configuring the rate policy, before auth,
we still don't have the correct rates that were
agreed during association.

Reconfigure the rate policy on association in order
to update them.

Signed-off-by: Eliad Peller <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 21 ++++++++++++++++++---
1 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 6170e9e..ade206a 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2553,7 +2553,8 @@ static int wlcore_set_ssid(struct wl1271 *wl, struct wl12xx_vif *wlvif)
}

static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- struct ieee80211_bss_conf *bss_conf)
+ struct ieee80211_bss_conf *bss_conf,
+ u32 sta_rate_set)
{
int ieoffset;
int ret;
@@ -2619,6 +2620,18 @@ static int wlcore_set_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif,
* setting is off (ACTIVE), so sync the fw with the correct value.
*/
ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE);
+ if (ret < 0)
+ return ret;
+
+ if (sta_rate_set) {
+ wlvif->rate_set =
+ wl1271_tx_enabled_rates_get(wl,
+ sta_rate_set,
+ wlvif->band);
+ ret = wl1271_acx_sta_rate_policies(wl, wlvif);
+ if (ret < 0)
+ return ret;
+ }

return ret;
}
@@ -3912,7 +3925,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
wlvif->rssi_thold = bss_conf->cqm_rssi_thold;
}

- if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT)) {
+ if (changed & (BSS_CHANGED_BSSID | BSS_CHANGED_HT |
+ BSS_CHANGED_ASSOC)) {
rcu_read_lock();
sta = ieee80211_find_sta(vif, bss_conf->bssid);
if (!sta)
@@ -3982,7 +3996,8 @@ sta_not_found:

if (changed & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
- ret = wlcore_set_assoc(wl, wlvif, bss_conf);
+ ret = wlcore_set_assoc(wl, wlvif, bss_conf,
+ sta_rate_set);
if (ret < 0)
goto out;

--
1.7.6.401.g6a319


2012-11-20 12:11:56

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 01/11] wlcore: start sta role on CHANGED_BSSID

On Tue, 2012-11-20 at 13:57 +0200, Eliad Peller wrote:
> On Tue, Nov 20, 2012 at 1:42 PM, Luciano Coelho <[email protected]> wrote:
> > On Tue, 2012-11-20 at 13:20 +0200, Eliad Peller wrote:
> >> if (ret < 0)
> >> - goto out;
> >> + return ret;
> >>
> >> -out:
> >> return ret;
> >> }
> >
> > This is, of course, functionally okay, but seems like an unrelated
> > change. Also, this is different than the general style we use in the
> > driver (ie. use goto out in most error cases).
> >
> > Same thing for the other functions you added. It would be nice to be
> > consistent with the existing style, at least in error paths.
> >
> AFAICT, a major part of the functions in the driver use the "goto out"
> only when there's cleanup work to do.
> this is also the convention in mac80211.
>
> but then again, i don't mind changing it if that's your preference.

Nah, never mind. I noticed this is now different everywhere, no need to
bother.

--
Luca.


2012-11-20 11:42:44

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 01/11] wlcore: start sta role on CHANGED_BSSID

On Tue, 2012-11-20 at 13:20 +0200, Eliad Peller wrote:
> Make the connection flow simpler by starting
> sta role on bssid change.
>
> Currently, we start dev role when going idle-off,
> and start the sta role only after association
> indication. This complicates the connection
> flow with some possible intermediate states.
>
> Make it simpler by starting sta role on bssid change,
> which now happens *before* auth req get sent.
>
> Update the handling of mac80211's notifications
> and change wl1271_join/unjoin accordingly -
> * Split wl1271_join() into wlcore_join (tuning on
> a channel/bssid) and wlcore_set_assoc (configure
> sta after association).
> * Rename wl1271_unjoin() to wlcore_unset_assoc(), as
> it is no longer the inversion of wl1271_join()
> (now it's only used to disconnect associated sta /
> joined ibss, without stopping the role).
> * Set ssid before starting station role (needed for
> start_role(sta)
>
> While on it, split wl1271_bss_info_changed_sta() into
> some sub-functions.
>
> since we no longer use dev role in the connection flow,
> we now always use the hlid of the sta role.
>
> Signed-off-by: Eliad Peller <[email protected]>
> ---
> v2: take some more code out of wl1271_bss_info_changed_sta (thanks Luca!)
> squash patches [2/15, 3/15] into this one (thanks Julian!)
>

[...]

> diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
> index 41ed1d5..2690fe9 100644
> --- a/drivers/net/wireless/ti/wlcore/main.c
> +++ b/drivers/net/wireless/ti/wlcore/main.c

[...]

> @@ -2510,29 +2587,58 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
> */
> ret = wl1271_acx_keep_alive_mode(wl, wlvif, true);
> if (ret < 0)
> - goto out;
> + return ret;
>
> ret = wl1271_acx_aid(wl, wlvif, wlvif->aid);
> if (ret < 0)
> - goto out;
> + return ret;
>
> ret = wl12xx_cmd_build_klv_null_data(wl, wlvif);
> if (ret < 0)
> - goto out;
> + return ret;
>
> ret = wl1271_acx_keep_alive_config(wl, wlvif,
> wlvif->sta.klv_template_id,
> ACX_KEEP_ALIVE_TPL_VALID);
> if (ret < 0)
> - goto out;
> + return ret;
>
> -out:
> return ret;
> }

This is, of course, functionally okay, but seems like an unrelated
change. Also, this is different than the general style we use in the
driver (ie. use goto out in most error cases).

Same thing for the other functions you added. It would be nice to be
consistent with the existing style, at least in error paths.

--
Luca.


2012-11-20 12:04:36

by Eliad Peller

[permalink] [raw]
Subject: Re: [PATCH v2 05/11] wlcore: add chanctx implementation

On Tue, Nov 20, 2012 at 1:45 PM, Arik Nemtsov <[email protected]> wrote:
> On Tue, Nov 20, 2012 at 1:20 PM, Eliad Peller <[email protected]> wrote:
>> Add some basic chanctx implementation - debug prints,
>> and save the vif's channel/band/type.
>>
>> After that, we no longer need to handle channel change
>> notifications on op_config.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> Signed-off-by: Eliad Peller <[email protected]>
>> ---
>> + /* update default rates according to the band */
>> + wl1271_set_band_rate(wl, wlvif);
>
> set_band_rate should be fixed to also set wl->basic_rate. but this can
> be a separate patch..
> previously we had some manual code to set the basic_rate, which you removed.

as i mentioned before, there is a huge mess in the rates handling.
for sta - we set wlvif->basic_rate anyway on CHANGED_BSSID
for ap - we don't use it at all, but use wl1271_tx_min_rate_get(wl,
wlvif->basic_rate_set)

so it's not really an issue.
i think we should handle all this in a separate patch.

Eliad.

2012-11-28 09:12:36

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 00/11] wlcore: prepare to multi-channel

On Tue, 2012-11-20 at 13:20 +0200, Eliad Peller wrote:
> Make the connection flow simpler by using only the sta
> role (i.e. no need for dev role).
>
> This is possible now, as we know the AP's BSSID even
> before authentication.
>
> This means we no longer start dev role when the device
> is non-idle, so now we use the .remain_on_channel()
> and start device role only then.
>
> Finally, implement the chanctx callbacks in order to
> have a clear per-vif channel (instead of the global
> hw->channel)
>
> (this patchset depends on
> "mac80211: make remain_on_channel() op pass vif param")
>
> v2: fix some issues pointed out by Arik/Luca (thanks!)
> squash some patches together (thanks Johannes and Julian!)
>
> Eliad Peller (11):
> wlcore: start sta role on CHANGED_BSSID
> wlcore: workaround start_sta problem in wl12xx fw
> wlcore: implement .remain_on_channel() callback
> wlcore: get channel from bss_conf instead of hw->conf
> wlcore: add chanctx implementation
> wlcore: initiate ROC/CROC on sta state updates
> wlcore: set active psm on association
> wlcore: specify correct supported_rates
> wlcore: reconfigure rate policy on association
> wlcore: refactor CHANGED_HT handling
> wlcore: configure the remote rates with our own rates

Applied the whole series, thanks!

--
Luca.


2012-11-20 11:20:17

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 04/11] wlcore: get channel from bss_conf instead of hw->conf

We care only about the operational channel, not
about the temporal hw channel (which won't have
any real meaning in multi-channel env anyway)

Signed-off-by: Eliad Peller <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index e9a8092..18ac8d1 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -3977,7 +3977,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
goto sta_not_found;

/* save the supp_rates of the ap */
- sta_rate_set = sta->supp_rates[wl->hw->conf.channel->band];
+ sta_rate_set = sta->supp_rates[wlvif->band];
if (sta->ht_cap.ht_supported)
sta_rate_set |=
(sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET) |
--
1.7.6.401.g6a319


2012-11-20 11:20:29

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 10/11] wlcore: refactor CHANGED_HT handling

Pass a variable indicating whether HT is enabled,
instead of duplicating the function call with
different arguments.

Signed-off-by: Eliad Peller <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 48 ++++++++++++--------------------
1 files changed, 18 insertions(+), 30 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index ade206a..ff9437a 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -4009,44 +4009,32 @@ sta_not_found:
}

/* Handle new association with HT. Do this after join. */
- if (sta_exists) {
- if ((changed & BSS_CHANGED_HT) &&
- (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
- ret = wl1271_acx_set_ht_capabilities(wl,
- &sta_ht_cap,
- true,
- wlvif->sta.hlid);
- if (ret < 0) {
- wl1271_warning("Set ht cap true failed %d",
- ret);
- goto out;
- }
+ if (sta_exists &&
+ (changed & BSS_CHANGED_HT)) {
+ bool enabled =
+ bss_conf->channel_type != NL80211_CHAN_NO_HT;
+
+ ret = wl1271_acx_set_ht_capabilities(wl,
+ &sta_ht_cap,
+ enabled,
+ wlvif->sta.hlid);
+ if (ret < 0) {
+ wl1271_warning("Set ht cap failed %d", ret);
+ goto out;
+
}
- /* handle new association without HT and disassociation */
- else if (changed & BSS_CHANGED_ASSOC) {
- ret = wl1271_acx_set_ht_capabilities(wl,
- &sta_ht_cap,
- false,
- wlvif->sta.hlid);
+
+ if (enabled) {
+ ret = wl1271_acx_set_ht_information(wl, wlvif,
+ bss_conf->ht_operation_mode);
if (ret < 0) {
- wl1271_warning("Set ht cap false failed %d",
+ wl1271_warning("Set ht information failed %d",
ret);
goto out;
}
}
}

- /* Handle HT information change. Done after join. */
- if ((changed & BSS_CHANGED_HT) &&
- (bss_conf->channel_type != NL80211_CHAN_NO_HT)) {
- ret = wl1271_acx_set_ht_information(wl, wlvif,
- bss_conf->ht_operation_mode);
- if (ret < 0) {
- wl1271_warning("Set ht information failed %d", ret);
- goto out;
- }
- }
-
/* Handle arp filtering. Done after join. */
if ((changed & BSS_CHANGED_ARP_FILTER) ||
(!is_ibss && (changed & BSS_CHANGED_QOS))) {
--
1.7.6.401.g6a319


2012-11-20 11:20:21

by Eliad Peller

[permalink] [raw]
Subject: [PATCH v2 06/11] wlcore: initiate ROC/CROC on sta state updates

Use the sta_state notifications to ROC when a station
is about to connect, and CROC respectively on
authorization (success) / deletion (failure).

Change the wl12xx_update_sta_state() flow to bail out
only on error, so multiple code blocks could refer
to the same state.

Signed-off-by: Eliad Peller <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 44 +++++++++++++++++++++------------
1 files changed, 28 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 450ff49..55dce4a 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -83,8 +83,6 @@ static int wl12xx_set_authorized(struct wl1271 *wl,
if (ret < 0)
return ret;

- wl12xx_croc(wl, wlvif->role_id);
-
wl1271_info("Association completed.");
return 0;
}
@@ -3974,14 +3972,6 @@ sta_not_found:
wl1271_warning("cmd join failed %d", ret);
goto out;
}
-
- /* ROC until connected (after EAPOL exchange) */
- if (!is_ibss) {
- ret = wl12xx_roc(wl, wlvif, wlvif->role_id,
- wlvif->band, wlvif->channel);
- if (ret < 0)
- goto out;
- }
}

if (changed & BSS_CHANGED_ASSOC) {
@@ -4398,8 +4388,11 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
/* Add station (AP mode) */
if (is_ap &&
old_state == IEEE80211_STA_NOTEXIST &&
- new_state == IEEE80211_STA_NONE)
- return wl12xx_sta_add(wl, wlvif, sta);
+ new_state == IEEE80211_STA_NONE) {
+ ret = wl12xx_sta_add(wl, wlvif, sta);
+ if (ret)
+ return ret;
+ }

/* Remove station (AP mode) */
if (is_ap &&
@@ -4407,7 +4400,6 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
new_state == IEEE80211_STA_NOTEXIST) {
/* must not fail */
wl12xx_sta_remove(wl, wlvif, sta);
- return 0;
}

/* Authorize station (AP mode) */
@@ -4419,14 +4411,17 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,

ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, true,
hlid);
- return ret;
+ if (ret)
+ return ret;
}

/* Authorize station */
if (is_sta &&
new_state == IEEE80211_STA_AUTHORIZED) {
set_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
- return wl12xx_set_authorized(wl, wlvif);
+ ret = wl12xx_set_authorized(wl, wlvif);
+ if (ret)
+ return ret;
}

if (is_sta &&
@@ -4434,9 +4429,26 @@ static int wl12xx_update_sta_state(struct wl1271 *wl,
new_state == IEEE80211_STA_ASSOC) {
clear_bit(WLVIF_FLAG_STA_AUTHORIZED, &wlvif->flags);
clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags);
- return 0;
}

+ /* clear ROCs on failure or authorization */
+ if (is_sta &&
+ (new_state == IEEE80211_STA_AUTHORIZED ||
+ new_state == IEEE80211_STA_NOTEXIST)) {
+ if (test_bit(wlvif->role_id, wl->roc_map))
+ wl12xx_croc(wl, wlvif->role_id);
+ }
+
+ if (is_sta &&
+ old_state == IEEE80211_STA_NOTEXIST &&
+ new_state == IEEE80211_STA_NONE) {
+ if (find_first_bit(wl->roc_map,
+ WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES) {
+ WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID);
+ wl12xx_roc(wl, wlvif, wlvif->role_id,
+ wlvif->band, wlvif->channel);
+ }
+ }
return 0;
}

--
1.7.6.401.g6a319