aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorShahar Levi <shahar_levi@ti.com>2011-09-08 06:01:33 -0400
committerLuciano Coelho <coelho@ti.com>2011-10-07 01:32:27 -0400
commit6d158ff38d8c99dc1bee775a66451168316692f4 (patch)
tree500f64f4e4e4bacda3fc62b571e10dab52aa10e6 /drivers/net/wireless
parentc9e79a4714493df6508d8346195ea30fb69b7783 (diff)
wl12xx: Add support for HW channel switch
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>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r--drivers/net/wireless/wl12xx/boot.c3
-rw-r--r--drivers/net/wireless/wl12xx/cmd.c58
-rw-r--r--drivers/net/wireless/wl12xx/cmd.h20
-rw-r--r--drivers/net/wireless/wl12xx/event.c15
-rw-r--r--drivers/net/wireless/wl12xx/main.c37
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx.h1
6 files changed, 133 insertions, 1 deletions
diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c
index 6d5664bfc37d..9b400270397b 100644
--- a/drivers/net/wireless/wl12xx/boot.c
+++ b/drivers/net/wireless/wl12xx/boot.c
@@ -503,7 +503,8 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
503 BA_SESSION_RX_CONSTRAINT_EVENT_ID | 503 BA_SESSION_RX_CONSTRAINT_EVENT_ID |
504 REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | 504 REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID |
505 INACTIVE_STA_EVENT_ID | 505 INACTIVE_STA_EVENT_ID |
506 MAX_TX_RETRY_EVENT_ID; 506 MAX_TX_RETRY_EVENT_ID |
507 CHANNEL_SWITCH_COMPLETE_EVENT_ID;
507 508
508 ret = wl1271_event_unmask(wl); 509 ret = wl1271_event_unmask(wl);
509 if (ret < 0) { 510 if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index 287fe95ecb40..8c963a6bb0a5 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -1700,3 +1700,61 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id)
1700out: 1700out:
1701 return ret; 1701 return ret;
1702} 1702}
1703
1704int wl12xx_cmd_channel_switch(struct wl1271 *wl,
1705 struct ieee80211_channel_switch *ch_switch)
1706{
1707 struct wl12xx_cmd_channel_switch *cmd;
1708 int ret;
1709
1710 wl1271_debug(DEBUG_ACX, "cmd channel switch");
1711
1712 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1713 if (!cmd) {
1714 ret = -ENOMEM;
1715 goto out;
1716 }
1717
1718 cmd->channel = ch_switch->channel->hw_value;
1719 cmd->switch_time = ch_switch->count;
1720 cmd->tx_suspend = ch_switch->block_tx;
1721 cmd->flush = 0; /* this value is ignored by the FW */
1722
1723 ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0);
1724 if (ret < 0) {
1725 wl1271_error("failed to send channel switch command");
1726 goto out_free;
1727 }
1728
1729out_free:
1730 kfree(cmd);
1731
1732out:
1733 return ret;
1734}
1735
1736int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl)
1737{
1738 struct wl12xx_cmd_stop_channel_switch *cmd;
1739 int ret;
1740
1741 wl1271_debug(DEBUG_ACX, "cmd stop channel switch");
1742
1743 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1744 if (!cmd) {
1745 ret = -ENOMEM;
1746 goto out;
1747 }
1748
1749 ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0);
1750 if (ret < 0) {
1751 wl1271_error("failed to stop channel switch command");
1752 goto out_free;
1753 }
1754
1755out_free:
1756 kfree(cmd);
1757
1758out:
1759 return ret;
1760}
diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index 8e4d11ec0c55..b7bd42769aa7 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -79,6 +79,9 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
79int wl12xx_cmd_config_fwlog(struct wl1271 *wl); 79int wl12xx_cmd_config_fwlog(struct wl1271 *wl);
80int wl12xx_cmd_start_fwlog(struct wl1271 *wl); 80int wl12xx_cmd_start_fwlog(struct wl1271 *wl);
81int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); 81int wl12xx_cmd_stop_fwlog(struct wl1271 *wl);
82int wl12xx_cmd_channel_switch(struct wl1271 *wl,
83 struct ieee80211_channel_switch *ch_switch);
84int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl);
82 85
83enum wl1271_commands { 86enum wl1271_commands {
84 CMD_INTERROGATE = 1, /*use this to read information elements*/ 87 CMD_INTERROGATE = 1, /*use this to read information elements*/
@@ -677,4 +680,21 @@ struct wl12xx_cmd_stop_fwlog {
677 struct wl1271_cmd_header header; 680 struct wl1271_cmd_header header;
678} __packed; 681} __packed;
679 682
683struct wl12xx_cmd_channel_switch {
684 struct wl1271_cmd_header header;
685
686 /* The new serving channel */
687 u8 channel;
688 /* Relative time of the serving channel switch in TBTT units */
689 u8 switch_time;
690 /* 1: Suspend TX till switch time; 0: Do not suspend TX */
691 u8 tx_suspend;
692 /* 1: Flush TX at switch time; 0: Do not flush */
693 u8 flush;
694} __packed;
695
696struct wl12xx_cmd_stop_channel_switch {
697 struct wl1271_cmd_header header;
698} __packed;
699
680#endif /* __WL1271_CMD_H__ */ 700#endif /* __WL1271_CMD_H__ */
diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c
index e66db69f8d17..674ad2a9e409 100644
--- a/drivers/net/wireless/wl12xx/event.c
+++ b/drivers/net/wireless/wl12xx/event.c
@@ -300,6 +300,21 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
300 wl1271_stop_ba_event(wl); 300 wl1271_stop_ba_event(wl);
301 } 301 }
302 302
303 if ((vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) && !is_ap) {
304 wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. "
305 "status = 0x%x",
306 mbox->channel_switch_status);
307 /*
308 * That event uses for two cases:
309 * 1) channel switch complete with status=0
310 * 2) channel switch failed status=1
311 */
312 if (test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags) &&
313 (wl->vif))
314 ieee80211_chswitch_done(wl->vif,
315 mbox->channel_switch_status ? false : true);
316 }
317
303 if ((vector & DUMMY_PACKET_EVENT_ID)) { 318 if ((vector & DUMMY_PACKET_EVENT_ID)) {
304 wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); 319 wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID");
305 if (wl->vif) 320 if (wl->vif)
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index b1b405b576cc..6b8a8a339f96 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -2222,6 +2222,11 @@ static int wl1271_unjoin(struct wl1271 *wl)
2222{ 2222{
2223 int ret; 2223 int ret;
2224 2224
2225 if (test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags)) {
2226 wl12xx_cmd_stop_channel_switch(wl);
2227 ieee80211_chswitch_done(wl->vif, false);
2228 }
2229
2225 /* to stop listening to a channel, we disconnect */ 2230 /* to stop listening to a channel, we disconnect */
2226 ret = wl12xx_cmd_role_stop_sta(wl); 2231 ret = wl12xx_cmd_role_stop_sta(wl);
2227 if (ret < 0) 2232 if (ret < 0)
@@ -4130,6 +4135,37 @@ static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
4130 return 0; 4135 return 0;
4131} 4136}
4132 4137
4138static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
4139 struct ieee80211_channel_switch *ch_switch)
4140{
4141 struct wl1271 *wl = hw->priv;
4142 int ret;
4143
4144 wl1271_debug(DEBUG_MAC80211, "mac80211 channel switch");
4145
4146 mutex_lock(&wl->mutex);
4147
4148 if (unlikely(wl->state == WL1271_STATE_OFF)) {
4149 mutex_unlock(&wl->mutex);
4150 ieee80211_chswitch_done(wl->vif, false);
4151 return;
4152 }
4153
4154 ret = wl1271_ps_elp_wakeup(wl);
4155 if (ret < 0)
4156 goto out;
4157
4158 ret = wl12xx_cmd_channel_switch(wl, ch_switch);
4159
4160 if (!ret)
4161 set_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags);
4162
4163 wl1271_ps_elp_sleep(wl);
4164
4165out:
4166 mutex_unlock(&wl->mutex);
4167}
4168
4133static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) 4169static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
4134{ 4170{
4135 struct wl1271 *wl = hw->priv; 4171 struct wl1271 *wl = hw->priv;
@@ -4406,6 +4442,7 @@ static const struct ieee80211_ops wl1271_ops = {
4406 .ampdu_action = wl1271_op_ampdu_action, 4442 .ampdu_action = wl1271_op_ampdu_action,
4407 .tx_frames_pending = wl1271_tx_frames_pending, 4443 .tx_frames_pending = wl1271_tx_frames_pending,
4408 .set_bitrate_mask = wl12xx_set_bitrate_mask, 4444 .set_bitrate_mask = wl12xx_set_bitrate_mask,
4445 .channel_switch = wl12xx_op_channel_switch,
4409 CFG80211_TESTMODE_CMD(wl1271_tm_cmd) 4446 CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
4410}; 4447};
4411 4448
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 997f53245011..02644b4fb697 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -348,6 +348,7 @@ enum wl12xx_flags {
348 WL1271_FLAG_SOFT_GEMINI, 348 WL1271_FLAG_SOFT_GEMINI,
349 WL1271_FLAG_RX_STREAMING_STARTED, 349 WL1271_FLAG_RX_STREAMING_STARTED,
350 WL1271_FLAG_RECOVERY_IN_PROGRESS, 350 WL1271_FLAG_RECOVERY_IN_PROGRESS,
351 WL1271_FLAG_CS_PROGRESS,
351}; 352};
352 353
353struct wl1271_link { 354struct wl1271_link {