Skip to content

Commit

Permalink
wl12xx: Add support for HW channel switch
Browse files Browse the repository at this point in the history
The wl12xx FW supports HW channel switch.  If we don't use it,
sometimes the firmware gets confused when recalibrating to the new
channel, causing RX problems.  This commit adds HW channel switch
support by implementing the channell_switch op.

Signed-off-by: Shahar Levi <shahar_levi@ti.com>
[added one comment, remove the tx_flush and rephrased the commit message]
Signed-off-by: Luciano Coelho <coelho@ti.com>
  • Loading branch information
Shahar Levi authored and Luciano Coelho committed Oct 7, 2011
1 parent c9e79a4 commit 6d158ff
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 1 deletion.
3 changes: 2 additions & 1 deletion drivers/net/wireless/wl12xx/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
BA_SESSION_RX_CONSTRAINT_EVENT_ID |
REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
INACTIVE_STA_EVENT_ID |
MAX_TX_RETRY_EVENT_ID;
MAX_TX_RETRY_EVENT_ID |
CHANNEL_SWITCH_COMPLETE_EVENT_ID;

ret = wl1271_event_unmask(wl);
if (ret < 0) {
Expand Down
58 changes: 58 additions & 0 deletions drivers/net/wireless/wl12xx/cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1700,3 +1700,61 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id)
out:
return ret;
}

int wl12xx_cmd_channel_switch(struct wl1271 *wl,
struct ieee80211_channel_switch *ch_switch)
{
struct wl12xx_cmd_channel_switch *cmd;
int ret;

wl1271_debug(DEBUG_ACX, "cmd channel switch");

cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}

cmd->channel = ch_switch->channel->hw_value;
cmd->switch_time = ch_switch->count;
cmd->tx_suspend = ch_switch->block_tx;
cmd->flush = 0; /* this value is ignored by the FW */

ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to send channel switch command");
goto out_free;
}

out_free:
kfree(cmd);

out:
return ret;
}

int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
{
struct wl12xx_cmd_stop_channel_switch *cmd;
int ret;

wl1271_debug(DEBUG_ACX, "cmd stop channel switch");

cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto out;
}

ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("failed to stop channel switch command");
goto out_free;
}

out_free:
kfree(cmd);

out:
return ret;
}
20 changes: 20 additions & 0 deletions drivers/net/wireless/wl12xx/cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
int wl12xx_cmd_channel_switch(struct wl1271 *wl,
struct ieee80211_channel_switch *ch_switch);
int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl);

enum wl1271_commands {
CMD_INTERROGATE = 1, /*use this to read information elements*/
Expand Down Expand Up @@ -677,4 +680,21 @@ struct wl12xx_cmd_stop_fwlog {
struct wl1271_cmd_header header;
} __packed;

struct wl12xx_cmd_channel_switch {
struct wl1271_cmd_header header;

/* The new serving channel */
u8 channel;
/* Relative time of the serving channel switch in TBTT units */
u8 switch_time;
/* 1: Suspend TX till switch time; 0: Do not suspend TX */
u8 tx_suspend;
/* 1: Flush TX at switch time; 0: Do not flush */
u8 flush;
} __packed;

struct wl12xx_cmd_stop_channel_switch {
struct wl1271_cmd_header header;
} __packed;

#endif /* __WL1271_CMD_H__ */
15 changes: 15 additions & 0 deletions drivers/net/wireless/wl12xx/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,21 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
wl1271_stop_ba_event(wl);
}

if ((vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) && !is_ap) {
wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
"status = 0x%x",
mbox->channel_switch_status);
/*
* That event uses for two cases:
* 1) channel switch complete with status=0
* 2) channel switch failed status=1
*/
if (test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags) &&
(wl->vif))
ieee80211_chswitch_done(wl->vif,
mbox->channel_switch_status ? false : true);
}

if ((vector & DUMMY_PACKET_EVENT_ID)) {
wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
if (wl->vif)
Expand Down
37 changes: 37 additions & 0 deletions drivers/net/wireless/wl12xx/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,11 @@ static int wl1271_unjoin(struct wl1271 *wl)
{
int ret;

if (test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags)) {
wl12xx_cmd_stop_channel_switch(wl);
ieee80211_chswitch_done(wl->vif, false);
}

/* to stop listening to a channel, we disconnect */
ret = wl12xx_cmd_role_stop_sta(wl);
if (ret < 0)
Expand Down Expand Up @@ -4130,6 +4135,37 @@ static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
return 0;
}

static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
struct ieee80211_channel_switch *ch_switch)
{
struct wl1271 *wl = hw->priv;
int ret;

wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");

mutex_lock(&wl->mutex);

if (unlikely(wl->state == WL1271_STATE_OFF)) {
mutex_unlock(&wl->mutex);
ieee80211_chswitch_done(wl->vif, false);
return;
}

ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0)
goto out;

ret = wl12xx_cmd_channel_switch(wl, ch_switch);

if (!ret)
set_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags);

wl1271_ps_elp_sleep(wl);

out:
mutex_unlock(&wl->mutex);
}

static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
{
struct wl1271 *wl = hw->priv;
Expand Down Expand Up @@ -4406,6 +4442,7 @@ static const struct ieee80211_ops wl1271_ops = {
.ampdu_action = wl1271_op_ampdu_action,
.tx_frames_pending = wl1271_tx_frames_pending,
.set_bitrate_mask = wl12xx_set_bitrate_mask,
.channel_switch = wl12xx_op_channel_switch,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
};

Expand Down
1 change: 1 addition & 0 deletions drivers/net/wireless/wl12xx/wl12xx.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ enum wl12xx_flags {
WL1271_FLAG_SOFT_GEMINI,
WL1271_FLAG_RX_STREAMING_STARTED,
WL1271_FLAG_RECOVERY_IN_PROGRESS,
WL1271_FLAG_CS_PROGRESS,
};

struct wl1271_link {
Expand Down

0 comments on commit 6d158ff

Please sign in to comment.