From 4b29886feb072427df80ca6395b2b11a09245eaf Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 3 Oct 2011 12:06:36 +0200 Subject: [PATCH 1/4] wl12xx: configure rate policy for p2p operations p2p packets should go out only with OFDM rates. Configure a new rate policy that will (later) be used during p2p_find (when the p2p_cli / p2p_go interfaces are in use, we won't have to use this policy, as the configured rates should already be OFDM-only). Additionally, update CONF_TX_MAX_RATE_CLASSES to reflect the current value from the fw api. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 16 ++++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 1 + drivers/net/wireless/wl12xx/conf.h | 6 +++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 399849eeb247..ca044a743191 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -777,7 +777,23 @@ int wl1271_acx_sta_rate_policies(struct wl1271 *wl) acx->rate_policy.long_retry_limit = c->long_retry_limit; acx->rate_policy.aflags = c->aflags; + ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of rate policies failed: %d", ret); + goto out; + } + /* + * configure one rate class for basic p2p operations. + * (p2p packets should always go out with OFDM rates, even + * if we are currently connected to 11b AP) + */ + acx->rate_policy_idx = cpu_to_le32(ACX_TX_BASIC_RATE_P2P); + acx->rate_policy.enabled_rates = + cpu_to_le32(CONF_TX_RATE_MASK_BASIC_P2P); + acx->rate_policy.short_retry_limit = c->short_retry_limit; + acx->rate_policy.long_retry_limit = c->long_retry_limit; + acx->rate_policy.aflags = c->aflags; ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 556ee4e282d5..e3f93b4b3429 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -656,6 +656,7 @@ struct acx_rate_class { #define ACX_TX_BASIC_RATE 0 #define ACX_TX_AP_FULL_RATE 1 +#define ACX_TX_BASIC_RATE_P2P 2 #define ACX_TX_AP_MODE_MGMT_RATE 4 #define ACX_TX_AP_MODE_BCST_RATE 5 struct acx_rate_policy { diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 6a6805c3cc74..04bb8fbf93f9 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -416,13 +416,17 @@ struct conf_rx_settings { u8 queue_type; }; -#define CONF_TX_MAX_RATE_CLASSES 8 +#define CONF_TX_MAX_RATE_CLASSES 10 #define CONF_TX_RATE_MASK_UNSPECIFIED 0 #define CONF_TX_RATE_MASK_BASIC (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS) #define CONF_TX_RATE_RETRY_LIMIT 10 +/* basic rates for p2p operations (probe req/resp, etc.) */ +#define CONF_TX_RATE_MASK_BASIC_P2P (CONF_HW_BIT_RATE_6MBPS | \ + CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS) + /* * Rates supported for data packets when operating as AP. Note the absence * of the 22Mbps rate. There is a FW limitation on 12 rates so we must drop From c9e79a4714493df6508d8346195ea30fb69b7783 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 27 Sep 2011 16:22:35 +0300 Subject: [PATCH 2/4] wl12xx: set max_sched_scan_ie_len correctly The wiphy max_sched_scan_ie_len attribute was not set correctly and remained as 0, so when IEs were being passed in a scheduled scan, we were returning -EINVAL. Fix this by setting the attribute properly. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e2d6edd2fcd2..b1b405b576cc 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -4679,6 +4679,9 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE - sizeof(struct ieee80211_header); + wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE - + sizeof(struct ieee80211_header); + wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; /* make sure all our channels fit in the scanned_ch bitmask */ From 6d158ff38d8c99dc1bee775a66451168316692f4 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Thu, 8 Sep 2011 13:01:33 +0300 Subject: [PATCH 3/4] 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 [added one comment, remove the tx_flush and rephrased the commit message] Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 3 +- drivers/net/wireless/wl12xx/cmd.c | 58 ++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/cmd.h | 20 ++++++++++ drivers/net/wireless/wl12xx/event.c | 15 +++++++ drivers/net/wireless/wl12xx/main.c | 37 ++++++++++++++++++ drivers/net/wireless/wl12xx/wl12xx.h | 1 + 6 files changed, 133 insertions(+), 1 deletion(-) 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) 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) { 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) 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; +} 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); 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*/ @@ -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__ */ 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) 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) 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) { 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) @@ -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; @@ -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) }; 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 { WL1271_FLAG_SOFT_GEMINI, WL1271_FLAG_RX_STREAMING_STARTED, WL1271_FLAG_RECOVERY_IN_PROGRESS, + WL1271_FLAG_CS_PROGRESS, }; struct wl1271_link { From 694440d93b9963a2c426708cb11494236c0b2552 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 5 Oct 2011 11:55:38 +0200 Subject: [PATCH 4/4] wl12xx: disable AP-mode-specific quirks The current wl12xx fw (7.3.0.0.77) supports both STA and AP mode, and we no longer use AP-mode-specific quirks. WL12XX_QUIRK_END_OF_TRANSACTION is still used for certain HWs, while WL12XX_QUIRK_LPD_MODE is not used anymore. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 3 --- drivers/net/wireless/wl12xx/cmd.c | 5 ----- drivers/net/wireless/wl12xx/main.c | 8 -------- drivers/net/wireless/wl12xx/wl12xx.h | 6 ------ 4 files changed, 22 deletions(-) diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 9b400270397b..d4e628db76b0 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -770,9 +770,6 @@ int wl1271_load_firmware(struct wl1271 *wl) clk |= (wl->ref_clock << 1) << 4; } - if (wl->quirks & WL12XX_QUIRK_LPD_MODE) - clk |= SCRATCH_ENABLE_LPD; - wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 8c963a6bb0a5..a52299e548fa 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -134,11 +134,6 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) /* Override the REF CLK from the NVS with the one from platform data */ gen_parms->general_params.ref_clock = wl->ref_clock; - /* LPD mode enable (bits 6-7) in WL1271 AP mode only */ - if (wl->quirks & WL12XX_QUIRK_LPD_MODE) - gen_parms->general_params.general_settings |= - GENERAL_SETTINGS_DRPW_LPD; - ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 6b8a8a339f96..884f82b63219 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1333,14 +1333,6 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", wl->chip.id); - /* - * 'end-of-transaction flag' and 'LPD mode flag' - * should be set in wl127x AP mode only - */ - if (wl->bss_type == BSS_TYPE_AP_BSS) - wl->quirks |= (WL12XX_QUIRK_END_OF_TRANSACTION | - WL12XX_QUIRK_LPD_MODE); - ret = wl1271_setup(wl); if (ret < 0) goto out; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 02644b4fb697..1ec90fc7505e 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -672,12 +672,6 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); /* WL128X requires aggregated packets to be aligned to the SDIO block size */ #define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2) -/* - * WL127X AP mode requires Low Power DRPw (LPD) enable to reduce power - * consumption - */ -#define WL12XX_QUIRK_LPD_MODE BIT(3) - /* Older firmwares did not implement the FW logger over bus feature */ #define WL12XX_QUIRK_FWLOG_NOT_IMPLEMENTED BIT(4)