From 2cc78ff78c0af502b040d4527212e29e02d3231d Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 16 Sep 2010 01:22:04 +0200 Subject: wl1271: propagate set_power's return value Make it possible for the set power method to indicate a success/failure return value. This is needed to support more complex power on/off operations such as SDIO power manipulations. Signed-off-by: Ohad Ben-Cohen Acked-by: Luciano Coelho Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index dd3cee6ea5bb..faa5925efe30 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -313,7 +313,7 @@ struct wl1271_if_operations { bool fixed); void (*reset)(struct wl1271 *wl); void (*init)(struct wl1271 *wl); - void (*power)(struct wl1271 *wl, bool enable); + int (*power)(struct wl1271 *wl, bool enable); struct device* (*dev)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl); -- cgit v1.2.3-55-g7522 From 15cea99306ae14ce5f7c3d3989bcc17202e2b0be Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Thu, 16 Sep 2010 01:31:51 +0200 Subject: wl1271: make ref_clock configurable by board The wl1271 device is using a reference clock that may change between board to board. Make the ref_clock parameter configurable by board settings instead of having a hard coded value in the sources. Signed-off-by: Ohad Ben-Cohen Signed-off-by: John W. Linville --- drivers/net/wireless/wl12xx/wl1271.h | 1 + drivers/net/wireless/wl12xx/wl1271_boot.c | 11 +++++++---- drivers/net/wireless/wl12xx/wl1271_boot.h | 1 - drivers/net/wireless/wl12xx/wl1271_sdio.c | 1 + drivers/net/wireless/wl12xx/wl1271_spi.c | 2 ++ include/linux/wl12xx.h | 1 + 6 files changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index faa5925efe30..4134f4495b95 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -330,6 +330,7 @@ struct wl1271 { void (*set_power)(bool enable); int irq; + int ref_clock; spinlock_t wl_lock; diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index f36430b0336d..fc21db810812 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -457,17 +457,20 @@ int wl1271_boot(struct wl1271 *wl) { int ret = 0; u32 tmp, clk, pause; + int ref_clock = wl->ref_clock; wl1271_boot_hw_version(wl); - if (REF_CLOCK == 0 || REF_CLOCK == 2 || REF_CLOCK == 4) + if (ref_clock == 0 || ref_clock == 2 || ref_clock == 4) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; - else if (REF_CLOCK == 1 || REF_CLOCK == 3) + else if (ref_clock == 1 || ref_clock == 3) /* ref clk: 26/52 */ clk = 0x5; + else + return -EINVAL; - if (REF_CLOCK != 0) { + if (ref_clock != 0) { u16 val; /* Set clock type (open drain) */ val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); @@ -516,7 +519,7 @@ int wl1271_boot(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); /* 2 */ - clk |= (REF_CLOCK << 1) << 4; + clk |= (ref_clock << 1) << 4; wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h index f829699d597e..f73b0b15a280 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.h +++ b/drivers/net/wireless/wl12xx/wl1271_boot.h @@ -46,7 +46,6 @@ struct wl1271_static_data { /* delay between retries */ #define INIT_LOOP_DELAY 50 -#define REF_CLOCK 2 #define WU_COUNTER_PAUSE_VAL 0x3FF #define WELP_ARM_COMMAND_VAL 0x4 diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index 987ecdc9406d..f2f04663627c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -234,6 +234,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, } wl->irq = wlan_data->irq; + wl->ref_clock = wlan_data->board_ref_clock; ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index de56d8d9ee66..ced0a9e2c7e1 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -372,6 +372,8 @@ static int __devinit wl1271_probe(struct spi_device *spi) goto out_free; } + wl->ref_clock = pdata->board_ref_clock; + wl->irq = spi->irq; if (wl->irq < 0) { wl1271_error("irq missing in platform data"); diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index bd70563107fa..95deae3968f4 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -29,6 +29,7 @@ struct wl12xx_platform_data { /* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */ int irq; bool use_eeprom; + int board_ref_clock; }; int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); -- cgit v1.2.3-55-g7522 From c2c192ac6c16e2e8f5cc8cf54e02bb1d4e0e761d Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 27 Jul 2010 03:30:09 +0300 Subject: wl1271: Add trigger to net_device oper_state to change BT coex priority Add a trigger to net_device changes to monitor for oper_state changes in order to be able to inform the firmware when association is fully complete (including the EAP negotiation.) Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271.h | 1 + drivers/net/wireless/wl12xx/wl1271_main.c | 69 +++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 4134f4495b95..1098d1689b0e 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -350,6 +350,7 @@ struct wl1271 { #define WL1271_FLAG_IDLE (10) #define WL1271_FLAG_IDLE_REQUESTED (11) #define WL1271_FLAG_PSPOLL_FAILURE (12) +#define WL1271_FLAG_STA_STATE_SENT (13) unsigned long flags; struct wl1271_partition_set part; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 776cd7c41148..45d4ce36343c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -277,6 +277,67 @@ static struct platform_device wl1271_device = { static LIST_HEAD(wl_list); +static int wl1271_dev_notify(struct notifier_block *me, unsigned long what, + void *arg) +{ + struct net_device *dev = arg; + struct wireless_dev *wdev; + struct wiphy *wiphy; + struct ieee80211_hw *hw; + struct wl1271 *wl; + struct wl1271 *wl_temp; + int ret = 0; + + /* Check that this notification is for us. */ + if (what != NETDEV_CHANGE) + return NOTIFY_DONE; + + wdev = dev->ieee80211_ptr; + if (wdev == NULL) + return NOTIFY_DONE; + + wiphy = wdev->wiphy; + if (wiphy == NULL) + return NOTIFY_DONE; + + hw = wiphy_priv(wiphy); + if (hw == NULL) + return NOTIFY_DONE; + + wl_temp = hw->priv; + list_for_each_entry(wl, &wl_list, list) { + if (wl == wl_temp) + break; + } + if (wl != wl_temp) + return NOTIFY_DONE; + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + if ((dev->operstate == IF_OPER_UP) && + !test_and_set_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags)) { + wl1271_cmd_set_sta_state(wl); + wl1271_info("Association completed."); + } + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return NOTIFY_OK; +} + static void wl1271_conf_init(struct wl1271 *wl) { @@ -814,6 +875,10 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) return NETDEV_TX_OK; } +static struct notifier_block wl1271_dev_notifier = { + .notifier_call = wl1271_dev_notify, +}; + static int wl1271_op_start(struct ieee80211_hw *hw) { wl1271_debug(DEBUG_MAC80211, "mac80211 start"); @@ -1783,6 +1848,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, } } else { /* use defaults when not associated */ + clear_bit(WL1271_FLAG_STA_STATE_SENT, &wl->flags); clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); wl->aid = 0; @@ -2307,6 +2373,8 @@ int wl1271_register_hw(struct wl1271 *wl) wl->mac80211_registered = true; + register_netdevice_notifier(&wl1271_dev_notifier); + wl1271_notice("loaded"); return 0; @@ -2315,6 +2383,7 @@ EXPORT_SYMBOL_GPL(wl1271_register_hw); void wl1271_unregister_hw(struct wl1271 *wl) { + unregister_netdevice_notifier(&wl1271_dev_notifier); ieee80211_unregister_hw(wl->hw); wl->mac80211_registered = false; -- cgit v1.2.3-55-g7522 From 02fabb0eafde901ae51532ad15fdd4737b7d71e3 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 19 Aug 2010 04:41:15 +0200 Subject: wl1271: Enable/disable 11a support based on INI configuration Instead of hardcoding 11a support, enable/disable driver support based on the dual-mode-select parameter in the nvs-file general paramters. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271.h | 18 +++--------------- drivers/net/wireless/wl12xx/wl1271_boot.c | 22 ++++++++++++++++++++++ drivers/net/wireless/wl12xx/wl1271_init.c | 2 +- drivers/net/wireless/wl12xx/wl1271_main.c | 18 +++--------------- drivers/net/wireless/wl12xx/wl1271_scan.c | 2 +- drivers/net/wireless/wl12xx/wl1271_testmode.c | 14 +------------- 6 files changed, 31 insertions(+), 45 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 1098d1689b0e..763ece823c54 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -117,11 +117,6 @@ enum { #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) -/* - * Enable/disable 802.11a support for WL1273 - */ -#undef WL1271_80211A_ENABLED - #define WL1271_BUSY_WORD_CNT 1 #define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32)) @@ -363,6 +358,7 @@ struct wl1271 { u8 *fw; size_t fw_len; struct wl1271_nvs_file *nvs; + size_t nvs_len; s8 hw_pg_ver; @@ -476,6 +472,8 @@ struct wl1271 { bool sg_enabled; + bool enable_11a; + struct list_head list; /* Most recently reported noise in dBm */ @@ -499,14 +497,4 @@ int wl1271_plt_stop(struct wl1271 *wl); #define WL1271_PRE_POWER_ON_SLEEP 20 /* in miliseconds */ #define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */ -static inline bool wl1271_11a_enabled(void) -{ - /* FIXME: this could be determined based on the NVS-INI file */ -#ifdef WL1271_80211A_ENABLED - return true; -#else - return false; -#endif -} - #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index f750d5a79089..b91021242098 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -225,6 +225,28 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) if (wl->nvs == NULL) return -ENODEV; + /* + * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band + * configurations) can be removed when those NVS files stop floating + * around. + */ + if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || + wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { + if (wl->nvs->general_params.dual_mode_select) + wl->enable_11a = true; + } + + if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && + (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || + wl->enable_11a)) { + wl1271_error("nvs size is not as expected: %zu != %zu", + wl->nvs_len, sizeof(struct wl1271_nvs_file)); + kfree(wl->nvs); + wl->nvs = NULL; + wl->nvs_len = 0; + return -EILSEQ; + } + /* only the first part of the NVS needs to be uploaded */ nvs_len = sizeof(wl->nvs->nvs); nvs_ptr = (u8 *)wl->nvs->nvs; diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 4447af1557f5..879bae095a63 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -61,7 +61,7 @@ int wl1271_init_templates_config(struct wl1271 *wl) if (ret < 0) return ret; - if (wl1271_11a_enabled()) { + if (wl->enable_11a) { size_t size = sizeof(struct wl12xx_probe_req_template); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, NULL, size, 0, diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index bbd075a88f2d..11e112ff79d6 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -622,20 +622,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) return ret; } - /* - * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band - * configurations) can be removed when those NVS files stop floating - * around. - */ - if (fw->size != sizeof(struct wl1271_nvs_file) && - (fw->size != WL1271_INI_LEGACY_NVS_FILE_SIZE || - wl1271_11a_enabled())) { - wl1271_error("nvs size is not as expected: %zu != %zu", - fw->size, sizeof(struct wl1271_nvs_file)); - ret = -EILSEQ; - goto out; - } - wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL); if (!wl->nvs) { @@ -644,6 +630,8 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) goto out; } + wl->nvs_len = fw->size; + out: release_firmware(fw); @@ -2414,7 +2402,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_scan_ssids = 1; wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; - if (wl1271_11a_enabled()) + if (wl->enable_11a) wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; wl->hw->queues = 4; diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.c b/drivers/net/wireless/wl12xx/wl1271_scan.c index 7f42ca9abab8..8d30150f3f46 100644 --- a/drivers/net/wireless/wl12xx/wl1271_scan.c +++ b/drivers/net/wireless/wl12xx/wl1271_scan.c @@ -188,7 +188,7 @@ void wl1271_scan_stm(struct wl1271 *wl) ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, true, wl->conf.tx.basic_rate); if (ret == WL1271_NOTHING_TO_SCAN) { - if (wl1271_11a_enabled()) + if (wl->enable_11a) wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; else wl->scan.state = WL1271_SCAN_STATE_DONE; diff --git a/drivers/net/wireless/wl12xx/wl1271_testmode.c b/drivers/net/wireless/wl12xx/wl1271_testmode.c index 6e0952f79e9a..a3aa84386c88 100644 --- a/drivers/net/wireless/wl12xx/wl1271_testmode.c +++ b/drivers/net/wireless/wl12xx/wl1271_testmode.c @@ -199,19 +199,6 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) buf = nla_data(tb[WL1271_TM_ATTR_DATA]); len = nla_len(tb[WL1271_TM_ATTR_DATA]); - /* - * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band - * configurations) can be removed when those NVS files stop floating - * around. - */ - if (len != sizeof(struct wl1271_nvs_file) && - (len != WL1271_INI_LEGACY_NVS_FILE_SIZE || - wl1271_11a_enabled())) { - wl1271_error("nvs size is not as expected: %zu != %zu", - len, sizeof(struct wl1271_nvs_file)); - return -EMSGSIZE; - } - mutex_lock(&wl->mutex); kfree(wl->nvs); @@ -224,6 +211,7 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) } memcpy(wl->nvs, buf, len); + wl->nvs_len = len; wl1271_debug(DEBUG_TESTMODE, "testmode pushed nvs"); -- cgit v1.2.3-55-g7522 From c454f1d9a896d3519c756355b37bb39941093233 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 24 Aug 2010 06:28:03 +0300 Subject: wl1271: Move scan complete invocation into work function The current scan implementation can jam, if the scan request ends up containing no work. This can especially happen if there is a scan request with only 11a band channels for HW that does not support 11a. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271.h | 1 + drivers/net/wireless/wl12xx/wl1271_main.c | 4 ++++ drivers/net/wireless/wl12xx/wl1271_scan.c | 23 ++++++++++++++++++----- drivers/net/wireless/wl12xx/wl1271_scan.h | 1 + 4 files changed, 24 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 763ece823c54..272cff44ab53 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -416,6 +416,7 @@ struct wl1271 { /* Are we currently scanning */ struct wl1271_scan scan; + struct work_struct scan_complete_work; /* Our association ID */ u16 aid; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 4b8f3662101f..0026e775bb0d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -634,6 +634,8 @@ static int wl1271_setup(struct wl1271 *wl) INIT_WORK(&wl->irq_work, wl1271_irq_work); INIT_WORK(&wl->tx_work, wl1271_tx_work); + INIT_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); + return 0; } @@ -962,6 +964,8 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; int i; + cancel_work_sync(&wl->scan_complete_work); + mutex_lock(&wl->mutex); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.c b/drivers/net/wireless/wl12xx/wl1271_scan.c index 8d30150f3f46..9f1da82ed8d6 100644 --- a/drivers/net/wireless/wl12xx/wl1271_scan.c +++ b/drivers/net/wireless/wl12xx/wl1271_scan.c @@ -28,6 +28,23 @@ #include "wl1271_scan.h" #include "wl1271_acx.h" +void wl1271_scan_complete_work(struct work_struct *work) +{ + struct wl1271 *wl = + container_of(work, struct wl1271, scan_complete_work); + + wl1271_debug(DEBUG_SCAN, "Scanning complete"); + + mutex_lock(&wl->mutex); + wl->scan.state = WL1271_SCAN_STATE_IDLE; + kfree(wl->scan.scanned_ch); + wl->scan.scanned_ch = NULL; + mutex_unlock(&wl->mutex); + + ieee80211_scan_completed(wl->hw, false); +} + + static int wl1271_get_scan_channels(struct wl1271 *wl, struct cfg80211_scan_request *req, struct basic_scan_channel_params *channels, @@ -218,11 +235,7 @@ void wl1271_scan_stm(struct wl1271 *wl) break; case WL1271_SCAN_STATE_DONE: - kfree(wl->scan.scanned_ch); - wl->scan.scanned_ch = NULL; - - wl->scan.state = WL1271_SCAN_STATE_IDLE; - ieee80211_scan_completed(wl->hw, false); + ieee80211_queue_work(wl->hw, &wl->scan_complete_work); break; default: diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.h b/drivers/net/wireless/wl12xx/wl1271_scan.h index f1815700f5f9..1404e00dc963 100644 --- a/drivers/net/wireless/wl12xx/wl1271_scan.h +++ b/drivers/net/wireless/wl12xx/wl1271_scan.h @@ -32,6 +32,7 @@ int wl1271_scan_build_probe_req(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, 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); #define WL1271_SCAN_MAX_CHANNELS 24 #define WL1271_SCAN_DEFAULT_TAG 1 -- cgit v1.2.3-55-g7522 From 52b0e7a61fd4b67fe8efe295297d8549f052f786 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 21 Sep 2010 06:23:31 +0200 Subject: wl1271: Add hardware recovery mechanism There is some probability of hardware failures, which currently go largely undetected. Attempt to recover from these failures by shutting down the hardware, and requesting mac80211 to reconfigure it. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271.h | 3 +++ drivers/net/wireless/wl12xx/wl1271_cmd.c | 5 ++++- drivers/net/wireless/wl12xx/wl1271_main.c | 28 +++++++++++++++++++++++++++- drivers/net/wireless/wl12xx/wl1271_ps.c | 3 ++- 4 files changed, 36 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 272cff44ab53..3576c1cb067f 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -408,6 +408,9 @@ struct wl1271 { /* The target interrupt mask */ struct work_struct irq_work; + /* Hardware recovery work */ + struct work_struct recovery_work; + /* The mbox event mask */ u32 event_mask; diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index 06b14f2abf55..170b5a8bdabc 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -94,6 +94,7 @@ 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; } @@ -182,8 +183,10 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask) timeout = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT); do { - if (time_after(jiffies, timeout)) + if (time_after(jiffies, timeout)) { + ieee80211_queue_work(wl->hw, &wl->recovery_work); return -ETIMEDOUT; + } msleep(1); diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index e7f096fb6212..fecb0c313a1d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -235,6 +235,9 @@ static struct conf_drv_settings default_conf = { } }; +static void __wl1271_op_remove_interface(struct wl1271 *wl); + + static void wl1271_device_release(struct device *dev) { @@ -612,6 +615,26 @@ out: return ret; } +static void wl1271_recovery_work(struct work_struct *work) +{ + struct wl1271 *wl = + container_of(work, struct wl1271, recovery_work); + + mutex_lock(&wl->mutex); + + if (wl->state != WL1271_STATE_ON) + goto out; + + wl1271_info("Hardware recovery in progress."); + + /* reboot the chipset */ + __wl1271_op_remove_interface(wl); + ieee80211_restart_hw(wl->hw); + +out: + mutex_unlock(&wl->mutex); +} + static void wl1271_fw_wakeup(struct wl1271 *wl) { u32 elp_reg; @@ -635,6 +658,7 @@ static int wl1271_setup(struct wl1271 *wl) INIT_WORK(&wl->irq_work, wl1271_irq_work); INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); + INIT_WORK(&wl->recovery_work, wl1271_recovery_work); return 0; } @@ -793,11 +817,11 @@ out: mutex_unlock(&wl->mutex); cancel_work_sync(&wl->irq_work); + cancel_work_sync(&wl->recovery_work); return ret; } - static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; @@ -1046,6 +1070,8 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, WARN_ON(wl->vif != vif); __wl1271_op_remove_interface(wl); mutex_unlock(&wl->mutex); + + cancel_work_sync(&wl->recovery_work); } static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c index 150dc674d8b4..e3c332e2f97c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_ps.c +++ b/drivers/net/wireless/wl12xx/wl1271_ps.c @@ -64,7 +64,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) 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)); + msecs_to_jiffies(ELP_ENTRY_DELAY)); } } @@ -99,6 +99,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); if (ret == 0) { wl1271_error("ELP wakeup timeout!"); + ieee80211_queue_work(wl->hw, &wl->recovery_work); ret = -ETIMEDOUT; goto err; } else if (ret < 0) { -- cgit v1.2.3-55-g7522 From 78abd3207438b20e099b41cbed58d640cbd237a6 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 21 Sep 2010 06:23:32 +0200 Subject: wl1271: Add handling for failing hardware scan command Currently, the driver does not handle a failing hardware command to scan in any way - effectively, the scan machine will jam until the driver is shut down, and future scan requests will just return -EBUSY to user space, resulting in a type of busy-loop. The same problem occurs if the firmware fails to deliver the scan completion event - add timeout for this. Signed-off-by: Juuso Oikarinen Reviewed-by: Teemu Paasikivi Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271.h | 3 ++- drivers/net/wireless/wl12xx/wl1271_main.c | 4 ++-- drivers/net/wireless/wl12xx/wl1271_scan.c | 30 ++++++++++++++++++++++++++---- drivers/net/wireless/wl12xx/wl1271_scan.h | 2 ++ 4 files changed, 32 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 3576c1cb067f..cae489300e06 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -296,6 +296,7 @@ struct wl1271_rx_mem_pool_addr { struct wl1271_scan { struct cfg80211_scan_request *req; bool *scanned_ch; + bool failed; u8 state; u8 ssid[IW_ESSID_MAX_SIZE+1]; size_t ssid_len; @@ -419,7 +420,7 @@ struct wl1271 { /* Are we currently scanning */ struct wl1271_scan scan; - struct work_struct scan_complete_work; + struct delayed_work scan_complete_work; /* Our association ID */ u16 aid; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index fecb0c313a1d..c13175892960 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -657,8 +657,8 @@ static int wl1271_setup(struct wl1271 *wl) INIT_WORK(&wl->irq_work, wl1271_irq_work); INIT_WORK(&wl->tx_work, wl1271_tx_work); - INIT_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work); + INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); return 0; } @@ -1013,7 +1013,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) mutex_unlock(&wl->mutex); - cancel_work_sync(&wl->scan_complete_work); + cancel_delayed_work_sync(&wl->scan_complete_work); cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->pspoll_work); diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.c b/drivers/net/wireless/wl12xx/wl1271_scan.c index 20caceba435e..37f9ccbe738f 100644 --- a/drivers/net/wireless/wl12xx/wl1271_scan.c +++ b/drivers/net/wireless/wl12xx/wl1271_scan.c @@ -30,8 +30,11 @@ void wl1271_scan_complete_work(struct work_struct *work) { - struct wl1271 *wl = - container_of(work, struct wl1271, scan_complete_work); + struct delayed_work *dwork; + struct wl1271 *wl; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1271, scan_complete_work); wl1271_debug(DEBUG_SCAN, "Scanning complete"); @@ -48,6 +51,11 @@ void wl1271_scan_complete_work(struct work_struct *work) mutex_unlock(&wl->mutex); ieee80211_scan_completed(wl->hw, false); + + if (wl->scan.failed) { + wl1271_info("Scan completed due to error."); + ieee80211_queue_work(wl->hw, &wl->recovery_work); + } } @@ -191,7 +199,7 @@ out: void wl1271_scan_stm(struct wl1271 *wl) { - int ret; + int ret = 0; switch (wl->scan.state) { case WL1271_SCAN_STATE_IDLE: @@ -241,13 +249,22 @@ void wl1271_scan_stm(struct wl1271 *wl) break; case WL1271_SCAN_STATE_DONE: - ieee80211_queue_work(wl->hw, &wl->scan_complete_work); + wl->scan.failed = false; + cancel_delayed_work(&wl->scan_complete_work); + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(0)); break; default: wl1271_error("invalid scan state"); break; } + + if (ret < 0) { + cancel_delayed_work(&wl->scan_complete_work); + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(0)); + } } int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, @@ -270,6 +287,11 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, wl->scan.scanned_ch = kzalloc(req->n_channels * sizeof(*wl->scan.scanned_ch), GFP_KERNEL); + /* we assume failure so that timeout scenarios are handled correctly */ + wl->scan.failed = true; + ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, + msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); + wl1271_scan_stm(wl); return 0; diff --git a/drivers/net/wireless/wl12xx/wl1271_scan.h b/drivers/net/wireless/wl12xx/wl1271_scan.h index 1404e00dc963..bb7af2a102fa 100644 --- a/drivers/net/wireless/wl12xx/wl1271_scan.h +++ b/drivers/net/wireless/wl12xx/wl1271_scan.h @@ -46,6 +46,8 @@ void wl1271_scan_complete_work(struct work_struct *work); #define WL1271_SCAN_BAND_5_GHZ 1 #define WL1271_SCAN_PROBE_REQS 3 +#define WL1271_SCAN_TIMEOUT 10000 /* msec */ + enum { WL1271_SCAN_STATE_IDLE, WL1271_SCAN_STATE_2GHZ_ACTIVE, -- cgit v1.2.3-55-g7522 From 7a55724e0dc24cadaeb58f8306222a7802a24ff1 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Mon, 27 Sep 2010 12:42:07 +0200 Subject: wl1271: Add support for hardware GEM cipher This patch adds support for the hardware GEM cipher suite. Signed-off-by: Juuso Oikarinen Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271.h | 2 ++ drivers/net/wireless/wl12xx/wl1271_cmd.h | 2 +- drivers/net/wireless/wl12xx/wl1271_main.c | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index cae489300e06..779b130fdb3e 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -117,6 +117,8 @@ enum { #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) +#define WL1271_CIPHER_SUITE_GEM 0x00147201 + #define WL1271_BUSY_WORD_CNT 1 #define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32)) diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index ff8e35e87d98..33b946b4cb07 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -279,7 +279,7 @@ enum wl1271_cmd_key_type { KEY_WEP = 1, KEY_TKIP = 2, KEY_AES = 3, - KEY_GEM = 4 + KEY_GEM = 4, }; /* FIXME: Add description for key-types */ diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index b7e9c69f3772..cb18f22bbc5c 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -1549,6 +1549,11 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq); tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq); break; + case WL1271_CIPHER_SUITE_GEM: + key_type = KEY_GEM; + tx_seq_32 = WL1271_TX_SECURITY_HI32(wl->tx_security_seq); + tx_seq_16 = WL1271_TX_SECURITY_LO16(wl->tx_security_seq); + break; default: wl1271_error("Unknown key algo 0x%x", key_conf->cipher); @@ -2403,6 +2408,14 @@ EXPORT_SYMBOL_GPL(wl1271_unregister_hw); int wl1271_init_ieee80211(struct wl1271 *wl) { + static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WL1271_CIPHER_SUITE_GEM, + }; + /* The tx descriptor buffer and the TKIP space. */ wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE + sizeof(struct wl1271_tx_hw_descr); @@ -2420,6 +2433,9 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_CQM_RSSI; + wl->hw->wiphy->cipher_suites = cipher_suites; + wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); wl->hw->wiphy->max_scan_ssids = 1; -- cgit v1.2.3-55-g7522 From 1f37cbc9363462c99794699442da39f36be0aaf7 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 30 Sep 2010 13:28:27 +0200 Subject: wl1271: Support firmware RX packet aggregation Instead of retrieving one packet at a time from the firmware, try to retrieve all available packets at once. This optimization decreases the number of transactions, which saves CPU cycles and increases network throughput. Signed-off-by: Ido Yariv Tested-by: Juuso Oikarinen Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl1271.h | 5 +++ drivers/net/wireless/wl12xx/wl1271_main.c | 15 +++++++- drivers/net/wireless/wl12xx/wl1271_rx.c | 63 +++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl1271.h') diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 779b130fdb3e..8a4cd763e5a2 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -130,6 +130,8 @@ enum { #define ACX_TX_DESCRIPTORS 32 +#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE) + enum wl1271_state { WL1271_STATE_OFF, WL1271_STATE_ON, @@ -408,6 +410,9 @@ struct wl1271 { /* Rx memory pool address */ struct wl1271_rx_mem_pool_addr rx_mem_pool_addr; + /* Intermediate buffer, used for packet aggregation */ + u8 *aggr_buf; + /* The target interrupt mask */ struct work_struct irq_work; diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index cb18f22bbc5c..8071da10dbc9 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -2459,6 +2459,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) struct platform_device *plat_dev = NULL; struct wl1271 *wl; int i, ret; + unsigned int order; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { @@ -2517,11 +2518,18 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl1271_debugfs_init(wl); + order = get_order(WL1271_AGGR_BUFFER_SIZE); + wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); + if (!wl->aggr_buf) { + ret = -ENOMEM; + goto err_hw; + } + /* Register platform device */ ret = platform_device_register(wl->plat_dev); if (ret) { wl1271_error("couldn't register platform device"); - goto err_hw; + goto err_aggr; } dev_set_drvdata(&wl->plat_dev->dev, wl); @@ -2547,6 +2555,9 @@ err_bt_coex_state: err_platform: platform_device_unregister(wl->plat_dev); +err_aggr: + free_pages((unsigned long)wl->aggr_buf, order); + err_hw: wl1271_debugfs_exit(wl); kfree(plat_dev); @@ -2563,6 +2574,8 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); int wl1271_free_hw(struct wl1271 *wl) { platform_device_unregister(wl->plat_dev); + free_pages((unsigned long)wl->aggr_buf, + get_order(WL1271_AGGR_BUFFER_SIZE)); kfree(wl->plat_dev); wl1271_debugfs_exit(wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c index 94da5dd7723c..bea133b6e489 100644 --- a/drivers/net/wireless/wl12xx/wl1271_rx.c +++ b/drivers/net/wireless/wl12xx/wl1271_rx.c @@ -74,7 +74,7 @@ static void wl1271_rx_status(struct wl1271 *wl, } } -static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) +static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) { struct wl1271_rx_descriptor *desc; struct sk_buff *skb; @@ -87,16 +87,16 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) * workaround this by not retrieving them at all. */ if (unlikely(wl->state == WL1271_STATE_PLT)) - return; + return -EINVAL; skb = __dev_alloc_skb(length, GFP_KERNEL); if (!skb) { wl1271_error("Couldn't allocate RX frame"); - return; + return -ENOMEM; } buf = skb_put(skb, length); - wl1271_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); + memcpy(buf, data, length); /* the data read starts with the descriptor */ desc = (struct wl1271_rx_descriptor *) buf; @@ -116,6 +116,8 @@ static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) skb_trim(skb, skb->len - desc->pad_len); ieee80211_rx_ni(wl->hw, skb); + + return 0; } void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) @@ -124,31 +126,60 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) u32 buf_size; u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK; u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + u32 rx_counter; u32 mem_block; + u32 pkt_length; + u32 pkt_offset; while (drv_rx_counter != fw_rx_counter) { - mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); - buf_size = wl1271_rx_get_buf_size(status, drv_rx_counter); + buf_size = 0; + rx_counter = drv_rx_counter; + while (rx_counter != fw_rx_counter) { + pkt_length = wl1271_rx_get_buf_size(status, rx_counter); + if (buf_size + pkt_length > WL1271_AGGR_BUFFER_SIZE) + break; + buf_size += pkt_length; + rx_counter++; + rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; + } if (buf_size == 0) { wl1271_warning("received empty data"); break; } + /* + * Choose the block we want to read + * For aggregated packets, only the first memory block should + * be retrieved. The FW takes care of the rest. + */ + mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); wl->rx_mem_pool_addr.addr = (mem_block << 8) + le32_to_cpu(wl_mem_map->packet_memory_pool_start); wl->rx_mem_pool_addr.addr_extra = wl->rx_mem_pool_addr.addr + 4; - - /* Choose the block we want to read */ wl1271_write(wl, WL1271_SLV_REG_DATA, &wl->rx_mem_pool_addr, - sizeof(wl->rx_mem_pool_addr), false); - - wl1271_rx_handle_data(wl, buf_size); - - wl->rx_counter++; - drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + sizeof(wl->rx_mem_pool_addr), false); + + /* Read all available packets at once */ + wl1271_read(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, + buf_size, true); + + /* Split data into separate packets */ + pkt_offset = 0; + while (pkt_offset < buf_size) { + pkt_length = wl1271_rx_get_buf_size(status, + drv_rx_counter); + if (wl1271_rx_handle_data(wl, + wl->aggr_buf + pkt_offset, + pkt_length) < 0) + break; + wl->rx_counter++; + drv_rx_counter++; + drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; + pkt_offset += pkt_length; + } } - - wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); + wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, + cpu_to_le32(wl->rx_counter)); } -- cgit v1.2.3-55-g7522