From 564f59509e26355965949c677f9d6eb064a3aa0b Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Mon, 4 Apr 2011 10:20:39 +0300 Subject: wl12xx: Set End-of-transaction Flag at Wl127x AP Mode End-of-transaction flag should be set when working with wl127x chip on AP mode. Thanks Ido Yariv for the guidance with that. Signed-off-by: Shahar Levi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0c69e959d0d..11497a90463 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1011,6 +1011,10 @@ 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 should be set in wl127x AP mode */ + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; + ret = wl1271_setup(wl); if (ret < 0) goto out; -- cgit v1.2.3 From c75bbcdb200e2815c855e42a4685d170858af306 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 4 Apr 2011 10:38:47 +0300 Subject: wl12xx: sleep instead of wakeup after tx work commit d05c806 ("wl12xx: rearrange some ELP wake_up/sleep calls") introduced a bug in which wl1271_ps_elp_wakeup() was called instead of wl1271_ps_elp_sleep() after completing the tx work. Reported-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 7a3339fd341..c8366596446 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -630,7 +630,7 @@ void wl1271_tx_work(struct work_struct *work) wl1271_tx_work_locked(wl); - wl1271_ps_elp_wakeup(wl); + wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); } -- cgit v1.2.3 From a665d6e260f0233aac73f74d15bb6a029cc5ec47 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 3 Apr 2011 02:01:59 +0300 Subject: wl12xx: avoid premature elp entrance The elp_work is being enqueued on wl1271_ps_elp_sleep, but doesn't get cancelled on wl1271_ps_elp_wakeup. This might cause immediate entrance to elp when the wl->mutex is being released, rather than using the delayed enqueueing optimization. Cancel elp_work on wakeup request, and add a new WL1271_FLAG_ELP_REQUESTED flag to further synchronize the elp actions. [Fixed a couple of typos in some comments -- Luca] Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/ps.c | 30 ++++++++++++++++++++++++------ drivers/net/wireless/wl12xx/wl12xx.h | 1 + 2 files changed, 25 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index b8deada5d02..b59b67711a1 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -43,6 +43,10 @@ void wl1271_elp_work(struct work_struct *work) if (unlikely(wl->state == WL1271_STATE_OFF)) goto out; + /* our work might have been already cancelled */ + if (unlikely(!test_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) + goto out; + if (test_bit(WL1271_FLAG_IN_ELP, &wl->flags) || (!test_bit(WL1271_FLAG_PSM, &wl->flags) && !test_bit(WL1271_FLAG_IDLE, &wl->flags))) @@ -61,12 +65,16 @@ out: /* Routines to toggle sleep mode while in ELP */ void wl1271_ps_elp_sleep(struct wl1271 *wl) { - if (test_bit(WL1271_FLAG_PSM, &wl->flags) || - test_bit(WL1271_FLAG_IDLE, &wl->flags)) { - cancel_delayed_work(&wl->elp_work); - ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, - msecs_to_jiffies(ELP_ENTRY_DELAY)); - } + /* we shouldn't get consecutive sleep requests */ + if (WARN_ON(test_and_set_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags))) + return; + + if (!test_bit(WL1271_FLAG_PSM, &wl->flags) && + !test_bit(WL1271_FLAG_IDLE, &wl->flags)) + return; + + ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, + msecs_to_jiffies(ELP_ENTRY_DELAY)); } int wl1271_ps_elp_wakeup(struct wl1271 *wl) @@ -77,6 +85,16 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) u32 start_time = jiffies; bool pending = false; + /* + * we might try to wake up even if we didn't go to sleep + * before (e.g. on boot) + */ + if (!test_and_clear_bit(WL1271_FLAG_ELP_REQUESTED, &wl->flags)) + return 0; + + /* don't cancel_sync as it might contend for a mutex and deadlock */ + cancel_delayed_work(&wl->elp_work); + if (!test_bit(WL1271_FLAG_IN_ELP, &wl->flags)) return 0; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 7c521af58e7..f3de96212b9 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -345,6 +345,7 @@ enum wl12xx_flags { WL1271_FLAG_TX_QUEUE_STOPPED, WL1271_FLAG_TX_PENDING, WL1271_FLAG_IN_ELP, + WL1271_FLAG_ELP_REQUESTED, WL1271_FLAG_PSM, WL1271_FLAG_PSM_REQUESTED, WL1271_FLAG_IRQ_RUNNING, -- cgit v1.2.3 From b03acadea4f46884aa3c3e4d3a6ce03d283525e6 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 3 Apr 2011 13:54:54 +0300 Subject: wl12xx: Set correct REF CLK and TCXO CLK values to the FW Fix mismatch between the REF CLK and TCXO CLK information that is set in the platform data and the NVS, so we override what comes from the NVS and replace it with what comes from the platform data. [Small fix in a comment -- Luca] Signed-off-by: Shahar Levi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index d48331682e7..a9ffdd86f9b 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -129,6 +129,9 @@ int wl1271_cmd_general_parms(struct wl1271 *wl) if (gp->tx_bip_fem_auto_detect) answer = true; + /* Override the REF CLK from the NVS with the one from platform data */ + gen_parms->general_params.ref_clock = wl->ref_clock; + ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); @@ -168,6 +171,10 @@ int wl128x_cmd_general_parms(struct wl1271 *wl) if (gp->tx_bip_fem_auto_detect) answer = true; + /* Replace REF and TCXO CLKs with the ones from platform data */ + gen_parms->general_params.ref_clock = wl->ref_clock; + gen_parms->general_params.tcxo_ref_clock = wl->tcxo_clock; + ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), answer); if (ret < 0) { wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); -- cgit v1.2.3 From a20a5b7e48e24c1bf9c10ba27cb1862f8f777d00 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 5 Apr 2011 18:21:31 +0300 Subject: wl12xx: print actual rx packet size (without padding) When debugging, reduce the padding size from each rx packet, to get the actual packet size (so comparing it against a cap file will be easier) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/rx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 2a581495d5c..faf5a1d3c2c 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -121,7 +121,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); - wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, + wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, + skb->len - desc->pad_len, beacon ? "beacon" : ""); skb_trim(skb, skb->len - desc->pad_len); -- cgit v1.2.3 From 30df14d0d35dd166d50b8ea80d5f0b7ef1edb6da Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 5 Apr 2011 19:13:28 +0300 Subject: wl12xx: avoid redundant join on interface reconfiguration ieee80211_reconfig() sets most of the "changed" flags regardless of the actual change (e.g. BSS_CHANGED_ASSOC will be set even if the interface is still not associated). in this case the driver will issue some unneeded commands. Since the driver relies solely on the BSS_CHANGED_ASSOC flag, without checking if there was an actual change, it will end up issuing unjoin() and dummy_join() commands, although it was never associated and should just remain idle. Avoid it by checking the actual state change, in addition to the "changed" flag. (there seem to be more redundant configuration commands being issued, but they shouldn't harm) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 11497a90463..1dd735cc38b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2698,8 +2698,10 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } else { /* use defaults when not associated */ + bool was_assoc = + !!test_and_clear_bit(WL1271_FLAG_STA_ASSOCIATED, + &wl->flags); clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags); - clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); wl->aid = 0; /* free probe-request template */ @@ -2725,8 +2727,10 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, goto out; /* restore the bssid filter and go to dummy bssid */ - wl1271_unjoin(wl); - wl1271_dummy_join(wl); + if (was_assoc) { + wl1271_unjoin(wl); + wl1271_dummy_join(wl); + } } } -- cgit v1.2.3 From cb5ae0530e0e2af86d128ce758645b6b4a9132e1 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 7 Apr 2011 15:52:05 +0300 Subject: wl12xx: configure rates when working in ibss mode When working in ibss mode, we don't configure rate policy per station (as we use the same link for multiple stations), so currently the 1mb/s rate is being used. Instead, configure the firmware to use the whole 11b rates by default. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 7 +++++++ drivers/net/wireless/wl12xx/main.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index d16094f2604..5551d9d4016 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -516,6 +516,13 @@ struct conf_rx_settings { #define CONF_TX_AP_DEFAULT_MGMT_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS) +/* + * Default rates for working as IBSS. use 11b rates + */ +#define CONF_TX_IBSS_DEFAULT_RATES (CONF_HW_BIT_RATE_1MBPS | \ + CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ + CONF_HW_BIT_RATE_11MBPS); + struct conf_tx_rate_class { /* diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1dd735cc38b..53a8e2357cc 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2509,6 +2509,24 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, } } + if (changed & BSS_CHANGED_IBSS) { + wl1271_debug(DEBUG_ADHOC, "ibss_joined: %d", + bss_conf->ibss_joined); + + if (bss_conf->ibss_joined) { + u32 rates = bss_conf->basic_rates; + wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, + rates); + wl->basic_rate = wl1271_tx_min_rate_get(wl); + + /* by default, use 11b rates */ + wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES; + ret = wl1271_acx_sta_rate_policies(wl); + if (ret < 0) + goto out; + } + } + ret = wl1271_bss_erp_info_changed(wl, bss_conf, changed); if (ret < 0) goto out; -- cgit v1.2.3 From ff86843dfbb368766d0aecd0147821d9a2b60edb Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Mon, 11 Apr 2011 15:41:46 +0300 Subject: wl12xx: FM WLAN coexistence Add support to FM WLAN coexistence (STA only). Some WiFi harmonics may interfere with FM operation, to avoid this problem special coexistence techniques are activated around some FM frequencies. Signed-off-by: Shahar Levi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 40 +++++++++++++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 61 ++++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/conf.h | 14 +++++++++ drivers/net/wireless/wl12xx/init.c | 5 ++++ drivers/net/wireless/wl12xx/main.c | 17 +++++++++++ 5 files changed, 137 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index a5c9c0aff83..8a39d1e40a6 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1626,3 +1626,43 @@ out: kfree(acx); return ret; } + +int wl1271_acx_fm_coex(struct wl1271 *wl) +{ + struct wl1271_acx_fm_coex *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx fm coex setting"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = wl->conf.fm_coex.enable; + acx->swallow_period = wl->conf.fm_coex.swallow_period; + acx->n_divider_fref_set_1 = wl->conf.fm_coex.n_divider_fref_set_1; + acx->n_divider_fref_set_2 = wl->conf.fm_coex.n_divider_fref_set_2; + acx->m_divider_fref_set_1 = + cpu_to_le16(wl->conf.fm_coex.m_divider_fref_set_1); + acx->m_divider_fref_set_2 = + cpu_to_le16(wl->conf.fm_coex.m_divider_fref_set_2); + acx->coex_pll_stabilization_time = + cpu_to_le32(wl->conf.fm_coex.coex_pll_stabilization_time); + acx->ldo_stabilization_time = + cpu_to_le16(wl->conf.fm_coex.ldo_stabilization_time); + acx->fm_disturbed_band_margin = + wl->conf.fm_coex.fm_disturbed_band_margin; + acx->swallow_clk_diff = wl->conf.fm_coex.swallow_clk_diff; + + ret = wl1271_cmd_configure(wl, ACX_FM_COEX_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx fm coex setting failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 942908cd53a..2dde0346e95 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1179,6 +1179,65 @@ struct wl1271_acx_inconnection_sta { u8 padding1[2]; } __packed; +/* + * ACX_FM_COEX_CFG + * set the FM co-existence parameters. + */ +struct wl1271_acx_fm_coex { + struct acx_header header; + /* enable(1) / disable(0) the FM Coex feature */ + u8 enable; + /* + * Swallow period used in COEX PLL swallowing mechanism. + * 0xFF = use FW default + */ + u8 swallow_period; + /* + * The N divider used in COEX PLL swallowing mechanism for Fref of + * 38.4/19.2 Mhz. 0xFF = use FW default + */ + u8 n_divider_fref_set_1; + /* + * The N divider used in COEX PLL swallowing mechanism for Fref of + * 26/52 Mhz. 0xFF = use FW default + */ + u8 n_divider_fref_set_2; + /* + * The M divider used in COEX PLL swallowing mechanism for Fref of + * 38.4/19.2 Mhz. 0xFFFF = use FW default + */ + __le16 m_divider_fref_set_1; + /* + * The M divider used in COEX PLL swallowing mechanism for Fref of + * 26/52 Mhz. 0xFFFF = use FW default + */ + __le16 m_divider_fref_set_2; + /* + * The time duration in uSec required for COEX PLL to stabilize. + * 0xFFFFFFFF = use FW default + */ + __le32 coex_pll_stabilization_time; + /* + * The time duration in uSec required for LDO to stabilize. + * 0xFFFFFFFF = use FW default + */ + __le16 ldo_stabilization_time; + /* + * The disturbed frequency band margin around the disturbed frequency + * center (single sided). + * For example, if 2 is configured, the following channels will be + * considered disturbed channel: + * 80 +- 0.1 MHz, 91 +- 0.1 MHz, 98 +- 0.1 MHz, 102 +- 0.1 MH + * 0xFF = use FW default + */ + u8 fm_disturbed_band_margin; + /* + * The swallow clock difference of the swallowing mechanism. + * 0xFF = use FW default + */ + u8 swallow_clk_diff; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1208,6 +1267,7 @@ enum { ACX_BCN_DTIM_OPTIONS = 0x0031, ACX_SG_ENABLE = 0x0032, ACX_SG_CFG = 0x0033, + ACX_FM_COEX_CFG = 0x0034, ACX_BEACON_FILTER_TABLE = 0x0038, ACX_ARP_IP_FILTER = 0x0039, ACX_ROAMING_STATISTICS_TBL = 0x003B, @@ -1318,5 +1378,6 @@ int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl); int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); +int wl1271_acx_fm_coex(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 5551d9d4016..a4e0acff867 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1199,6 +1199,19 @@ struct conf_memory_settings { u8 tx_min; }; +struct conf_fm_coex { + u8 enable; + u8 swallow_period; + u8 n_divider_fref_set_1; + u8 n_divider_fref_set_2; + u16 m_divider_fref_set_1; + u16 m_divider_fref_set_2; + u32 coex_pll_stabilization_time; + u16 ldo_stabilization_time; + u8 fm_disturbed_band_margin; + u8 swallow_clk_diff; +}; + struct conf_drv_settings { struct conf_sg_settings sg; struct conf_rx_settings rx; @@ -1212,6 +1225,7 @@ struct conf_drv_settings { struct conf_ht_setting ht; struct conf_memory_settings mem_wl127x; struct conf_memory_settings mem_wl128x; + struct conf_fm_coex fm_coex; u8 hci_io_ds; }; diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index ab3b1e21de2..46fd7212b23 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -356,6 +356,11 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + /* FM WLAN coexistence */ + ret = wl1271_acx_fm_coex(wl); + if (ret < 0) + return ret; + /* Beacons and broadcast settings */ ret = wl1271_init_beacon_broadcast(wl); if (ret < 0) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 53a8e2357cc..8a7a8c5fcf5 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -320,6 +320,18 @@ static struct conf_drv_settings default_conf = { .min_req_rx_blocks = 22, .tx_min = 27, }, + .fm_coex = { + .enable = true, + .swallow_period = 5, + .n_divider_fref_set_1 = 0xff, /* default */ + .n_divider_fref_set_2 = 12, + .m_divider_fref_set_1 = 148, + .m_divider_fref_set_2 = 0xffff, /* default */ + .coex_pll_stabilization_time = 0xffffffff, /* default */ + .ldo_stabilization_time = 0xffff, /* default */ + .fm_disturbed_band_margin = 0xff, /* default */ + .swallow_clk_diff = 0xff, /* default */ + }, .hci_io_ds = HCI_IO_DS_6MA, }; @@ -508,6 +520,11 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* FM WLAN coexistence */ + ret = wl1271_acx_fm_coex(wl); + if (ret < 0) + goto out_free_memmap; + /* Energy detection */ ret = wl1271_init_energy_detection(wl); if (ret < 0) -- cgit v1.2.3 From 2370841bf1a735661db3d3ae63385be3475b7452 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Wed, 13 Apr 2011 14:52:50 +0300 Subject: wl12xx: Update Power Save Exit Retries Packets Reducing the retries of sending PS exit packets to the peer AP. That fix is to avoid sending unrealizable number of PS exit packets in case of ap lost. Signed-off-by: Shahar Levi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8a7a8c5fcf5..c0cfcc7e11a 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -258,7 +258,7 @@ static struct conf_drv_settings default_conf = { .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 50, .psm_entry_retries = 5, - .psm_exit_retries = 255, + .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, .psm_entry_hangover_period = 1, .keep_alive_interval = 55000, -- cgit v1.2.3 From 1fe9e2464c667903d7eec0314db26c462ca9d276 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 17 Apr 2011 11:20:47 +0300 Subject: wl12xx: add debugfs entries for dtim_interval and beacon_interval When configuring ACX_WAKE_UP_CONDITIONS (before entering psm), we tell the firmware to wake up once in N DTIMs/beacons. Allow control of this value via debugfs (for debugging purposes). Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 132 ++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 70ab1986788..88c6efe33ec 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -291,6 +291,136 @@ static const struct file_operations gpio_power_ops = { .llseek = default_llseek, }; +static ssize_t dtim_interval_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + u8 value; + + if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_DTIM || + wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM) + value = wl->conf.conn.listen_interval; + else + value = 0; + + return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); +} + +static ssize_t dtim_interval_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = strict_strtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value for dtim_interval"); + return -EINVAL; + } + + if (value < 1 || value > 10) { + wl1271_warning("dtim value is not in valid range"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + wl->conf.conn.listen_interval = value; + /* for some reason there are different event types for 1 and >1 */ + if (value == 1) + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_DTIM; + else + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM; + + /* + * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only + * take effect on the next time we enter psm. + */ + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations dtim_interval_ops = { + .read = dtim_interval_read, + .write = dtim_interval_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + +static ssize_t beacon_interval_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + u8 value; + + if (wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_BEACON || + wl->conf.conn.wake_up_event == CONF_WAKE_UP_EVENT_N_BEACONS) + value = wl->conf.conn.listen_interval; + else + value = 0; + + return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); +} + +static ssize_t beacon_interval_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = strict_strtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value for beacon_interval"); + return -EINVAL; + } + + if (value < 1 || value > 255) { + wl1271_warning("beacon interval value is not in valid range"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + wl->conf.conn.listen_interval = value; + /* for some reason there are different event types for 1 and >1 */ + if (value == 1) + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_BEACON; + else + wl->conf.conn.wake_up_event = CONF_WAKE_UP_EVENT_N_BEACONS; + + /* + * we don't reconfigure ACX_WAKE_UP_CONDITIONS now, so it will only + * take effect on the next time we enter psm. + */ + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations beacon_interval_ops = { + .read = beacon_interval_read, + .write = beacon_interval_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -399,6 +529,8 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(excessive_retries, rootdir); DEBUGFS_ADD(gpio_power, rootdir); + DEBUGFS_ADD(dtim_interval, rootdir); + DEBUGFS_ADD(beacon_interval, rootdir); return 0; -- cgit v1.2.3 From ae825e4ba81203e1b3d3159f24327cdc2629dbd8 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 18 Apr 2011 16:40:14 +0300 Subject: wl12xx: Modify memory configuration for 128x/AP The 128x/AP firmware does not yet support dynamic memory. Temporarily, the memory configuration for the 127x was used both for 127x/AP as well as 128x/AP. Since the two chips don't have the same number of memory blocks, TP was significantly degraded. This hasn't been fine tuned yet, but using the base 128x numbers (without dynamic memory) seems to yield much better results (around 30% more). Additional fine tuning will be required in the future. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 8a39d1e40a6..b1b5139139e 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -954,6 +954,7 @@ out: int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) { struct wl1271_acx_ap_config_memory *mem_conf; + struct conf_memory_settings *mem; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); @@ -964,14 +965,21 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) goto out; } + if (wl->chip.id == CHIP_ID_1283_PG20) + /* + * FIXME: The 128x AP FW does not yet support dynamic memory. + * Use the base memory configuration for 128x for now. This + * should be fine tuned in the future. + */ + mem = &wl->conf.mem_wl128x; + else + mem = &wl->conf.mem_wl127x; + /* memory config */ - /* FIXME: for now we always use mem_wl127x for AP, because it - * doesn't support dynamic memory and we don't have the - * optimal values for wl128x without dynamic memory yet */ - mem_conf->num_stations = wl->conf.mem_wl127x.num_stations; - mem_conf->rx_mem_block_num = wl->conf.mem_wl127x.rx_block_num; - mem_conf->tx_min_mem_block_num = wl->conf.mem_wl127x.tx_min_block_num; - mem_conf->num_ssid_profiles = wl->conf.mem_wl127x.ssid_profiles; + mem_conf->num_stations = mem->num_stations; + mem_conf->rx_mem_block_num = mem->rx_block_num; + mem_conf->tx_min_mem_block_num = mem->tx_min_block_num; + mem_conf->num_ssid_profiles = mem->ssid_profiles; mem_conf->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, -- cgit v1.2.3 From ef2e3004855e90d2919105e4a91d7df6ab9845a9 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 18 Apr 2011 16:44:11 +0300 Subject: wl12xx: Restart TX when TX descriptors are available The driver stops sending TX packets when either there aren't enough memory blocks, or it runs out of TX descriptors. The driver continues to send packets to the FW only when more memory blocks are available. The FW might free TX descriptors without freeing the corresponding memory blocks, especially when dynamic memory is enabled. In cases where memory blocks are not freed at all, the driver will keep waiting for more memory blocks indefinitely. Fix this by clearing the WL1271_FLAG_FW_TX_BUSY flag when there are available TX descriptors. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/tx.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index c8366596446..cc837bba546 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -65,6 +65,9 @@ static int wl1271_alloc_tx_id(struct wl1271 *wl, struct sk_buff *skb) static void wl1271_free_tx_id(struct wl1271 *wl, int id) { if (__test_and_clear_bit(id, wl->tx_frames_map)) { + if (unlikely(wl->tx_frames_cnt == ACX_TX_DESCRIPTORS)) + clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); + wl->tx_frames[id] = NULL; wl->tx_frames_cnt--; } -- cgit v1.2.3 From 4cf557fcf01e352fb418e110dd013e4128493c5f Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Mon, 18 Apr 2011 16:45:10 +0300 Subject: wl12xx: Enable dynamic memory for 127x The FW can dynamically manage its internal TX & RX memory pools, moving blocks from one pool to another when necessary. This can significantly improve performance. Currently this feature is enabled only for 128x. Enable dynamic memory for 127x as well. Other parameters in the memory configuration structure may need to be fine tuned, as the optimal values for these may change once dynamic memory is enabled. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index c0cfcc7e11a..6dd72365b63 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -305,7 +305,7 @@ static struct conf_drv_settings default_conf = { .ssid_profiles = 1, .rx_block_num = 70, .tx_min_block_num = 40, - .dynamic_memory = 0, + .dynamic_memory = 1, .min_req_tx_blocks = 100, .min_req_rx_blocks = 22, .tx_min = 27, -- cgit v1.2.3 From 33437893025aa3c0195b933ac99ef7de924019f4 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 26 Apr 2011 23:35:39 +0300 Subject: wl12xx: implement the tx_frames_pending mac80211 callback Frames are considered pending when they reside in the driver TX queue or already queued in the FW. This notion of "pending" is appropriate for power save considerations in STA mode, but not necessarily in other modes (for instance P2P-GO). [Fixed a sparse warning about missing "static" in a function declaration -- Luca] Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 6dd72365b63..e177764e714 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3147,6 +3147,28 @@ out: return ret; } +static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + bool ret = false; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + /* packets are considered pending if in the TX queue or the FW */ + ret = (wl->tx_queue_count > 0) || (wl->tx_frames_cnt > 0); + + /* the above is appropriate for STA mode for PS purposes */ + WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + /* can't be const, mac80211 writes to this */ static struct ieee80211_rate wl1271_rates[] = { { .bitrate = 10, @@ -3398,6 +3420,7 @@ static const struct ieee80211_ops wl1271_ops = { .sta_add = wl1271_op_sta_add, .sta_remove = wl1271_op_sta_remove, .ampdu_action = wl1271_op_ampdu_action, + .tx_frames_pending = wl1271_tx_frames_pending, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; -- cgit v1.2.3 From 34c8e3d2bb901b2920d2a8930c0de82e7fefac76 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 26 Apr 2011 23:35:40 +0300 Subject: wl12xx: discard corrupted packets in RX When packets arrive with a RX descriptor indicating corruption, discard them. In general white-list the RX descriptor status to prevent rouge data from being sent up. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/rx.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index faf5a1d3c2c..70091035e01 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -76,12 +76,15 @@ static void wl1271_rx_status(struct wl1271 *wl, status->band); if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { - status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; + u8 desc_err_code = desc->status & WL1271_RX_DESC_STATUS_MASK; - if (likely(!(desc->status & WL1271_RX_DESC_DECRYPT_FAIL))) - status->flag |= RX_FLAG_DECRYPTED; - if (unlikely(desc->status & WL1271_RX_DESC_MIC_FAIL)) + status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED | + RX_FLAG_DECRYPTED; + + if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) { status->flag |= RX_FLAG_MMIC_ERROR; + wl1271_warning("Michael MIC error"); + } } } @@ -100,6 +103,25 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) if (unlikely(wl->state == WL1271_STATE_PLT)) return -EINVAL; + /* the data read starts with the descriptor */ + desc = (struct wl1271_rx_descriptor *) data; + + switch (desc->status & WL1271_RX_DESC_STATUS_MASK) { + /* discard corrupted packets */ + case WL1271_RX_DESC_DRIVER_RX_Q_FAIL: + case WL1271_RX_DESC_DECRYPT_FAIL: + wl1271_warning("corrupted packet in RX with status: 0x%x", + desc->status & WL1271_RX_DESC_STATUS_MASK); + return -EINVAL; + case WL1271_RX_DESC_SUCCESS: + case WL1271_RX_DESC_MIC_FAIL: + break; + default: + wl1271_error("invalid RX descriptor status: 0x%x", + desc->status & WL1271_RX_DESC_STATUS_MASK); + return -EINVAL; + } + skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1271_error("Couldn't allocate RX frame"); @@ -109,9 +131,6 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) buf = skb_put(skb, length); memcpy(buf, data, length); - /* the data read starts with the descriptor */ - desc = (struct wl1271_rx_descriptor *) buf; - /* now we pull the descriptor out of the buffer */ skb_pull(skb, sizeof(*desc)); -- cgit v1.2.3 From 86c438f40cf3e0f6ce2da80f2d759e61d3e85ad7 Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Tue, 26 Apr 2011 23:27:44 +0200 Subject: wl12xx: do not set queue_mapping directly It is preferred to use the setter that to set queue_mapping directly. This also helps backporting in compat-wireless. Signed-off-by: Hauke Mehrtens Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e177764e714..a8770102a74 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1294,7 +1294,7 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) skb->priority = WL1271_TID_MGMT; /* Initialize all fields that might be used */ - skb->queue_mapping = 0; + skb_set_queue_mapping(skb, 0); memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); return skb; -- cgit v1.2.3 From f7c7c7e69cbc3c5b660a32cc2cb31720b2b420c8 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 29 Apr 2011 22:25:28 +0300 Subject: wl12xx: strict_stroul introduced converted to kstrtoul One new patch applied added a couple of new strict_strtoul calls. Converted those to kstroul(). Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 88c6efe33ec..b17cff6cd75 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -321,7 +321,7 @@ static ssize_t dtim_interval_write(struct file *file, return -EFAULT; buf[len] = '\0'; - ret = strict_strtoul(buf, 0, &value); + ret = kstrtoul(buf, 0, &value); if (ret < 0) { wl1271_warning("illegal value for dtim_interval"); return -EINVAL; @@ -386,7 +386,7 @@ static ssize_t beacon_interval_write(struct file *file, return -EFAULT; buf[len] = '\0'; - ret = strict_strtoul(buf, 0, &value); + ret = kstrtoul(buf, 0, &value); if (ret < 0) { wl1271_warning("illegal value for beacon_interval"); return -EINVAL; -- cgit v1.2.3 From 801f870bc0524bad7ebef9cea52d20e4d4992e4a Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:20 +0300 Subject: wl12xx: add BT-coexistance for AP Initialize AP specific BT coexitance parameters to default values and enable them in AP mode. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 40 ++++++++++++++++++++++---- drivers/net/wireless/wl12xx/acx.h | 16 +++++++++-- drivers/net/wireless/wl12xx/conf.h | 35 +++++++++++++++++++++-- drivers/net/wireless/wl12xx/init.c | 15 ++++++---- drivers/net/wireless/wl12xx/main.c | 57 +++++++++++++++++++++++++++++++++++++- 5 files changed, 146 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index b1b5139139e..2b5fb3d2df1 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -540,13 +540,13 @@ out: return ret; } -int wl1271_acx_sg_cfg(struct wl1271 *wl) +int wl1271_acx_sta_sg_cfg(struct wl1271 *wl) { - struct acx_bt_wlan_coex_param *param; + struct acx_sta_bt_wlan_coex_param *param; struct conf_sg_settings *c = &wl->conf.sg; int i, ret; - wl1271_debug(DEBUG_ACX, "acx sg cfg"); + wl1271_debug(DEBUG_ACX, "acx sg sta cfg"); param = kzalloc(sizeof(*param), GFP_KERNEL); if (!param) { @@ -555,8 +555,38 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - for (i = 0; i < CONF_SG_PARAMS_MAX; i++) - param->params[i] = cpu_to_le32(c->params[i]); + for (i = 0; i < CONF_SG_STA_PARAMS_MAX; i++) + param->params[i] = cpu_to_le32(c->sta_params[i]); + param->param_idx = CONF_SG_PARAMS_ALL; + + ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); + if (ret < 0) { + wl1271_warning("failed to set sg config: %d", ret); + goto out; + } + +out: + kfree(param); + return ret; +} + +int wl1271_acx_ap_sg_cfg(struct wl1271 *wl) +{ + struct acx_ap_bt_wlan_coex_param *param; + struct conf_sg_settings *c = &wl->conf.sg; + int i, ret; + + wl1271_debug(DEBUG_ACX, "acx sg ap cfg"); + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) { + ret = -ENOMEM; + goto out; + } + + /* BT-WLAN coext parameters */ + for (i = 0; i < CONF_SG_AP_PARAMS_MAX; i++) + param->params[i] = cpu_to_le32(c->ap_params[i]); param->param_idx = CONF_SG_PARAMS_ALL; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 2dde0346e95..cd7548dacd5 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -370,14 +370,23 @@ struct acx_bt_wlan_coex { u8 pad[3]; } __packed; -struct acx_bt_wlan_coex_param { +struct acx_sta_bt_wlan_coex_param { struct acx_header header; - __le32 params[CONF_SG_PARAMS_MAX]; + __le32 params[CONF_SG_STA_PARAMS_MAX]; u8 param_idx; u8 padding[3]; } __packed; +struct acx_ap_bt_wlan_coex_param { + struct acx_header header; + + __le32 params[CONF_SG_AP_PARAMS_MAX]; + u8 param_idx; + u8 padding[3]; +} __packed; + + struct acx_dco_itrim_params { struct acx_header header; @@ -1330,7 +1339,8 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); int wl1271_acx_conn_monit_params(struct wl1271 *wl, bool enable); int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable); -int wl1271_acx_sg_cfg(struct wl1271 *wl); +int wl1271_acx_sta_sg_cfg(struct wl1271 *wl); +int wl1271_acx_ap_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); int wl1271_acx_bcn_dtim_options(struct wl1271 *wl); int wl1271_acx_aid(struct wl1271 *wl, u16 aid); diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index a4e0acff867..2ffbe3e0601 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -396,12 +396,43 @@ enum { CONF_SG_TEMP_PARAM_3, CONF_SG_TEMP_PARAM_4, CONF_SG_TEMP_PARAM_5, - CONF_SG_PARAMS_MAX, + + /* + * AP beacon miss + * + * Range: 0 - 255 + */ + CONF_SG_AP_BEACON_MISS_TX, + + /* + * AP RX window length + * + * Range: 0 - 50 + */ + CONF_SG_RX_WINDOW_LENGTH, + + /* + * AP connection protection time + * + * Range: 0 - 5000 + */ + CONF_SG_AP_CONNECTION_PROTECTION_TIME, + + CONF_SG_TEMP_PARAM_6, + CONF_SG_TEMP_PARAM_7, + CONF_SG_TEMP_PARAM_8, + CONF_SG_TEMP_PARAM_9, + CONF_SG_TEMP_PARAM_10, + + CONF_SG_STA_PARAMS_MAX = CONF_SG_TEMP_PARAM_5 + 1, + CONF_SG_AP_PARAMS_MAX = CONF_SG_TEMP_PARAM_10 + 1, + CONF_SG_PARAMS_ALL = 0xff }; struct conf_sg_settings { - u32 params[CONF_SG_PARAMS_MAX]; + u32 sta_params[CONF_SG_STA_PARAMS_MAX]; + u32 ap_params[CONF_SG_AP_PARAMS_MAX]; u8 state; }; diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 46fd7212b23..1be65691478 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -285,7 +285,10 @@ int wl1271_init_pta(struct wl1271 *wl) { int ret; - ret = wl1271_acx_sg_cfg(wl); + if (wl->bss_type == BSS_TYPE_AP_BSS) + ret = wl1271_acx_ap_sg_cfg(wl); + else + ret = wl1271_acx_sta_sg_cfg(wl); if (ret < 0) return ret; @@ -351,11 +354,6 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - /* Bluetooth WLAN coexistence */ - ret = wl1271_init_pta(wl); - if (ret < 0) - return ret; - /* FM WLAN coexistence */ ret = wl1271_acx_fm_coex(wl); if (ret < 0) @@ -572,6 +570,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + /* Bluetooth WLAN coexistence */ + ret = wl1271_init_pta(wl); + if (ret < 0) + return ret; + /* Default memory configuration */ ret = wl1271_acx_init_mem_config(wl); if (ret < 0) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a8770102a74..5f8bb35c647 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -51,7 +51,7 @@ static struct conf_drv_settings default_conf = { .sg = { - .params = { + .sta_params = { [CONF_SG_BT_PER_THRESHOLD] = 7500, [CONF_SG_HV3_MAX_OVERRIDE] = 0, [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, @@ -101,6 +101,61 @@ static struct conf_drv_settings default_conf = { [CONF_SG_DHCP_TIME] = 5000, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, }, + .ap_params = { + [CONF_SG_BT_PER_THRESHOLD] = 7500, + [CONF_SG_HV3_MAX_OVERRIDE] = 0, + [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, + [CONF_SG_BT_LOAD_RATIO] = 50, + [CONF_SG_AUTO_PS_MODE] = 1, + [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, + [CONF_SG_ANTENNA_CONFIGURATION] = 0, + [CONF_SG_BEACON_MISS_PERCENT] = 60, + [CONF_SG_RATE_ADAPT_THRESH] = 64, + [CONF_SG_RATE_ADAPT_SNR] = 1, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 25, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 25, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 25, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 25, + [CONF_SG_RXT] = 1200, + [CONF_SG_TXT] = 1000, + [CONF_SG_ADAPTIVE_RXT_TXT] = 1, + [CONF_SG_PS_POLL_TIMEOUT] = 10, + [CONF_SG_UPSD_TIMEOUT] = 10, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800, + [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75, + [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15, + [CONF_SG_HV3_MAX_SERVED] = 6, + [CONF_SG_DHCP_TIME] = 5000, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, + [CONF_SG_TEMP_PARAM_1] = 0, + [CONF_SG_TEMP_PARAM_2] = 0, + [CONF_SG_TEMP_PARAM_3] = 0, + [CONF_SG_TEMP_PARAM_4] = 0, + [CONF_SG_TEMP_PARAM_5] = 0, + [CONF_SG_AP_BEACON_MISS_TX] = 3, + [CONF_SG_RX_WINDOW_LENGTH] = 6, + [CONF_SG_AP_CONNECTION_PROTECTION_TIME] = 50, + [CONF_SG_TEMP_PARAM_6] = 1, + }, .state = CONF_SG_PROTECTIVE, }, .rx = { -- cgit v1.2.3 From 5f704d180e448d05859e1cb6572822ba27dbcdc7 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:21 +0300 Subject: wl12xx: use wiphy values for setting rts, frag thresholds on init Use the wiphy RTS and fragmentation thresholds for initializing the FW when possible. This mitigates a bug where previously set values are forgotten after interface down/up. Add checks before settings these values to ensure they are valid. Use default values when invalid thresholds are configured. Update the default RTS threshold to the maximum value given by the specification. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 26 ++++++++++++++++++++------ drivers/net/wireless/wl12xx/acx.h | 4 ++-- drivers/net/wireless/wl12xx/init.c | 4 ++-- drivers/net/wireless/wl12xx/main.c | 6 +++--- 4 files changed, 27 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 2b5fb3d2df1..729f72a7b91 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -325,12 +325,19 @@ out: return ret; } -int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) +int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold) { struct acx_rts_threshold *rts; int ret; - wl1271_debug(DEBUG_ACX, "acx rts threshold"); + /* + * If the RTS threshold is not configured or out of range, use the + * default value. + */ + if (rts_threshold > IEEE80211_MAX_RTS_THRESHOLD) + rts_threshold = wl->conf.rx.rts_threshold; + + wl1271_debug(DEBUG_ACX, "acx rts threshold: %d", rts_threshold); rts = kzalloc(sizeof(*rts), GFP_KERNEL); if (!rts) { @@ -338,7 +345,7 @@ int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) goto out; } - rts->threshold = cpu_to_le16(rts_threshold); + rts->threshold = cpu_to_le16((u16)rts_threshold); ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); if (ret < 0) { @@ -928,12 +935,19 @@ out: return ret; } -int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold) +int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold) { struct acx_frag_threshold *acx; int ret = 0; - wl1271_debug(DEBUG_ACX, "acx frag threshold"); + /* + * If the fragmentation is not configured or out of range, use the + * default value. + */ + if (frag_threshold > IEEE80211_MAX_FRAG_THRESHOLD) + frag_threshold = wl->conf.tx.frag_threshold; + + wl1271_debug(DEBUG_ACX, "acx frag threshold: %d", frag_threshold); acx = kzalloc(sizeof(*acx), GFP_KERNEL); @@ -942,7 +956,7 @@ int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold) goto out; } - acx->frag_threshold = cpu_to_le16(frag_threshold); + acx->frag_threshold = cpu_to_le16((u16)frag_threshold); ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx)); if (ret < 0) { wl1271_warning("Setting of frag threshold failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index cd7548dacd5..828367d6266 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1333,7 +1333,7 @@ int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time); int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable, void *mc_list, u32 mc_list_len); int wl1271_acx_service_period_timeout(struct wl1271 *wl); -int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); +int wl1271_acx_rts_threshold(struct wl1271 *wl, u32 rts_threshold); int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); @@ -1357,7 +1357,7 @@ int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max, int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type, u8 tsid, u8 ps_scheme, u8 ack_policy, u32 apsd_conf0, u32 apsd_conf1); -int wl1271_acx_frag_threshold(struct wl1271 *wl, u16 frag_threshold); +int wl1271_acx_frag_threshold(struct wl1271 *wl, u32 frag_threshold); int wl1271_acx_tx_config_options(struct wl1271 *wl); int wl1271_acx_ap_mem_cfg(struct wl1271 *wl); int wl1271_acx_sta_mem_cfg(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 1be65691478..060ca31818e 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -258,7 +258,7 @@ int wl1271_init_phy_config(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_rts_threshold(wl, wl->conf.rx.rts_threshold); + ret = wl1271_acx_rts_threshold(wl, wl->hw->wiphy->rts_threshold); if (ret < 0) return ret; @@ -614,7 +614,7 @@ int wl1271_hw_init(struct wl1271 *wl) goto out_free_memmap; /* Default fragmentation threshold */ - ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); + ret = wl1271_acx_frag_threshold(wl, wl->hw->wiphy->frag_threshold); if (ret < 0) goto out_free_memmap; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 5f8bb35c647..81a0c8ed5a4 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -163,7 +163,7 @@ static struct conf_drv_settings default_conf = { .packet_detection_threshold = 0, .ps_poll_timeout = 15, .upsd_timeout = 15, - .rts_threshold = 2347, + .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, .rx_cca_threshold = 0, .irq_blk_threshold = 0xFFFF, .irq_pkt_threshold = 0, @@ -2360,7 +2360,7 @@ static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) if (ret < 0) goto out; - ret = wl1271_acx_frag_threshold(wl, (u16)value); + ret = wl1271_acx_frag_threshold(wl, value); if (ret < 0) wl1271_warning("wl1271_op_set_frag_threshold failed: %d", ret); @@ -2388,7 +2388,7 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) if (ret < 0) goto out; - ret = wl1271_acx_rts_threshold(wl, (u16) value); + ret = wl1271_acx_rts_threshold(wl, value); if (ret < 0) wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret); -- cgit v1.2.3 From 521a4a23261354885c01bf75b42150629004ed83 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:22 +0300 Subject: wl12xx: AP-mode - disable beacon filtering on start up New AP-mode FWs filter external beacons by default. Disable this filtering on start up so we can properly configure ERP protection. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 25 +++++++++++++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 10 +++++++++- drivers/net/wireless/wl12xx/init.c | 8 ++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 729f72a7b91..6860d7e9df7 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1679,6 +1679,31 @@ out: return ret; } +int wl1271_acx_set_ap_beacon_filter(struct wl1271 *wl, bool enable) +{ + struct acx_ap_beacon_filter *acx = NULL; + int ret; + + wl1271_debug(DEBUG_ACX, "acx set ap beacon filter: %d", enable); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) + return -ENOMEM; + + acx->enable = enable ? 1 : 0; + + ret = wl1271_cmd_configure(wl, ACX_AP_BEACON_FILTER_OPT, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx set ap beacon filter failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + int wl1271_acx_fm_coex(struct wl1271 *wl) { struct wl1271_acx_fm_coex *acx; diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 828367d6266..75338f9947c 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -303,7 +303,6 @@ struct acx_beacon_filter_option { struct acx_header header; u8 enable; - /* * The number of beacons without the unicast TIM * bit set that the firmware buffers before @@ -1188,6 +1187,13 @@ struct wl1271_acx_inconnection_sta { u8 padding1[2]; } __packed; +struct acx_ap_beacon_filter { + struct acx_header header; + + u8 enable; + u8 pad[3]; +} __packed; + /* * ACX_FM_COEX_CFG * set the FM co-existence parameters. @@ -1265,6 +1271,7 @@ enum { ACX_TID_CFG = 0x001A, ACX_PS_RX_STREAMING = 0x001B, ACX_BEACON_FILTER_OPT = 0x001F, + ACX_AP_BEACON_FILTER_OPT = 0x0020, ACX_NOISE_HIST = 0x0021, ACX_HDK_VERSION = 0x0022, /* ??? */ ACX_PD_THRESHOLD = 0x0023, @@ -1388,6 +1395,7 @@ int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl); int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); +int wl1271_acx_set_ap_beacon_filter(struct wl1271 *wl, bool enable); int wl1271_acx_fm_coex(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 060ca31818e..e0de041e38f 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -475,6 +475,14 @@ static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) if (ret < 0) return ret; + /* + * when operating as AP we want to receive external beacons for + * configuring ERP protection. + */ + ret = wl1271_acx_set_ap_beacon_filter(wl, false); + if (ret < 0) + return ret; + return 0; } -- cgit v1.2.3 From f482b76202f4ac0f62a77b0e55ac1a1cfe480e2b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:23 +0300 Subject: wl12xx: schedule recovery on command timeout We use a long timeout (2 seconds) when sending commands to the FW. When a command times out, it means the FW is stuck, and we should commence recovery. This should make recovery times shorter as we'll recover on the first timeout indication. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index a9ffdd86f9b..d8596ae3a64 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -76,7 +76,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, if (time_after(jiffies, timeout)) { wl1271_error("command complete timeout"); ret = -ETIMEDOUT; - goto out; + goto fail; } poll_count++; @@ -96,14 +96,17 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len, status = le16_to_cpu(cmd->status); if (status != CMD_STATUS_SUCCESS) { wl1271_error("command execute failure %d", status); - ieee80211_queue_work(wl->hw, &wl->recovery_work); ret = -EIO; + goto fail; } wl1271_write32(wl, ACX_REG_INTERRUPT_ACK, WL1271_ACX_INTR_CMD_COMPLETE); + return 0; -out: +fail: + WARN_ON(1); + ieee80211_queue_work(wl->hw, &wl->recovery_work); return ret; } -- cgit v1.2.3 From 52dcaf577f3b6d878a337a44a99a122017c85ff6 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:24 +0300 Subject: wl12xx: print firmware program counter during recovery When performing recovery, print the firmware version and program counter (by reading the SCR_PAD4 register). The value of the firmware program counter during assert can be useful for debugging. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 81a0c8ed5a4..7b88dd2e85e 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1004,7 +1004,8 @@ static void wl1271_recovery_work(struct work_struct *work) if (wl->state != WL1271_STATE_ON) goto out; - wl1271_info("Hardware recovery in progress."); + wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", + wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ieee80211_connection_loss(wl->vif); -- cgit v1.2.3 From 70f474241b3d5fb633635a2ce39ea9da4afeea6c Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:25 +0300 Subject: wl12xx: AP-mode - overhaul rate policy configuration Use the minimal rate configured in the basic rates set as the AP broadcast and multicast rate. The minimal rate is used to ensure weak links can still communicate. When the basic rates contains at least one OFDM rate, configure all unicast TX rates to OFDM only. Unify rate configuration on initialization and on change notification into a single function. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 3 +- drivers/net/wireless/wl12xx/conf.h | 22 ++++-------- drivers/net/wireless/wl12xx/init.c | 71 ++++++++++++++++++++++++++++---------- drivers/net/wireless/wl12xx/init.h | 1 + drivers/net/wireless/wl12xx/main.c | 53 ++-------------------------- 5 files changed, 65 insertions(+), 85 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 6860d7e9df7..ec0156b3e27 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -841,7 +841,8 @@ int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c, struct acx_ap_rate_policy *acx; int ret = 0; - wl1271_debug(DEBUG_ACX, "acx ap rate policy"); + wl1271_debug(DEBUG_ACX, "acx ap rate policy %d rates 0x%x", + idx, c->enabled_rates); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) { diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 2ffbe3e0601..c0045f0b8b4 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -540,6 +540,12 @@ struct conf_rx_settings { CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ CONF_HW_BIT_RATE_54MBPS) +#define CONF_TX_OFDM_RATES (CONF_HW_BIT_RATE_6MBPS | \ + CONF_HW_BIT_RATE_12MBPS | CONF_HW_BIT_RATE_24MBPS | \ + CONF_HW_BIT_RATE_36MBPS | CONF_HW_BIT_RATE_48MBPS | \ + CONF_HW_BIT_RATE_54MBPS) + + /* * Default rates for management traffic when operating in AP mode. This * should be configured according to the basic rate set of the AP @@ -704,22 +710,6 @@ struct conf_tx_settings { u8 ac_conf_count; struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; - /* - * Configuration for rate classes in AP-mode. These rate classes - * are for the AC TX queues - */ - struct conf_tx_rate_class ap_rc_conf[CONF_TX_MAX_AC_COUNT]; - - /* - * Management TX rate class for AP-mode. - */ - struct conf_tx_rate_class ap_mgmt_conf; - - /* - * Broadcast TX rate class for AP-mode. - */ - struct conf_tx_rate_class ap_bcst_conf; - /* * Allow this number of TX retries to a connected station/AP before an * event is triggered from FW. diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index e0de041e38f..5d0ecd2018b 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -417,7 +417,7 @@ static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl) static int wl1271_ap_hw_init(struct wl1271 *wl) { - int ret, i; + int ret; ret = wl1271_ap_init_templates_config(wl); if (ret < 0) @@ -428,23 +428,7 @@ static int wl1271_ap_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - /* Configure initial TX rate classes */ - for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { - ret = wl1271_acx_ap_rate_policy(wl, - &wl->conf.tx.ap_rc_conf[i], i); - if (ret < 0) - return ret; - } - - ret = wl1271_acx_ap_rate_policy(wl, - &wl->conf.tx.ap_mgmt_conf, - ACX_TX_AP_MODE_MGMT_RATE); - if (ret < 0) - return ret; - - ret = wl1271_acx_ap_rate_policy(wl, - &wl->conf.tx.ap_bcst_conf, - ACX_TX_AP_MODE_BCST_RATE); + ret = wl1271_init_ap_rates(wl); if (ret < 0) return ret; @@ -486,6 +470,57 @@ static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) return 0; } +int wl1271_init_ap_rates(struct wl1271 *wl) +{ + int i, ret; + struct conf_tx_rate_class rc; + u32 supported_rates; + + wl1271_debug(DEBUG_AP, "AP basic rate set: 0x%x", wl->basic_rate_set); + + if (wl->basic_rate_set == 0) + return -EINVAL; + + rc.enabled_rates = wl->basic_rate_set; + rc.long_retry_limit = 10; + rc.short_retry_limit = 10; + rc.aflags = 0; + ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_MGMT_RATE); + if (ret < 0) + return ret; + + /* use the min basic rate for AP broadcast/multicast */ + rc.enabled_rates = wl1271_tx_min_rate_get(wl); + rc.short_retry_limit = 10; + rc.long_retry_limit = 10; + rc.aflags = 0; + ret = wl1271_acx_ap_rate_policy(wl, &rc, ACX_TX_AP_MODE_BCST_RATE); + if (ret < 0) + return ret; + + /* + * If the basic rates contain OFDM rates, use OFDM only + * rates for unicast TX as well. Else use all supported rates. + */ + if ((wl->basic_rate_set & CONF_TX_OFDM_RATES)) + supported_rates = CONF_TX_OFDM_RATES; + else + supported_rates = CONF_TX_AP_ENABLED_RATES; + + /* configure unicast TX rate classes */ + for (i = 0; i < wl->conf.tx.ac_conf_count; i++) { + rc.enabled_rates = supported_rates; + rc.short_retry_limit = 10; + rc.long_retry_limit = 10; + rc.aflags = 0; + ret = wl1271_acx_ap_rate_policy(wl, &rc, i); + if (ret < 0) + return ret; + } + + return 0; +} + static void wl1271_check_ba_support(struct wl1271 *wl) { /* validate FW cose ver x.x.x.50-60.x */ diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h index 4975270a91a..0dd2414a30d 100644 --- a/drivers/net/wireless/wl12xx/init.h +++ b/drivers/net/wireless/wl12xx/init.h @@ -33,5 +33,6 @@ int wl1271_init_pta(struct wl1271 *wl); int wl1271_init_energy_detection(struct wl1271 *wl); int wl1271_chip_specific_init(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl); +int wl1271_init_ap_rates(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 7b88dd2e85e..e9d4cf48ba3 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -209,44 +209,6 @@ static struct conf_drv_settings default_conf = { .tx_op_limit = 1504, }, }, - .ap_rc_conf = { - [0] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - [1] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - [2] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - [3] = { - .enabled_rates = CONF_TX_AP_ENABLED_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - }, - .ap_mgmt_conf = { - .enabled_rates = CONF_TX_AP_DEFAULT_MGMT_RATES, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, - .ap_bcst_conf = { - .enabled_rates = CONF_HW_BIT_RATE_1MBPS, - .short_retry_limit = 10, - .long_retry_limit = 10, - .aflags = 0, - }, .max_tx_retries = 100, .ap_aging_period = 300, .tid_conf_count = 4, @@ -2532,22 +2494,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, if ((changed & BSS_CHANGED_BASIC_RATES)) { u32 rates = bss_conf->basic_rates; - struct conf_tx_rate_class mgmt_rc; wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates); wl->basic_rate = wl1271_tx_min_rate_get(wl); - wl1271_debug(DEBUG_AP, "basic rates: 0x%x", - wl->basic_rate_set); - - /* update the AP management rate policy with the new rates */ - mgmt_rc.enabled_rates = wl->basic_rate_set; - mgmt_rc.long_retry_limit = 10; - mgmt_rc.short_retry_limit = 10; - mgmt_rc.aflags = 0; - ret = wl1271_acx_ap_rate_policy(wl, &mgmt_rc, - ACX_TX_AP_MODE_MGMT_RATE); + + ret = wl1271_init_ap_rates(wl); if (ret < 0) { - wl1271_error("AP mgmt policy change failed %d", ret); + wl1271_error("AP rate policy change failed %d", ret); goto out; } } -- cgit v1.2.3 From c45a85b5a3c0ca841a7ffc700bdece8ee01486be Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:26 +0300 Subject: wl12xx: AP-mode - reconfigure templates after basic rates change When there's a change in the basic rates of the AP, reconfigure relevant templates with the new rates. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/init.c | 7 ++++++- drivers/net/wireless/wl12xx/init.h | 1 + drivers/net/wireless/wl12xx/main.c | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 5d0ecd2018b..b1242a6de27 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -443,7 +443,7 @@ static int wl1271_ap_hw_init(struct wl1271 *wl) return 0; } -static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) +int wl1271_ap_init_templates(struct wl1271 *wl) { int ret; @@ -470,6 +470,11 @@ static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) return 0; } +static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl) +{ + return wl1271_ap_init_templates(wl); +} + int wl1271_init_ap_rates(struct wl1271 *wl) { int i, ret; diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h index 0dd2414a30d..3a3c230fd29 100644 --- a/drivers/net/wireless/wl12xx/init.h +++ b/drivers/net/wireless/wl12xx/init.h @@ -34,5 +34,6 @@ int wl1271_init_energy_detection(struct wl1271 *wl); int wl1271_chip_specific_init(struct wl1271 *wl); int wl1271_hw_init(struct wl1271 *wl); int wl1271_init_ap_rates(struct wl1271 *wl); +int wl1271_ap_init_templates(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e9d4cf48ba3..433bc035741 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2503,6 +2503,10 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, wl1271_error("AP rate policy change failed %d", ret); goto out; } + + ret = wl1271_ap_init_templates(wl); + if (ret < 0) + goto out; } ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, changed); -- cgit v1.2.3 From 2dc5a5c2c656b9029a0e635bb3a1cbcfbcb4ca5c Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:27 +0300 Subject: wl12xx: add debugfs entry for starting recovery This entry is useful for debugging the driver state machine during recovery. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index b17cff6cd75..a2b55e55671 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -291,6 +291,25 @@ static const struct file_operations gpio_power_ops = { .llseek = default_llseek, }; +static ssize_t start_recovery_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + mutex_lock(&wl->mutex); + ieee80211_queue_work(wl->hw, &wl->recovery_work); + mutex_unlock(&wl->mutex); + + return count; +} + +static const struct file_operations start_recovery_ops = { + .write = start_recovery_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static ssize_t dtim_interval_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -529,6 +548,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(excessive_retries, rootdir); DEBUGFS_ADD(gpio_power, rootdir); + DEBUGFS_ADD(start_recovery, rootdir); DEBUGFS_ADD(dtim_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir); -- cgit v1.2.3 From 7dece1c8e1044287287d44ac183a946333b55fc3 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:28 +0300 Subject: wl12xx: fix race condition during recovery in AP mode When operating as AP, the TX queues are not stopped when we start recovery. mac80211 is notified only after the fact. When there is pending TX, it will be queued even after the FW is down. This leads to situations where the TX queues are stopped (because of the TX-watermark mechanism), and are never woken up when we return from recovery. Fix this by explicitly stopping the TX queues when before initiating recovery. Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 21 ++++++++++++++++----- drivers/net/wireless/wl12xx/tx.c | 8 +++++--- drivers/net/wireless/wl12xx/tx.h | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 433bc035741..6dd42c98766 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -352,7 +352,8 @@ static struct conf_drv_settings default_conf = { .hci_io_ds = HCI_IO_DS_6MA, }; -static void __wl1271_op_remove_interface(struct wl1271 *wl); +static void __wl1271_op_remove_interface(struct wl1271 *wl, + bool reset_tx_queues); static void wl1271_free_ap_keys(struct wl1271 *wl); @@ -972,10 +973,19 @@ static void wl1271_recovery_work(struct work_struct *work) if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ieee80211_connection_loss(wl->vif); + /* Prevent spurious TX during FW restart */ + ieee80211_stop_queues(wl->hw); + /* reboot the chipset */ - __wl1271_op_remove_interface(wl); + __wl1271_op_remove_interface(wl, false); ieee80211_restart_hw(wl->hw); + /* + * Its safe to enable TX now - the queues are stopped after a request + * to restart the HW. + */ + ieee80211_wake_queues(wl->hw); + out: mutex_unlock(&wl->mutex); } @@ -1479,7 +1489,8 @@ out: return ret; } -static void __wl1271_op_remove_interface(struct wl1271 *wl) +static void __wl1271_op_remove_interface(struct wl1271 *wl, + bool reset_tx_queues) { int i; @@ -1525,7 +1536,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) mutex_lock(&wl->mutex); /* let's notify MAC80211 about the remaining pending TX frames */ - wl1271_tx_reset(wl); + wl1271_tx_reset(wl, reset_tx_queues); wl1271_power_off(wl); memset(wl->bssid, 0, ETH_ALEN); @@ -1586,7 +1597,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, */ if (wl->vif) { WARN_ON(wl->vif != vif); - __wl1271_op_remove_interface(wl); + __wl1271_op_remove_interface(wl, true); } mutex_unlock(&wl->mutex); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index cc837bba546..ca3ab1c1ace 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -769,8 +769,8 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) wl1271_handle_tx_low_watermark(wl); } -/* caller must hold wl->mutex */ -void wl1271_tx_reset(struct wl1271 *wl) +/* caller must hold wl->mutex and TX must be stopped */ +void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) { int i; struct sk_buff *skb; @@ -806,8 +806,10 @@ void wl1271_tx_reset(struct wl1271 *wl) /* * Make sure the driver is at a consistent state, in case this * function is called from a context other than interface removal. + * This call will always wake the TX queues. */ - wl1271_handle_tx_low_watermark(wl); + if (reset_tx_queues) + wl1271_handle_tx_low_watermark(wl); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) { if (wl->tx_frames[i] == NULL) diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index fc7835c4cf6..832f9258d67 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -185,7 +185,7 @@ static inline int wl1271_tx_get_queue(int queue) void wl1271_tx_work(struct work_struct *work); void wl1271_tx_work_locked(struct wl1271 *wl); void wl1271_tx_complete(struct wl1271 *wl); -void wl1271_tx_reset(struct wl1271 *wl); +void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues); void wl1271_tx_flush(struct wl1271 *wl); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); -- cgit v1.2.3 From 2d66bee7fbd38d28e9ed12f45b8e9db8e6aa0c49 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 18 Apr 2011 14:15:29 +0300 Subject: wl12xx: export driver state to debugfs By reading the "driver_state" debugfs value we get all the important state information from the wl12xx driver. This helps testing and debugging, particularly in situations where the driver seems "stuck". Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index a2b55e55671..b2f692babed 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -310,6 +310,92 @@ static const struct file_operations start_recovery_ops = { .llseek = default_llseek, }; +static ssize_t driver_state_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + int res = 0; + char buf[1024]; + + mutex_lock(&wl->mutex); + +#define DRIVER_STATE_PRINT(x, fmt) \ + (res += scnprintf(buf + res, sizeof(buf) - res,\ + #x " = " fmt "\n", wl->x)) + +#define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld") +#define DRIVER_STATE_PRINT_INT(x) DRIVER_STATE_PRINT(x, "%d") +#define DRIVER_STATE_PRINT_STR(x) DRIVER_STATE_PRINT(x, "%s") +#define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx") +#define DRIVER_STATE_PRINT_HEX(x) DRIVER_STATE_PRINT(x, "0x%x") + + DRIVER_STATE_PRINT_INT(tx_blocks_available); + DRIVER_STATE_PRINT_INT(tx_allocated_blocks); + DRIVER_STATE_PRINT_INT(tx_frames_cnt); + DRIVER_STATE_PRINT_LHEX(tx_frames_map[0]); + DRIVER_STATE_PRINT_INT(tx_queue_count); + DRIVER_STATE_PRINT_INT(tx_packets_count); + DRIVER_STATE_PRINT_INT(tx_results_count); + DRIVER_STATE_PRINT_LHEX(flags); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[0]); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[1]); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[2]); + DRIVER_STATE_PRINT_INT(tx_blocks_freed[3]); + DRIVER_STATE_PRINT_INT(tx_security_last_seq); + DRIVER_STATE_PRINT_INT(rx_counter); + DRIVER_STATE_PRINT_INT(session_counter); + DRIVER_STATE_PRINT_INT(state); + DRIVER_STATE_PRINT_INT(bss_type); + DRIVER_STATE_PRINT_INT(channel); + DRIVER_STATE_PRINT_HEX(rate_set); + DRIVER_STATE_PRINT_HEX(basic_rate_set); + DRIVER_STATE_PRINT_HEX(basic_rate); + DRIVER_STATE_PRINT_INT(band); + DRIVER_STATE_PRINT_INT(beacon_int); + DRIVER_STATE_PRINT_INT(psm_entry_retry); + DRIVER_STATE_PRINT_INT(ps_poll_failures); + DRIVER_STATE_PRINT_HEX(filters); + DRIVER_STATE_PRINT_HEX(rx_config); + DRIVER_STATE_PRINT_HEX(rx_filter); + DRIVER_STATE_PRINT_INT(power_level); + DRIVER_STATE_PRINT_INT(rssi_thold); + DRIVER_STATE_PRINT_INT(last_rssi_event); + DRIVER_STATE_PRINT_INT(sg_enabled); + DRIVER_STATE_PRINT_INT(enable_11a); + DRIVER_STATE_PRINT_INT(noise); + DRIVER_STATE_PRINT_LHEX(ap_hlid_map[0]); + DRIVER_STATE_PRINT_INT(last_tx_hlid); + DRIVER_STATE_PRINT_INT(ba_support); + DRIVER_STATE_PRINT_HEX(ba_rx_bitmap); + DRIVER_STATE_PRINT_HEX(ap_fw_ps_map); + DRIVER_STATE_PRINT_LHEX(ap_ps_map); + DRIVER_STATE_PRINT_HEX(quirks); + DRIVER_STATE_PRINT_HEX(irq); + DRIVER_STATE_PRINT_HEX(ref_clock); + DRIVER_STATE_PRINT_HEX(tcxo_clock); + DRIVER_STATE_PRINT_HEX(hw_pg_ver); + DRIVER_STATE_PRINT_HEX(platform_quirks); + DRIVER_STATE_PRINT_HEX(chip.id); + DRIVER_STATE_PRINT_STR(chip.fw_ver_str); + +#undef DRIVER_STATE_PRINT_INT +#undef DRIVER_STATE_PRINT_LONG +#undef DRIVER_STATE_PRINT_HEX +#undef DRIVER_STATE_PRINT_LHEX +#undef DRIVER_STATE_PRINT_STR +#undef DRIVER_STATE_PRINT + + mutex_unlock(&wl->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, res); +} + +static const struct file_operations driver_state_ops = { + .read = driver_state_read, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static ssize_t dtim_interval_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -549,6 +635,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(gpio_power, rootdir); DEBUGFS_ADD(start_recovery, rootdir); + DEBUGFS_ADD(driver_state, rootdir); DEBUGFS_ADD(dtim_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir); -- cgit v1.2.3 From 25eaea30cd7b009ba2ca693708330d2f395cbc4d Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 2 May 2011 12:37:33 +0300 Subject: Revert "wl12xx: support FW TX inactivity triggers" This reverts commit 47684808fd89d6809c0886e06f8ac324252499d8. Conflicts: drivers/net/wireless/wl12xx/conf.h drivers/net/wireless/wl12xx/main.c --- drivers/net/wireless/wl12xx/acx.c | 34 ++++---------------------- drivers/net/wireless/wl12xx/acx.h | 12 ++------- drivers/net/wireless/wl12xx/boot.c | 6 ++--- drivers/net/wireless/wl12xx/cmd.c | 2 +- drivers/net/wireless/wl12xx/conf.h | 12 ++------- drivers/net/wireless/wl12xx/event.c | 47 ------------------------------------ drivers/net/wireless/wl12xx/event.h | 12 +-------- drivers/net/wireless/wl12xx/init.c | 6 +---- drivers/net/wireless/wl12xx/main.c | 10 ++------ drivers/net/wireless/wl12xx/wl12xx.h | 1 + 10 files changed, 17 insertions(+), 125 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index ec0156b3e27..c6ee530e5bf 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1577,46 +1577,22 @@ out: return ret; } -int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl) +int wl1271_acx_max_tx_retry(struct wl1271 *wl) { - struct wl1271_acx_ap_max_tx_retry *acx = NULL; + struct wl1271_acx_max_tx_retry *acx = NULL; int ret; - wl1271_debug(DEBUG_ACX, "acx ap max tx retry"); + wl1271_debug(DEBUG_ACX, "acx max tx retry"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; - acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries); + acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries); ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx)); if (ret < 0) { - wl1271_warning("acx ap max tx retry failed: %d", ret); - goto out; - } - -out: - kfree(acx); - return ret; -} - -int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl) -{ - struct wl1271_acx_sta_max_tx_retry *acx = NULL; - int ret; - - wl1271_debug(DEBUG_ACX, "acx sta max tx retry"); - - acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) - return -ENOMEM; - - acx->max_tx_retry = wl->conf.tx.max_tx_retries; - - ret = wl1271_cmd_configure(wl, ACX_CONS_TX_FAILURE, acx, sizeof(*acx)); - if (ret < 0) { - wl1271_warning("acx sta max tx retry failed: %d", ret); + wl1271_warning("acx max tx retry failed: %d", ret); goto out; } diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 75338f9947c..9a895e3cc61 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1153,7 +1153,7 @@ struct wl1271_acx_fw_tsf_information { u8 padding[3]; } __packed; -struct wl1271_acx_ap_max_tx_retry { +struct wl1271_acx_max_tx_retry { struct acx_header header; /* @@ -1164,13 +1164,6 @@ struct wl1271_acx_ap_max_tx_retry { u8 padding_1[2]; } __packed; -struct wl1271_acx_sta_max_tx_retry { - struct acx_header header; - - u8 max_tx_retry; - u8 padding_1[3]; -} __packed; - struct wl1271_acx_config_ps { struct acx_header header; @@ -1391,8 +1384,7 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl, int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); -int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl); -int wl1271_acx_sta_max_tx_retry(struct wl1271 *wl); +int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); int wl1271_acx_set_ap_beacon_filter(struct wl1271 *wl, bool enable); diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index d263ebb6f97..2b0cf85788b 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -478,12 +478,10 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) DISCONNECT_EVENT_COMPLETE_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID | - SOFT_GEMINI_SENSE_EVENT_ID | - MAX_TX_RETRY_EVENT_ID; + SOFT_GEMINI_SENSE_EVENT_ID; if (wl->bss_type == BSS_TYPE_AP_BSS) - wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID | - INACTIVE_STA_EVENT_ID; + wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; else wl->event_mask |= DUMMY_PACKET_EVENT_ID; diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index d8596ae3a64..2116a376c3f 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1080,7 +1080,7 @@ int wl1271_cmd_start_bss(struct wl1271 *wl) memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN); - cmd->aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period); + cmd->aging_period = cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC); cmd->bss_index = WL1271_AP_BSS_INDEX; cmd->global_hlid = WL1271_AP_GLOBAL_HLID; cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID; diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index c0045f0b8b4..1f947368f9e 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -711,18 +711,10 @@ struct conf_tx_settings { struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT]; /* - * Allow this number of TX retries to a connected station/AP before an + * AP-mode - allow this number of TX retries to a station before an * event is triggered from FW. - * In AP-mode the hlids of unreachable stations are given in the - * "sta_tx_retry_exceeded" member in the event mailbox. */ - u8 max_tx_retries; - - /* - * AP-mode - after this number of seconds a connected station is - * considered inactive. - */ - u16 ap_aging_period; + u16 ap_max_tx_retries; /* * Configuration for TID parameters. diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index d7be3aec6fc..ae69330e807 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -174,8 +174,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) u32 vector; bool beacon_loss = false; bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); - bool disconnect_sta = false; - unsigned long sta_bitmap = 0; wl1271_event_mbox_dump(mbox); @@ -237,54 +235,9 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) wl1271_tx_dummy_packet(wl); } - /* - * "TX retries exceeded" has a different meaning according to mode. - * In AP mode the offending station is disconnected. In STA mode we - * report connection loss. - */ - if (vector & MAX_TX_RETRY_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); - if (is_ap) { - sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); - disconnect_sta = true; - } else { - beacon_loss = true; - } - } - - if ((vector & INACTIVE_STA_EVENT_ID) && is_ap) { - wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); - sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); - disconnect_sta = true; - } - if (wl->vif && beacon_loss) ieee80211_connection_loss(wl->vif); - if (is_ap && disconnect_sta) { - u32 num_packets = wl->conf.tx.max_tx_retries; - struct ieee80211_sta *sta; - const u8 *addr; - int h; - - for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS); - h < AP_MAX_LINKS; - h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) { - if (!wl1271_is_active_sta(wl, h)) - continue; - - addr = wl->links[h].addr; - - rcu_read_lock(); - sta = ieee80211_find_sta(wl->vif, addr); - if (sta) { - wl1271_debug(DEBUG_EVENT, "remove sta %d", h); - ieee80211_report_low_ack(sta, num_packets); - } - rcu_read_unlock(); - } - } - return 0; } diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index 7ae5a082124..b6cf06e565a 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -58,16 +58,13 @@ enum { CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), - MAX_TX_RETRY_EVENT_ID = BIT(20), + ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20), /* STA: dummy paket for dynamic mem blocks */ DUMMY_PACKET_EVENT_ID = BIT(21), /* AP: STA remove complete */ STA_REMOVE_COMPLETE_EVENT_ID = BIT(21), SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), - /* STA: SG prediction */ SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23), - /* AP: Inactive STA */ - INACTIVE_STA_EVENT_ID = BIT(23), SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25), DBG_EVENT_ID = BIT(26), @@ -122,11 +119,7 @@ struct event_mailbox { /* AP FW only */ u8 hlid_removed; - - /* a bitmap of hlids for stations that have been inactive too long */ __le16 sta_aging_status; - - /* a bitmap of hlids for stations which didn't respond to TX */ __le16 sta_tx_retry_exceeded; u8 reserved_5[24]; @@ -137,7 +130,4 @@ void wl1271_event_mbox_config(struct wl1271 *wl); int wl1271_event_handle(struct wl1271 *wl, u8 mbox); void wl1271_pspoll_work(struct work_struct *work); -/* Functions from main.c */ -bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid); - #endif diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index b1242a6de27..a8f4f156c05 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -378,10 +378,6 @@ static int wl1271_sta_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_sta_max_tx_retry(wl); - if (ret < 0) - return ret; - ret = wl1271_acx_sta_mem_cfg(wl); if (ret < 0) return ret; @@ -432,7 +428,7 @@ static int wl1271_ap_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_ap_max_tx_retry(wl); + ret = wl1271_acx_max_tx_retry(wl); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 6dd42c98766..6dab6f0c91b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -209,8 +209,7 @@ static struct conf_drv_settings default_conf = { .tx_op_limit = 1504, }, }, - .max_tx_retries = 100, - .ap_aging_period = 300, + .ap_max_tx_retries = 100, .tid_conf_count = 4, .tid_conf = { [CONF_TX_AC_BE] = { @@ -3021,12 +3020,6 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); } -bool wl1271_is_active_sta(struct wl1271 *wl, u8 hlid) -{ - int id = hlid - WL1271_AP_STA_HLID_START; - return test_bit(id, wl->ap_hlid_map); -} - static int wl1271_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) @@ -3632,6 +3625,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_AP_LINK_PS; wl->hw->wiphy->cipher_suites = cipher_suites; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index f3de96212b9..b7601438eca 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -172,6 +172,7 @@ extern u32 wl12xx_debug_level; #define WL1271_PS_STA_MAX_BLOCKS (2 * 9) #define WL1271_AP_BSS_INDEX 0 +#define WL1271_AP_DEF_INACTIV_SEC 300 #define WL1271_AP_DEF_BEACON_EXP 20 #define ACX_TX_DESCRIPTORS 32 -- cgit v1.2.3 From 889cb360b4f48c1334311093161f06f7b4bd77d2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 1 May 2011 09:56:45 +0300 Subject: wl12xx: simplify wl1271_ssid_set() Simplify wl1271_ssid_set by re-using cfg80211_find_ie instead of reimplementing it. Additionally, add a length check to prevent a potential buffer overflow. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 6dab6f0c91b..f82e736ba19 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2376,20 +2376,24 @@ out: static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, int offset) { - u8 *ptr = skb->data + offset; + u8 ssid_len; + const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, + skb->len - offset); - /* find the location of the ssid in the beacon */ - while (ptr < skb->data + skb->len) { - if (ptr[0] == WLAN_EID_SSID) { - wl->ssid_len = ptr[1]; - memcpy(wl->ssid, ptr+2, wl->ssid_len); - return 0; - } - ptr += (ptr[1] + 2); + if (!ptr) { + wl1271_error("No SSID in IEs!"); + return -ENOENT; } - wl1271_error("No SSID in IEs!\n"); - return -ENOENT; + ssid_len = ptr[1]; + if (ssid_len > IEEE80211_MAX_SSID_LEN) { + wl1271_error("SSID is too long!"); + return -EINVAL; + } + + wl->ssid_len = ssid_len; + memcpy(wl->ssid, ptr+2, ssid_len); + return 0; } static int wl1271_bss_erp_info_changed(struct wl1271 *wl, -- cgit v1.2.3 From bc76b94051983b94c8ba04fbfbc59651b9925fa7 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Wed, 11 May 2011 11:14:22 +0300 Subject: wl12xx: Don't filter beacons that include changed HT IEs This patch adds a beacon filter rule to pass up the beacons that contain changed HT information elements. These beacons need to be passed to mac80211 so that it can act on such changes. [Reworded commit log -- Luca.] Signed-off-by: Shahar Levi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f82e736ba19..fa6b996d7d4 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -257,12 +257,16 @@ static struct conf_drv_settings default_conf = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .listen_interval = 1, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, - .bcn_filt_ie_count = 1, + .bcn_filt_ie_count = 2, .bcn_filt_ie = { [0] = { .ie = WLAN_EID_CHANNEL_SWITCH, .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, - } + }, + [1] = { + .ie = WLAN_EID_HT_INFORMATION, + .rule = CONF_BCN_RULE_PASS_ON_CHANGE, + }, }, .synch_fail_thold = 10, .bss_lose_timeout = 100, -- cgit v1.2.3 From fcd23b6305e98f5ad3ddd7ff3f5081c75fcd4367 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Wed, 11 May 2011 12:12:56 +0300 Subject: wl12xx: add IEEE80211_HW_SPECTRUM_MGMT bit to the hw flags Set the spectrum management bit in the hw flags so that mac80211 will set the WLAN_CAPABILITY_SPECTRUM_MGMT bit in association requests (which in practice means that we support 802.11h spectrum management). [Reworded the commit log -- Luca.] Signed-off-by: Shahar Levi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fa6b996d7d4..acfbbfd3b7d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3634,6 +3634,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI | IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_AP_LINK_PS; wl->hw->wiphy->cipher_suites = cipher_suites; -- cgit v1.2.3 From 3a9d60e5bd72f9533b05d39278fec50b181dbdd2 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 10 May 2011 14:06:31 +0300 Subject: wl12xx: add configuration values for scheduled scan Add the structures and values for driver-configured scheduled scan parameters. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 21 +++++++++++++++++++++ drivers/net/wireless/wl12xx/main.c | 9 +++++++++ 2 files changed, 30 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 1f947368f9e..ba558fcc76d 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1147,6 +1147,26 @@ struct conf_scan_settings { }; +struct conf_sched_scan_settings { + /* minimum time to wait on the channel for active scans (in TUs) */ + u16 min_dwell_time_active; + + /* maximum time to wait on the channel for active scans (in TUs) */ + u16 max_dwell_time_active; + + /* time to wait on the channel for passive scans (in TUs) */ + u32 dwell_time_passive; + + /* number of probe requests to send on each channel in active scans */ + u8 num_probe_reqs; + + /* RSSI threshold to be used for filtering */ + s8 rssi_threshold; + + /* SNR threshold to be used for filtering */ + s8 snr_threshold; +}; + /* these are number of channels on the band divided by two, rounded up */ #define CONF_TX_PWR_COMPENSATION_LEN_2 7 #define CONF_TX_PWR_COMPENSATION_LEN_5 18 @@ -1234,6 +1254,7 @@ struct conf_drv_settings { struct conf_pm_config_settings pm_config; struct conf_roam_trigger_settings roam_trigger; struct conf_scan_settings scan; + struct conf_sched_scan_settings sched_scan; struct conf_rf_settings rf; struct conf_ht_setting ht; struct conf_memory_settings mem_wl127x; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index acfbbfd3b7d..88d2e9052a0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -306,6 +306,15 @@ static struct conf_drv_settings default_conf = { .max_dwell_time_passive = 100000, .num_probe_reqs = 2, }, + .sched_scan = { + /* sched_scan requires dwell times in TU instead of TU/1000 */ + .min_dwell_time_active = 8, + .max_dwell_time_active = 30, + .dwell_time_passive = 100, + .num_probe_reqs = 2, + .rssi_threshold = -90, + .snr_threshold = 0, + }, .rf = { .tx_per_channel_power_compensation_2 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -- cgit v1.2.3 From 6394c01b61f8ab66a6af1a24ff05f2429130afcd Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 10 May 2011 14:28:27 +0300 Subject: wl12xx: listen to scheduled scan events Subscribe and listen to PERIODIC_SCAN_REPORT_EVENT_ID and PERIODIC_SCAN_COMPLETE_EVENT_ID in preparation for the scheduled scan implementation. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 4 +++- drivers/net/wireless/wl12xx/event.c | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 2b0cf85788b..b07f8b7e5f1 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -478,7 +478,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) DISCONNECT_EVENT_COMPLETE_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID | - SOFT_GEMINI_SENSE_EVENT_ID; + SOFT_GEMINI_SENSE_EVENT_ID | + PERIODIC_SCAN_REPORT_EVENT_ID | + PERIODIC_SCAN_COMPLETE_EVENT_ID; if (wl->bss_type == BSS_TYPE_AP_BSS) wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index ae69330e807..dc110e8a618 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -188,6 +188,16 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) wl1271_scan_stm(wl); } + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " + "(status 0x%0x)", mbox->scheduled_scan_status); + } + + if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " + "(status 0x%0x)", mbox->scheduled_scan_status); + } + /* disable dynamic PS when requested by the firmware */ if (vector & SOFT_GEMINI_SENSE_EVENT_ID && wl->bss_type == BSS_TYPE_STA_BSS) { -- cgit v1.2.3 From 95feadca6dca909ae0f6e65665b782c7ca9d5122 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 10 May 2011 14:38:59 +0300 Subject: wl12xx: add scheduled scan structures and commands Add firmware command structures, definitions and code to to configure, start and stop scheduled scans. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/scan.c | 233 +++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/scan.h | 114 ++++++++++++++++++ 2 files changed, 347 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 5d0544c8f3f..d78044f0081 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -320,3 +320,236 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, return 0; } + +static int +wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct conn_scan_ch_params *channels, + u32 band, bool radar, bool passive, + int start) +{ + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + int i, j; + u32 flags; + + for (i = 0, j = start; + i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS; + i++) { + flags = req->channels[i]->flags; + + if (!(flags & IEEE80211_CHAN_DISABLED) && + ((flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive) && + ((flags & IEEE80211_CHAN_RADAR) == radar) && + (req->channels[i]->band == band)) { + wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", + req->channels[i]->band, + req->channels[i]->center_freq); + wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", + req->channels[i]->hw_value, + req->channels[i]->flags); + wl1271_debug(DEBUG_SCAN, "max_power %d", + req->channels[i]->max_power); + + if (flags & IEEE80211_CHAN_PASSIVE_SCAN) { + channels[j].passive_duration = + cpu_to_le16(c->dwell_time_passive); + } else { + channels[j].min_duration = + cpu_to_le16(c->min_dwell_time_active); + channels[j].max_duration = + cpu_to_le16(c->max_dwell_time_active); + } + channels[j].tx_power_att = req->channels[j]->max_power; + channels[j].channel = req->channels[i]->hw_value; + + j++; + } + } + + return j - start; +} + +static int +wl1271_scan_sched_scan_channels(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct wl1271_cmd_sched_scan_config *cfg) +{ + int idx = 0; + + cfg->passive[0] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_2GHZ, + false, true, idx); + idx += cfg->passive[0]; + + cfg->active[0] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_2GHZ, + false, false, idx); + idx += cfg->active[0]; + + cfg->passive[1] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_5GHZ, + false, true, idx); + idx += cfg->passive[1]; + + cfg->active[1] = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_5GHZ, + false, false, 14); + idx += cfg->active[1]; + + cfg->dfs = + wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, + IEEE80211_BAND_5GHZ, + true, false, idx); + idx += cfg->dfs; + + wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", + cfg->active[0], cfg->passive[0]); + wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", + cfg->active[1], cfg->passive[1]); + + return idx; +} + +int wl1271_scan_sched_scan_config(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct wl1271_cmd_sched_scan_config *cfg = NULL; + struct conf_sched_scan_settings *c = &wl->conf.sched_scan; + int i, total_channels, ret; + + wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->rssi_threshold = c->rssi_threshold; + cfg->snr_threshold = c->snr_threshold; + cfg->n_probe_reqs = c->num_probe_reqs; + /* cycles set to 0 it means infinite (until manually stopped) */ + cfg->cycles = 0; + /* report APs when at least 1 is found */ + cfg->report_after = 1; + /* don't stop scanning automatically when something is found */ + cfg->terminate = 0; + cfg->tag = WL1271_SCAN_DEFAULT_TAG; + /* don't filter on BSS type */ + cfg->bss_type = SCAN_BSS_TYPE_ANY; + /* currently NL80211 supports only a single interval */ + for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) + cfg->intervals[i] = cpu_to_le32(req->interval); + + if (req->ssids[0].ssid_len && req->ssids[0].ssid) { + cfg->filter_type = SCAN_SSID_FILTER_SPECIFIC; + cfg->ssid_len = req->ssids[0].ssid_len; + memcpy(cfg->ssid, req->ssids[0].ssid, + req->ssids[0].ssid_len); + } else { + cfg->filter_type = SCAN_SSID_FILTER_ANY; + cfg->ssid_len = 0; + } + + total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg); + if (total_channels == 0) { + wl1271_error("scan channel list is empty"); + ret = -EINVAL; + goto out; + } + + if (cfg->active[0]) { + ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[IEEE80211_BAND_2GHZ], + ies->len[IEEE80211_BAND_2GHZ], + IEEE80211_BAND_2GHZ); + if (ret < 0) { + wl1271_error("2.4GHz PROBE request template failed"); + goto out; + } + } + + if (cfg->active[1]) { + ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, + req->ssids[0].ssid_len, + ies->ie[IEEE80211_BAND_5GHZ], + ies->len[IEEE80211_BAND_5GHZ], + IEEE80211_BAND_5GHZ); + if (ret < 0) { + wl1271_error("5GHz PROBE request template failed"); + goto out; + } + } + + wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); + + ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, + sizeof(*cfg), 0); + if (ret < 0) { + wl1271_error("SCAN configuration failed"); + goto out; + } +out: + kfree(cfg); + return ret; +} + +int wl1271_scan_sched_scan_start(struct wl1271 *wl) +{ + struct wl1271_cmd_sched_scan_start *start; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); + + start = kzalloc(sizeof(*start), GFP_KERNEL); + if (!start) + return -ENOMEM; + + start->tag = WL1271_SCAN_DEFAULT_TAG; + + ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, + sizeof(*start), 0); + if (ret < 0) { + wl1271_error("failed to send scan start command"); + goto out_free; + } + +out_free: + kfree(start); + return ret; +} + +void wl1271_scan_sched_scan_results(struct wl1271 *wl) +{ + wl1271_debug(DEBUG_SCAN, "got periodic scan results"); + + ieee80211_sched_scan_results(wl->hw); +} + +void wl1271_scan_sched_scan_stop(struct wl1271 *wl) +{ + struct wl1271_cmd_sched_scan_stop *stop; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); + + /* FIXME: what to do if alloc'ing to stop fails? */ + stop = kzalloc(sizeof(*stop), GFP_KERNEL); + if (!stop) { + wl1271_error("failed to alloc memory to send sched scan stop"); + return; + } + + stop->tag = WL1271_SCAN_DEFAULT_TAG; + + ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, + sizeof(*stop), 0); + if (ret < 0) + wl1271_error("failed to send sched scan stop command"); + + kfree(stop); +} diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h index 421a750add5..c83319579ca 100644 --- a/drivers/net/wireless/wl12xx/scan.h +++ b/drivers/net/wireless/wl12xx/scan.h @@ -33,6 +33,12 @@ int wl1271_scan_build_probe_req(struct wl1271 *wl, const u8 *ie, size_t ie_len, u8 band); void wl1271_scan_stm(struct wl1271 *wl); void wl1271_scan_complete_work(struct work_struct *work); +int wl1271_scan_sched_scan_config(struct wl1271 *wl, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +int wl1271_scan_sched_scan_start(struct wl1271 *wl); +void wl1271_scan_sched_scan_stop(struct wl1271 *wl); +void wl1271_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_MAX_CHANNELS 24 #define WL1271_SCAN_DEFAULT_TAG 1 @@ -106,4 +112,112 @@ struct wl1271_cmd_trigger_scan_to { __le32 timeout; } __packed; +#define MAX_CHANNELS_ALL_BANDS 41 +#define SCAN_MAX_CYCLE_INTERVALS 16 +#define SCAN_MAX_BANDS 3 + +enum { + SCAN_CHANNEL_TYPE_2GHZ_PASSIVE, + SCAN_CHANNEL_TYPE_2GHZ_ACTIVE, + SCAN_CHANNEL_TYPE_5GHZ_PASSIVE, + SCAN_CHANNEL_TYPE_5GHZ_ACTIVE, + SCAN_CHANNEL_TYPE_5GHZ_DFS, +}; + +enum { + SCAN_SSID_FILTER_ANY = 0, + SCAN_SSID_FILTER_SPECIFIC = 1, + SCAN_SSID_FILTER_LIST = 2, + SCAN_SSID_FILTER_DISABLED = 3 +}; + +enum { + SCAN_BSS_TYPE_INDEPENDENT, + SCAN_BSS_TYPE_INFRASTRUCTURE, + SCAN_BSS_TYPE_ANY, +}; + +struct conn_scan_ch_params { + __le16 min_duration; + __le16 max_duration; + __le16 passive_duration; + + u8 channel; + u8 tx_power_att; + + /* bit 0: DFS channel; bit 1: DFS enabled */ + u8 flags; + + u8 padding[3]; +} __packed; + +struct wl1271_cmd_sched_scan_config { + struct wl1271_cmd_header header; + + __le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; + + s8 rssi_threshold; /* for filtering (in dBm) */ + s8 snr_threshold; /* for filtering (in dB) */ + + u8 cycles; /* maximum number of scan cycles */ + u8 report_after; /* report when this number of results are received */ + u8 terminate; /* stop scanning after reporting */ + + u8 tag; + u8 bss_type; /* for filtering */ + u8 filter_type; + + u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ + u8 ssid[IW_ESSID_MAX_SIZE]; + + u8 n_probe_reqs; /* Number of probes requests per channel */ + + u8 passive[SCAN_MAX_BANDS]; + u8 active[SCAN_MAX_BANDS]; + + u8 dfs; + + u8 padding[3]; + + struct conn_scan_ch_params channels[MAX_CHANNELS_ALL_BANDS]; +} __packed; + + +#define SCHED_SCAN_MAX_SSIDS 8 + +enum { + SCAN_SSID_TYPE_PUBLIC = 0, + SCAN_SSID_TYPE_HIDDEN = 1, +}; + +struct wl1271_ssid { + u8 type; + u8 len; + u8 ssid[IW_ESSID_MAX_SIZE]; + /* u8 padding[2]; */ +} __packed; + +struct wl1271_cmd_sched_scan_ssid_list { + struct wl1271_cmd_header header; + + u8 n_ssids; + struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS]; + u8 padding[3]; +} __packed; + +struct wl1271_cmd_sched_scan_start { + struct wl1271_cmd_header header; + + u8 tag; + u8 padding[3]; +} __packed; + +struct wl1271_cmd_sched_scan_stop { + struct wl1271_cmd_header header; + + u8 tag; + u8 padding[3]; +} __packed; + + #endif /* __WL1271_SCAN_H__ */ -- cgit v1.2.3 From 33c2c06cd2d766387cf919d0afd432cc5796c369 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 10 May 2011 14:46:02 +0300 Subject: wl12xx: implement scheduled scan driver operations and reporting This patch adds the mac80211 operations for scheduled scan and the scheduled scan results reporting. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/event.c | 6 ++++ drivers/net/wireless/wl12xx/main.c | 70 ++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/scan.c | 6 +++- drivers/net/wireless/wl12xx/wl12xx.h | 2 ++ 4 files changed, 83 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index dc110e8a618..1e4bd6a2c39 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -191,11 +191,17 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " "(status 0x%0x)", mbox->scheduled_scan_status); + + wl1271_scan_sched_scan_results(wl); } if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " "(status 0x%0x)", mbox->scheduled_scan_status); + if (wl->sched_scanning) { + wl1271_scan_sched_scan_stop(wl); + ieee80211_sched_scan_stopped(wl->hw); + } } /* disable dynamic PS when requested by the firmware */ diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 88d2e9052a0..a14a035aa44 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -988,6 +988,11 @@ static void wl1271_recovery_work(struct work_struct *work) /* Prevent spurious TX during FW restart */ ieee80211_stop_queues(wl->hw); + if (wl->sched_scanning) { + ieee80211_sched_scan_stopped(wl->hw); + wl->sched_scanning = false; + } + /* reboot the chipset */ __wl1271_op_remove_interface(wl, false); ieee80211_restart_hw(wl->hw); @@ -1576,6 +1581,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); wl->ap_fw_ps_map = 0; wl->ap_ps_map = 0; + wl->sched_scanning = false; /* * this is performed after the cancel_work calls and the associated @@ -1778,6 +1784,13 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle) wl->session_counter++; if (wl->session_counter >= SESSION_COUNTER_MAX) wl->session_counter = 0; + + /* The current firmware only supports sched_scan in idle */ + if (wl->sched_scanning) { + wl1271_scan_sched_scan_stop(wl); + ieee80211_sched_scan_stopped(wl->hw); + } + ret = wl1271_dummy_join(wl); if (ret < 0) goto out; @@ -2330,6 +2343,60 @@ out: return ret; } +static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct wl1271 *wl = hw->priv; + int ret; + + wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start"); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_scan_sched_scan_config(wl, req, ies); + if (ret < 0) + goto out_sleep; + + ret = wl1271_scan_sched_scan_start(wl); + if (ret < 0) + goto out_sleep; + + wl->sched_scanning = true; + +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return ret; +} + +static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wl1271 *wl = hw->priv; + int ret; + + wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop"); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl1271_scan_sched_scan_stop(wl); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) { struct wl1271 *wl = hw->priv; @@ -3445,6 +3512,8 @@ static const struct ieee80211_ops wl1271_ops = { .tx = wl1271_op_tx, .set_key = wl1271_op_set_key, .hw_scan = wl1271_op_hw_scan, + .sched_scan_start = wl1271_op_sched_scan_start, + .sched_scan_stop = wl1271_op_sched_scan_stop, .bss_info_changed = wl1271_op_bss_info_changed, .set_frag_threshold = wl1271_op_set_frag_threshold, .set_rts_threshold = wl1271_op_set_rts_threshold, @@ -3765,6 +3834,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_fw_ps_map = 0; wl->quirks = 0; wl->platform_quirks = 0; + wl->sched_scanning = false; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index d78044f0081..668ff46a682 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -548,8 +548,12 @@ void wl1271_scan_sched_scan_stop(struct wl1271 *wl) ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, sizeof(*stop), 0); - if (ret < 0) + if (ret < 0) { wl1271_error("failed to send sched scan stop command"); + goto out_free; + } + wl->sched_scanning = false; +out_free: kfree(stop); } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index b7601438eca..10f076770fe 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -480,6 +480,8 @@ struct wl1271 { struct wl1271_scan scan; struct delayed_work scan_complete_work; + bool sched_scanning; + /* probe-req template for the current AP */ struct sk_buff *probereq; -- cgit v1.2.3 From d3eff81de6048d8af8f95f52f0f06625980f2efb Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 10 May 2011 14:47:45 +0300 Subject: wl12xx: export scheduled scan state in debugfs Add the sched_scanning value to the driver_status debugfs entry. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index b2f692babed..f1f8df9b6cd 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -377,6 +377,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_HEX(platform_quirks); DRIVER_STATE_PRINT_HEX(chip.id); DRIVER_STATE_PRINT_STR(chip.fw_ver_str); + DRIVER_STATE_PRINT_INT(sched_scanning); #undef DRIVER_STATE_PRINT_INT #undef DRIVER_STATE_PRINT_LONG -- cgit v1.2.3 From 683c002447c12742f5151691083f68524f33b13a Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 12 May 2011 17:07:55 +0300 Subject: wl12xx: prevent sched_scan when not idle or not in station mode The current firmware only supports scheduled scan in station mode and when idle. To prevent the firmware from crashing, return -EOPNOTSUPP when sched_scan start is called in an invalid state. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/scan.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 668ff46a682..f37e5a39197 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -505,6 +505,12 @@ int wl1271_scan_sched_scan_start(struct wl1271 *wl) wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); + if (wl->bss_type != BSS_TYPE_STA_BSS) + return -EOPNOTSUPP; + + if (!test_bit(WL1271_FLAG_IDLE, &wl->flags)) + return -EBUSY; + start = kzalloc(sizeof(*start), GFP_KERNEL); if (!start) return -ENOMEM; -- cgit v1.2.3 From fe44870bcdf614e4abb35657c68081cda35ba741 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 12 May 2011 16:50:41 +0300 Subject: wl12xx: remove unused flag WL1271_FLAG_IDLE_REQUESTED This flag is not used anymore, remove it from the list. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl12xx.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 10f076770fe..ab0c2f155b8 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -351,7 +351,6 @@ enum wl12xx_flags { WL1271_FLAG_PSM_REQUESTED, WL1271_FLAG_IRQ_RUNNING, WL1271_FLAG_IDLE, - WL1271_FLAG_IDLE_REQUESTED, WL1271_FLAG_PSPOLL_FAILURE, WL1271_FLAG_STA_STATE_SENT, WL1271_FLAG_FW_TX_BUSY, -- cgit v1.2.3 From 2c0f24636c80aa09990c507c0cede39add4b4724 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 13 May 2011 11:57:08 +0300 Subject: wl12xx_sdio: set interrupt as wake_up interrupt set the sdio interrupt as wake_up interrupt, so we will be able to wake up the suspended system (Wake-On-Wireless) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index bcd4ad7ba90..1298461c45d 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -267,6 +267,8 @@ static int __devinit wl1271_probe(struct sdio_func *func, goto out_free; } + enable_irq_wake(wl->irq); + disable_irq(wl->irq); ret = wl1271_init_ieee80211(wl); @@ -303,6 +305,7 @@ static void __devexit wl1271_remove(struct sdio_func *func) pm_runtime_get_noresume(&func->dev); wl1271_unregister_hw(wl); + disable_irq_wake(wl->irq); free_irq(wl->irq, wl); wl1271_free_hw(wl); } -- cgit v1.2.3 From 402e48616078c1e56f55a69d314b77f1d750d6ad Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 13 May 2011 11:57:09 +0300 Subject: wl12xx: declare suspend/resume callbacks (for wowlan) Additionally, add wow_enabled field to wl, to indicate whether wowlan was configured. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 19 +++++++++++++++++++ drivers/net/wireless/wl12xx/wl12xx.h | 6 ++++++ 2 files changed, 25 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a14a035aa44..4b421d80187 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1350,6 +1350,23 @@ static struct notifier_block wl1271_dev_notifier = { .notifier_call = wl1271_dev_notify, }; +static int wl1271_op_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wow) +{ + struct wl1271 *wl = hw->priv; + wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); + wl->wow_enabled = !!wow; + return 0; +} + +static int wl1271_op_resume(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", + wl->wow_enabled); + return 0; +} + static int wl1271_op_start(struct ieee80211_hw *hw) { wl1271_debug(DEBUG_MAC80211, "mac80211 start"); @@ -3506,6 +3523,8 @@ static const struct ieee80211_ops wl1271_ops = { .stop = wl1271_op_stop, .add_interface = wl1271_op_add_interface, .remove_interface = wl1271_op_remove_interface, + .suspend = wl1271_op_suspend, + .resume = wl1271_op_resume, .config = wl1271_op_config, .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index ab0c2f155b8..9629e90d9b5 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -564,6 +564,12 @@ struct wl1271 { int tcxo_clock; + /* + * wowlan trigger was configured during suspend. + * (currently, only "ANY" trigger is supported) + */ + bool wow_enabled; + /* * AP-mode - links indexed by HLID. The global and broadcast links * are always active. -- cgit v1.2.3 From 039bdb1494d1d514987ce596a4898494021c7af2 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 13 May 2011 11:57:10 +0300 Subject: wl12xx_sdio: set MMC_PM_KEEP_POWER flag on suspend if a wow trigger was configured, set the MMC_PM_KEEP_POWER flag on suspend, so our power will be kept while the system is suspended. We needed to set this flag on each suspend attempt (when we want to keep power) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 1298461c45d..5b03fd5ee33 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -314,7 +314,34 @@ static int wl1271_suspend(struct device *dev) { /* Tell MMC/SDIO core it's OK to power down the card * (if it isn't already), but not to remove it completely */ - return 0; + struct sdio_func *func = dev_to_sdio_func(dev); + struct wl1271 *wl = sdio_get_drvdata(func); + mmc_pm_flag_t sdio_flags; + int ret = 0; + + wl1271_debug(DEBUG_MAC80211, "wl1271 suspend. wow_enabled: %d", + wl->wow_enabled); + + /* check whether sdio should keep power */ + if (wl->wow_enabled) { + sdio_flags = sdio_get_host_pm_caps(func); + + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + wl1271_error("can't keep power while host " + "is suspended"); + ret = -EINVAL; + goto out; + } + + /* keep power while host suspended */ + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) { + wl1271_error("error while trying to keep power"); + goto out; + } + } +out: + return ret; } static int wl1271_resume(struct device *dev) -- cgit v1.2.3 From f44e58681aec420b132a54823d8911293a644d4e Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 13 May 2011 11:57:11 +0300 Subject: wl12xx: prevent scheduling while suspending (WoW enabled) When WoW is enabled, the interface will stay up and the chip will be powered on, so we have to flush/cancel any remaining work, and prevent the irq handler from scheduling a new work until the system is resumed. Add 2 new flags: * WL1271_FLAG_SUSPENDED - the system is (about to be) suspended. * WL1271_FLAG_PENDING_WORK - there is a pending irq work which should be scheduled when the system is being resumed. In order to wake-up the system while getting an irq, we initialize the device as wakeup device, and calling pm_wakeup_event() upon getting the interrupt (while the system is about to be suspended) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 46 ++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/sdio.c | 24 +++++++++++++++++++ drivers/net/wireless/wl12xx/wl12xx.h | 2 ++ 3 files changed, 72 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 4b421d80187..8f9e6152f3b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1356,6 +1356,28 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); wl->wow_enabled = !!wow; + if (wl->wow_enabled) { + /* flush any remaining work */ + wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); + flush_delayed_work(&wl->scan_complete_work); + + /* + * disable and re-enable interrupts in order to flush + * the threaded_irq + */ + wl1271_disable_interrupts(wl); + + /* + * set suspended flag to avoid triggering a new threaded_irq + * work. no need for spinlock as interrupts are disabled. + */ + set_bit(WL1271_FLAG_SUSPENDED, &wl->flags); + + wl1271_enable_interrupts(wl); + flush_work(&wl->tx_work); + flush_delayed_work(&wl->pspoll_work); + flush_delayed_work(&wl->elp_work); + } return 0; } @@ -1364,6 +1386,30 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) struct wl1271 *wl = hw->priv; wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", wl->wow_enabled); + + /* + * re-enable irq_work enqueuing, and call irq_work directly if + * there is a pending work. + */ + if (wl->wow_enabled) { + struct wl1271 *wl = hw->priv; + unsigned long flags; + bool run_irq_work = false; + + spin_lock_irqsave(&wl->wl_lock, flags); + clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags); + if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags)) + run_irq_work = true; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + if (run_irq_work) { + wl1271_debug(DEBUG_MAC80211, + "run postponed irq_work directly"); + wl1271_irq(0, wl); + wl1271_enable_interrupts(wl); + } + } + return 0; } diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 5b03fd5ee33..41183db3483 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -82,6 +82,16 @@ static irqreturn_t wl1271_hardirq(int irq, void *cookie) complete(wl->elp_compl); wl->elp_compl = NULL; } + + if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) { + /* don't enqueue a work right now. mark it as pending */ + set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags); + wl1271_debug(DEBUG_IRQ, "should not enqueue work"); + disable_irq_nosync(wl->irq); + pm_wakeup_event(wl1271_sdio_wl_to_dev(wl), 0); + spin_unlock_irqrestore(&wl->wl_lock, flags); + return IRQ_HANDLED; + } spin_unlock_irqrestore(&wl->wl_lock, flags); return IRQ_WAKE_THREAD; @@ -268,6 +278,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, } enable_irq_wake(wl->irq); + device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1); disable_irq(wl->irq); @@ -305,6 +316,7 @@ static void __devexit wl1271_remove(struct sdio_func *func) pm_runtime_get_noresume(&func->dev); wl1271_unregister_hw(wl); + device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0); disable_irq_wake(wl->irq); free_irq(wl->irq, wl); wl1271_free_hw(wl); @@ -339,6 +351,9 @@ static int wl1271_suspend(struct device *dev) wl1271_error("error while trying to keep power"); goto out; } + + /* release host */ + sdio_release_host(func); } out: return ret; @@ -346,6 +361,15 @@ out: static int wl1271_resume(struct device *dev) { + struct sdio_func *func = dev_to_sdio_func(dev); + struct wl1271 *wl = sdio_get_drvdata(func); + + wl1271_debug(DEBUG_MAC80211, "wl1271 resume"); + if (wl->wow_enabled) { + /* claim back host */ + sdio_claim_host(func); + } + return 0; } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9629e90d9b5..2218b9c6384 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -357,6 +357,8 @@ enum wl12xx_flags { WL1271_FLAG_AP_STARTED, WL1271_FLAG_IF_INITIALIZED, WL1271_FLAG_DUMMY_PACKET_PENDING, + WL1271_FLAG_SUSPENDED, + WL1271_FLAG_PENDING_WORK, }; struct wl1271_link { -- cgit v1.2.3 From f795ea8b2f047409c59e891d6e5e86a925048bf4 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 13 May 2011 11:57:12 +0300 Subject: wl12xx_sdio: declare support for NL80211_WOW_TRIGGER_ANYTHING trigger Since wowlan requires the ability to stay awake while the host is suspended, declare support for NL80211_WOW_TRIGGER_ANYTHING if the MMC_PM_KEEP_POWER capability is being supported. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 41183db3483..92d29a860fc 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -231,6 +231,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, const struct wl12xx_platform_data *wlan_data; struct wl1271 *wl; unsigned long irqflags; + mmc_pm_flag_t mmcflags; int ret; /* We are only able to handle the wlan function */ @@ -282,6 +283,13 @@ static int __devinit wl1271_probe(struct sdio_func *func, disable_irq(wl->irq); + /* if sdio can keep power while host is suspended, enable wow */ + mmcflags = sdio_get_host_pm_caps(func); + wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags); + + if (mmcflags & MMC_PM_KEEP_POWER) + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; + ret = wl1271_init_ieee80211(wl); if (ret) goto out_irq; -- cgit v1.2.3 From 9439064cd9fce8a4db716a748dbf581eb234f9c7 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 13 May 2011 11:57:13 +0300 Subject: wl12xx: enter/exit psm on wowlan suspend/resume When operating as station, enter psm before suspending the device into wowlan state. Add a new completion event to signal when psm was entered successfully. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/event.c | 7 ++++ drivers/net/wireless/wl12xx/main.c | 81 ++++++++++++++++++++++++++++++++++++ drivers/net/wireless/wl12xx/ps.h | 2 + drivers/net/wireless/wl12xx/wl12xx.h | 1 + 4 files changed, 91 insertions(+) (limited to 'drivers/net/wireless/wl12xx') diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 1e4bd6a2c39..c3c554cd658 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -135,6 +135,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl, /* enable beacon early termination */ ret = wl1271_acx_bet_enable(wl, true); + if (ret < 0) + break; + + if (wl->ps_compl) { + complete(wl->ps_compl); + wl->ps_compl = NULL; + } break; default: break; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8f9e6152f3b..610be03a198 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1350,6 +1350,79 @@ static struct notifier_block wl1271_dev_notifier = { .notifier_call = wl1271_dev_notify, }; +static int wl1271_configure_suspend(struct wl1271 *wl) +{ + int ret; + + if (wl->bss_type != BSS_TYPE_STA_BSS) + return 0; + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + + /* enter psm if needed*/ + if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { + DECLARE_COMPLETION_ONSTACK(compl); + + wl->ps_compl = &compl; + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, + wl->basic_rate, true); + if (ret < 0) + goto out_sleep; + + /* we must unlock here so we will be able to get events */ + wl1271_ps_elp_sleep(wl); + mutex_unlock(&wl->mutex); + + ret = wait_for_completion_timeout( + &compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT)); + if (ret <= 0) { + wl1271_warning("couldn't enter ps mode!"); + ret = -EBUSY; + goto out; + } + + /* take mutex again, and wakeup */ + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + } +out_sleep: + wl1271_ps_elp_sleep(wl); +out_unlock: + mutex_unlock(&wl->mutex); +out: + return ret; + +} + +static void wl1271_configure_resume(struct wl1271 *wl) +{ + int ret; + + if (wl->bss_type != BSS_TYPE_STA_BSS) + return; + + mutex_lock(&wl->mutex); + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* exit psm if it wasn't configured */ + if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) + wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, + wl->basic_rate, true); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static int wl1271_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) { @@ -1357,6 +1430,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); wl->wow_enabled = !!wow; if (wl->wow_enabled) { + int ret; + ret = wl1271_configure_suspend(wl); + if (ret < 0) { + wl1271_warning("couldn't prepare device to suspend"); + return ret; + } /* flush any remaining work */ wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); flush_delayed_work(&wl->scan_complete_work); @@ -1408,6 +1487,8 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) wl1271_irq(0, wl); wl1271_enable_interrupts(wl); } + + wl1271_configure_resume(wl); } return 0; diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h index c41bd0a711b..25eb9bc9b62 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/ps.h @@ -35,4 +35,6 @@ void wl1271_elp_work(struct work_struct *work); void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues); void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid); +#define WL1271_PS_COMPLETE_TIMEOUT 500 + #endif /* __WL1271_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 2218b9c6384..fbe8f46d123 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -513,6 +513,7 @@ struct wl1271 { unsigned int rx_filter; struct completion *elp_compl; + struct completion *ps_compl; struct delayed_work elp_work; struct delayed_work pspoll_work; -- cgit v1.2.3