From 5aa42346bba2e385674eb1dd4019dfce4c2ef771 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:07 +0200 Subject: wl12xx: 1281/1283 support - Add Definitions Definitions to support wl128x: - New FW file name - Chip ID - New PLL Configuration Algorithm macros that will be used at wl128x boot stage - Rename NVS macro name: wl127x and wl128x are using the same NVS file name. However, the ini parameters between them are different. The driver will validate the correct NVS size in wl1271_boot_upload_nvs(). [Cleaned up some of the definitions. -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/wl12xx.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 86be83e25ec5..a2e899d4e1aa 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -131,9 +131,15 @@ extern u32 wl12xx_debug_level; #define WL1271_FW_NAME "ti-connectivity/wl1271-fw-2.bin" +#define WL128X_FW_NAME "ti-connectivity/wl128x-fw.bin" #define WL1271_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin" -#define WL1271_NVS_NAME "ti-connectivity/wl1271-nvs.bin" +/* + * wl127x and wl128x are using the same NVS file name. However, the + * ini parameters between them are different. The driver validates + * the correct NVS size in wl1271_boot_upload_nvs(). + */ +#define WL12XX_NVS_NAME "ti-connectivity/wl1271-nvs.bin" #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) -- cgit v1.2.3 From 48a61477bdc04896bd96d259388a0c42a7019943 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:08 +0200 Subject: wl12xx: 1281/1283 support - Add acx commands New acx command that sets: Rx fifo enable reduced bus transactions in RX path. Tx bus transactions padding to SDIO block size that improve preference in Tx and essential for working with SDIO HS (48Mhz). The max SDIO block size is 256 when working with Tx bus transactions padding to SDIO block. Add new ops to SDIO & SPI that handles the win size change in case of transactions padding (relevant only for SDIO). [Fix endianess issues; simplify sdio-specific block_size handling; minor changes in comments; use "aligned_len" in one calculation instead of "pad" to avoid confusion -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 26 +++++++++++++++ drivers/net/wireless/wl12xx/acx.h | 11 +++++++ drivers/net/wireless/wl12xx/init.c | 27 +++++++++++++++ drivers/net/wireless/wl12xx/init.h | 1 + drivers/net/wireless/wl12xx/io.c | 10 ++++++ drivers/net/wireless/wl12xx/io.h | 1 + drivers/net/wireless/wl12xx/main.c | 7 ++++ drivers/net/wireless/wl12xx/tx.c | 64 +++++++++++++++++++++++++----------- drivers/net/wireless/wl12xx/tx.h | 46 +++++++++++++++++++++----- drivers/net/wireless/wl12xx/wl12xx.h | 3 ++ 10 files changed, 168 insertions(+), 28 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index a3db755ceeda..50676b36ad26 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1019,6 +1019,32 @@ out: return ret; } +int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap) +{ + struct wl1271_acx_host_config_bitmap *bitmap_conf; + int ret; + + bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL); + if (!bitmap_conf) { + ret = -ENOMEM; + goto out; + } + + bitmap_conf->host_cfg_bitmap = cpu_to_le32(host_cfg_bitmap); + + ret = wl1271_cmd_configure(wl, ACX_HOST_IF_CFG_BITMAP, + bitmap_conf, sizeof(*bitmap_conf)); + if (ret < 0) { + wl1271_warning("wl1271 bitmap config opt failed: %d", ret); + goto out; + } + +out: + kfree(bitmap_conf); + + return ret; +} + int wl1271_acx_init_mem_config(struct wl1271 *wl) { int ret; diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index dd19b01d807b..0a40caeab2a2 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -939,6 +939,16 @@ struct wl1271_acx_keep_alive_config { u8 padding; } __packed; +#define HOST_IF_CFG_RX_FIFO_ENABLE BIT(0) +#define HOST_IF_CFG_TX_EXTRA_BLKS_SWAP BIT(1) +#define HOST_IF_CFG_TX_PAD_TO_SDIO_BLK BIT(3) + +struct wl1271_acx_host_config_bitmap { + struct acx_header header; + + __le32 host_cfg_bitmap; +} __packed; + enum { WL1271_ACX_TRIG_TYPE_LEVEL = 0, WL1271_ACX_TRIG_TYPE_EDGE, @@ -1275,6 +1285,7 @@ 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); int wl1271_acx_init_mem_config(struct wl1271 *wl); +int wl1271_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 6072fe457135..34c41084bcf1 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -31,6 +31,7 @@ #include "cmd.h" #include "reg.h" #include "tx.h" +#include "io.h" int wl1271_sta_init_templates_config(struct wl1271 *wl) { @@ -504,6 +505,27 @@ static int wl1271_set_ba_policies(struct wl1271 *wl) return ret; } +int wl1271_chip_specific_init(struct wl1271 *wl) +{ + int ret = 0; + + if (wl->chip.id == CHIP_ID_1283_PG20) { + u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; + + if (wl1271_set_block_size(wl)) + /* Enable SDIO padding */ + host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; + + /* Must be before wl1271_acx_init_mem_config() */ + ret = wl1271_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap); + if (ret < 0) + goto out; + } +out: + return ret; +} + + int wl1271_hw_init(struct wl1271 *wl) { struct conf_tx_ac_category *conf_ac; @@ -519,6 +541,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) return ret; + /* Chip-specific init */ + ret = wl1271_chip_specific_init(wl); + if (ret < 0) + return ret; + /* Mode specific init */ if (is_ap) ret = wl1271_ap_hw_init(wl); diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h index 3a8bd3f426d2..4975270a91ab 100644 --- a/drivers/net/wireless/wl12xx/init.h +++ b/drivers/net/wireless/wl12xx/init.h @@ -31,6 +31,7 @@ int wl1271_sta_init_templates_config(struct wl1271 *wl); int wl1271_init_phy_config(struct wl1271 *wl); 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); #endif diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index d557f73e7c19..aa40c98e8fd3 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c @@ -43,6 +43,16 @@ #define OCP_STATUS_REQ_FAILED 0x20000 #define OCP_STATUS_RESP_ERROR 0x30000 +bool wl1271_set_block_size(struct wl1271 *wl) +{ + if (wl->if_ops->set_block_size) { + wl->if_ops->set_block_size(wl); + return true; + } + + return false; +} + void wl1271_disable_interrupts(struct wl1271 *wl) { wl->if_ops->disable_irq(wl); diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index c1aac8292089..84454f6d8169 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h @@ -169,5 +169,6 @@ int wl1271_init_ieee80211(struct wl1271 *wl); struct ieee80211_hw *wl1271_alloc_hw(void); int wl1271_free_hw(struct wl1271 *wl); irqreturn_t wl1271_irq(int irq, void *data); +bool wl1271_set_block_size(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b0c49352b56f..f24906f54a71 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -450,6 +450,11 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) return ret; + /* Chip-specific initializations */ + ret = wl1271_chip_specific_init(wl); + if (ret < 0) + return ret; + ret = wl1271_sta_init_templates_config(wl); if (ret < 0) return ret; @@ -1335,6 +1340,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->block_size = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -3458,6 +3464,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; + wl->block_size = 0; 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/tx.c b/drivers/net/wireless/wl12xx/tx.c index 5e9ef7d53e7e..e296f0a5fe27 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -132,6 +132,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, { struct wl1271_tx_hw_descr *desc; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; + u32 len; u32 total_blocks; int id, ret = -EBUSY; @@ -145,14 +146,20 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, /* approximate the number of blocks required for this packet in the firmware */ - total_blocks = total_len + TX_HW_BLOCK_SIZE - 1; - total_blocks = total_blocks / TX_HW_BLOCK_SIZE + TX_HW_BLOCK_SPARE; + if (wl->block_size) + len = ALIGN(total_len, wl->block_size); + else + len = total_len; + + total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE + + TX_HW_BLOCK_SPARE; + if (total_blocks <= wl->tx_blocks_available) { desc = (struct wl1271_tx_hw_descr *)skb_push( skb, total_len - skb->len); - desc->extra_mem_blocks = TX_HW_BLOCK_SPARE; - desc->total_mem_blocks = total_blocks; + desc->wl127x_mem.extra_blocks = TX_HW_BLOCK_SPARE; + desc->wl127x_mem.total_mem_blocks = total_blocks; desc->id = id; wl->tx_blocks_available -= total_blocks; @@ -178,7 +185,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, { struct timespec ts; struct wl1271_tx_hw_descr *desc; - int pad, ac, rate_idx; + int aligned_len, ac, rate_idx; s64 hosttime; u16 tx_attr; @@ -237,20 +244,32 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; desc->reserved = 0; - /* align the length (and store in terms of words) */ - pad = ALIGN(skb->len, WL1271_TX_ALIGN_TO); - desc->length = cpu_to_le16(pad >> 2); + if (wl->block_size) { + aligned_len = ALIGN(skb->len, wl->block_size); + + desc->wl128x_mem.extra_bytes = aligned_len - skb->len; + desc->length = cpu_to_le16(aligned_len >> 2); + } else { + int pad; + + /* align the length (and store in terms of words) */ + aligned_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + desc->length = cpu_to_le16(aligned_len >> 2); + + /* calculate number of padding bytes */ + pad = aligned_len - skb->len; + tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; - /* calculate number of padding bytes */ - pad = pad - skb->len; - tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + wl1271_debug(DEBUG_TX, "tx_fill_hdr: padding: %d", pad); + } desc->tx_attr = cpu_to_le16(tx_attr); - wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d " - "tx_attr: 0x%x len: %d life: %d mem: %d", pad, desc->hlid, - le16_to_cpu(desc->tx_attr), le16_to_cpu(desc->length), - le16_to_cpu(desc->life_time), desc->total_mem_blocks); + wl1271_debug(DEBUG_TX, "tx_fill_hdr: hlid: %d tx_attr: 0x%x " + "len: %d life: %d mem: %d", + desc->hlid, le16_to_cpu(desc->tx_attr), + le16_to_cpu(desc->length), le16_to_cpu(desc->life_time), + desc->wl127x_mem.total_mem_blocks); } /* caller must hold wl->mutex */ @@ -305,11 +324,18 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); /* - * The length of each packet is stored in terms of words. Thus, we must - * pad the skb data to make sure its length is aligned. - * The number of padding bytes is computed and set in wl1271_tx_fill_hdr + * The length of each packet is stored in terms of + * words. Thus, we must pad the skb data to make sure its + * length is aligned. The number of padding bytes is computed + * and set in wl1271_tx_fill_hdr. + * In special cases, we want to align to a specific block size + * (eg. for wl128x with SDIO we align to 256). */ - total_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + if (wl->block_size) + total_len = ALIGN(skb->len, wl->block_size); + else + total_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 02f07fa66e82..e31317717ab2 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -55,20 +55,48 @@ #define WL1271_TX_ALIGN_TO 4 #define WL1271_TKIP_IV_SPACE 4 +struct wl127x_tx_mem { + /* + * Number of extra memory blocks to allocate for this packet + * in addition to the number of blocks derived from the packet + * length. + */ + u8 extra_blocks; + /* + * Total number of memory blocks allocated by the host for + * this packet. Must be equal or greater than the actual + * blocks number allocated by HW. + */ + u8 total_mem_blocks; +} __packed; + +struct wl128x_tx_mem { + /* + * Total number of memory blocks allocated by the host for + * this packet. + */ + u8 total_mem_blocks; + /* + * Number of extra bytes, at the end of the frame. the host + * uses this padding to complete each frame to integer number + * of SDIO blocks. + */ + u8 extra_bytes; +} __packed; + struct wl1271_tx_hw_descr { /* Length of packet in words, including descriptor+header+data */ __le16 length; - /* Number of extra memory blocks to allocate for this packet in - addition to the number of blocks derived from the packet length */ - u8 extra_mem_blocks; - /* Total number of memory blocks allocated by the host for this packet. - Must be equal or greater than the actual blocks number allocated by - HW!! */ - u8 total_mem_blocks; + union { + struct wl127x_tx_mem wl127x_mem; + struct wl128x_tx_mem wl128x_mem; + } __packed; /* Device time (in us) when the packet arrived to the driver */ __le32 start_time; - /* Max delay in TUs until transmission. The last device time the - packet can be transmitted is: startTime+(1024*LifeTime) */ + /* + * Max delay in TUs until transmission. The last device time the + * packet can be transmitted is: start_time + (1024 * life_time) + */ __le16 life_time; /* Bitwise fields - see TX_ATTR... definitions above. */ __le16 tx_attr; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index a2e899d4e1aa..959b338d0af4 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -303,6 +303,7 @@ struct wl1271_if_operations { struct device* (*dev)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl); + void (*set_block_size) (struct wl1271 *wl); }; #define MAX_NUM_KEYS 14 @@ -533,6 +534,8 @@ struct wl1271 { bool ba_support; u8 ba_rx_bitmap; + u32 block_size; + /* * AP-mode - links indexed by HLID. The global and broadcast links * are always active. -- cgit v1.2.3 From bc765bf3b9a095b3e41c8cda80643901884c3dd4 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:10 +0200 Subject: wl12xx: 1281/1283 support - Loading FW & NVS Take care of FW & NVS with the auto-detection between wl127x and wl128x. [Moved some common code outside if statements and added notes about NVS structure assumptions; Fixed a bug when checking the nvs size: if the size was incorrect, the local nvs variable was set to NULL, it should be wl->nvs instead. -- Luca] [Merged with potential buffer overflow fix -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 83 ++++++++++++++++++++++----------- drivers/net/wireless/wl12xx/cmd.c | 11 +++-- drivers/net/wireless/wl12xx/ini.h | 4 +- drivers/net/wireless/wl12xx/main.c | 13 ++++-- drivers/net/wireless/wl12xx/sdio_test.c | 9 +++- drivers/net/wireless/wl12xx/testmode.c | 6 ++- drivers/net/wireless/wl12xx/wl12xx.h | 2 +- 7 files changed, 86 insertions(+), 42 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 69fe8703be42..38f3e8ba2628 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -243,33 +243,57 @@ 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) { - /* for now 11a is unsupported in AP mode */ - if (wl->bss_type != BSS_TYPE_AP_BSS && - wl->nvs->general_params.dual_mode_select) - wl->enable_11a = true; - } + if (wl->chip.id == CHIP_ID_1283_PG20) { + struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; + + if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { + if (nvs->general_params.dual_mode_select) + wl->enable_11a = true; + } else { + wl1271_error("nvs size is not as expected: %zu != %zu", + wl->nvs_len, + sizeof(struct wl128x_nvs_file)); + kfree(wl->nvs); + wl->nvs = NULL; + wl->nvs_len = 0; + return -EILSEQ; + } - 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(nvs->nvs); + nvs_ptr = (u8 *)nvs->nvs; - /* only the first part of the NVS needs to be uploaded */ - nvs_len = sizeof(wl->nvs->nvs); - nvs_ptr = (u8 *)wl->nvs->nvs; + } else { + struct wl1271_nvs_file *nvs = + (struct wl1271_nvs_file *)wl->nvs; + /* + * 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) { + /* for now 11a is unsupported in AP mode */ + if (wl->bss_type != BSS_TYPE_AP_BSS && + 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(nvs->nvs); + nvs_ptr = (u8 *) nvs->nvs; + } /* update current MAC address to NVS */ nvs_ptr[11] = wl->mac_addr[0]; @@ -319,10 +343,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) /* * We've reached the first zero length, the first NVS table * is located at an aligned offset which is at least 7 bytes further. + * NOTE: The wl->nvs->nvs element must be first, in order to + * simplify the casting, we assume it is at the beginning of + * the wl->nvs structure. */ - nvs_ptr = (u8 *)wl->nvs->nvs + - ALIGN(nvs_ptr - (u8 *)wl->nvs->nvs + 7, 4); - nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; + nvs_ptr = (u8 *)wl->nvs + + ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); + nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ wl1271_set_partition(wl, &part_table[PART_WORK]); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 37eb9f366942..246804428517 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -187,8 +187,9 @@ out: int wl1271_cmd_radio_parms(struct wl1271 *wl) { + struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *)wl->nvs; struct wl1271_radio_parms_cmd *radio_parms; - struct wl1271_ini_general_params *gp = &wl->nvs->general_params; + struct wl1271_ini_general_params *gp = &nvs->general_params; int ret; if (!wl->nvs) @@ -201,18 +202,18 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl) radio_parms->test.id = TEST_CMD_INI_FILE_RADIO_PARAM; /* 2.4GHz parameters */ - memcpy(&radio_parms->static_params_2, &wl->nvs->stat_radio_params_2, + memcpy(&radio_parms->static_params_2, &nvs->stat_radio_params_2, sizeof(struct wl1271_ini_band_params_2)); memcpy(&radio_parms->dyn_params_2, - &wl->nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params, + &nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params, sizeof(struct wl1271_ini_fem_params_2)); /* 5GHz parameters */ memcpy(&radio_parms->static_params_5, - &wl->nvs->stat_radio_params_5, + &nvs->stat_radio_params_5, sizeof(struct wl1271_ini_band_params_5)); memcpy(&radio_parms->dyn_params_5, - &wl->nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params, + &nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params, sizeof(struct wl1271_ini_fem_params_5)); wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", diff --git a/drivers/net/wireless/wl12xx/ini.h b/drivers/net/wireless/wl12xx/ini.h index 30efcd6643b1..1420c842b8f1 100644 --- a/drivers/net/wireless/wl12xx/ini.h +++ b/drivers/net/wireless/wl12xx/ini.h @@ -174,7 +174,7 @@ struct wl128x_ini_fem_params_5 { #define WL1271_INI_LEGACY_NVS_FILE_SIZE 800 struct wl1271_nvs_file { - /* NVS section */ + /* NVS section - must be first! */ u8 nvs[WL1271_INI_NVS_SECTION_SIZE]; /* INI section */ @@ -195,7 +195,7 @@ struct wl1271_nvs_file { } __packed; struct wl128x_nvs_file { - /* NVS section */ + /* NVS section - must be first! */ u8 nvs[WL1271_INI_NVS_SECTION_SIZE]; /* INI section */ diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e1fd005dd048..fe0cf47a656c 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -804,7 +804,10 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) break; case BSS_TYPE_IBSS: case BSS_TYPE_STA_BSS: - fw_name = WL1271_FW_NAME; + if (wl->chip.id == CHIP_ID_1283_PG20) + fw_name = WL128X_FW_NAME; + else + fw_name = WL1271_FW_NAME; break; default: wl1271_error("no compatible firmware for bss_type %d", @@ -860,7 +863,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) return ret; } - wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL); + wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); @@ -3289,7 +3292,11 @@ int wl1271_register_hw(struct wl1271 *wl) ret = wl1271_fetch_nvs(wl); if (ret == 0) { - u8 *nvs_ptr = (u8 *)wl->nvs->nvs; + /* NOTE: The wl->nvs->nvs element must be first, in + * order to simplify the casting, we assume it is at + * the beginning of the wl->nvs structure. + */ + u8 *nvs_ptr = (u8 *)wl->nvs; wl->mac_addr[0] = nvs_ptr[11]; wl->mac_addr[1] = nvs_ptr[10]; diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index 01adf1b1003b..e6e2ad63a1c1 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -189,7 +189,12 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) const struct firmware *fw; int ret; - ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl)); + if (wl->chip.id == CHIP_ID_1283_PG20) + ret = request_firmware(&fw, WL128X_FW_NAME, + wl1271_wl_to_dev(wl)); + else + ret = request_firmware(&fw, WL1271_FW_NAME, + wl1271_wl_to_dev(wl)); if (ret < 0) { wl1271_error("could not get firmware: %d", ret); @@ -234,7 +239,7 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) return ret; } - wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL); + wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL); if (!wl->nvs) { wl1271_error("could not allocate memory for the nvs file"); diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index 6ec06a4a4c6d..da351d7cd1f2 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -27,6 +27,7 @@ #include "wl12xx.h" #include "acx.h" +#include "reg.h" #define WL1271_TM_MAX_DATA_LENGTH 1024 @@ -204,7 +205,10 @@ static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) kfree(wl->nvs); - if (len != sizeof(struct wl1271_nvs_file)) + if ((wl->chip.id == CHIP_ID_1283_PG20) && + (len != sizeof(struct wl128x_nvs_file))) + return -EINVAL; + else if (len != sizeof(struct wl1271_nvs_file)) return -EINVAL; wl->nvs = kzalloc(len, GFP_KERNEL); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 959b338d0af4..e59f5392e909 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -378,7 +378,7 @@ struct wl1271 { u8 *fw; size_t fw_len; u8 fw_bss_type; - struct wl1271_nvs_file *nvs; + void *nvs; size_t nvs_len; s8 hw_pg_ver; -- cgit v1.2.3 From 5ea417ae7749076ddaacb5b36487cae6ac920413 Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:11 +0200 Subject: wl12xx: 1281/1283 support - New boot sequence Boot sequence support FREF clock and TCXO clock. WL128x has two clocks input - TCXO and FREF. TCXO is the main clock of the device, while FREF is used to sync between the GPS and the cellular modem. Auto-detection checks where TCXO is 32.736MHz or 16.368MHz, in that case the FREF will be used as the WLAN/BT main clock. [Use clock enumeration as defined in linux/wl12xx.h; remove unnecessary else block in wl128x_switch_fref; remove unnecessary change in main.c; remove some unnecessary debug prints and comments; fix potential use of uninitialized value (pll_config) -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 183 ++++++++++++++++++++++++++++++-- drivers/net/wireless/wl12xx/sdio.c | 1 + drivers/net/wireless/wl12xx/sdio_test.c | 1 + drivers/net/wireless/wl12xx/spi.c | 1 + drivers/net/wireless/wl12xx/wl12xx.h | 1 + 5 files changed, 179 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 38f3e8ba2628..9d742c1e75a9 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -22,6 +22,7 @@ */ #include +#include #include "acx.h" #include "reg.h" @@ -520,24 +521,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; } -/* uploads NVS and firmware */ -int wl1271_load_firmware(struct wl1271 *wl) +/* + * WL128x has two clocks input - TCXO and FREF. + * TCXO is the main clock of the device, while FREF is used to sync + * between the GPS and the cellular modem. + * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used + * as the WLAN/BT main clock. + */ +static int wl128x_switch_fref(struct wl1271 *wl, bool *is_ref_clk) { - int ret = 0; - u32 tmp, clk, pause; + u16 sys_clk_cfg_val; + + /* if working on XTAL-only mode go directly to TCXO TO FREF SWITCH */ + if ((wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) || + (wl->ref_clock == CONF_REF_CLK_26_M_XTAL)) + return true; + + /* Read clock source FREF or TCXO */ + sys_clk_cfg_val = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG); + + if (sys_clk_cfg_val & PRCM_CM_EN_MUX_WLAN_FREF) { + /* if bit 3 is set - working with FREF clock */ + wl1271_debug(DEBUG_BOOT, "working with FREF clock, skip" + " to FREF"); + + *is_ref_clk = true; + } else { + /* if bit 3 is clear - working with TCXO clock */ + wl1271_debug(DEBUG_BOOT, "working with TCXO clock"); + + /* TCXO to FREF switch, check TXCO clock config */ + if ((wl->tcxo_clock != WL12XX_TCXOCLOCK_16_368) && + (wl->tcxo_clock != WL12XX_TCXOCLOCK_32_736)) { + /* + * not 16.368Mhz and not 32.736Mhz - skip to + * configure ELP stage + */ + wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:" + " TcxoRefClk=%d - not 16.368Mhz and not" + " 32.736Mhz - skip to configure ELP" + " stage", wl->tcxo_clock); + + *is_ref_clk = false; + } else { + wl1271_debug(DEBUG_BOOT, "NEW PLL ALGO:" + "TcxoRefClk=%d - 16.368Mhz or 32.736Mhz" + " - TCXO to FREF switch", + wl->tcxo_clock); + + return true; + } + } + + return false; +} + +static int wl128x_boot_clk(struct wl1271 *wl, bool *is_ref_clk) +{ + if (wl128x_switch_fref(wl, is_ref_clk)) { + wl1271_debug(DEBUG_BOOT, "XTAL-only mode go directly to" + " TCXO TO FREF SWITCH"); + /* TCXO to FREF switch - for PG2.0 */ + wl1271_top_reg_write(wl, WL_SPARE_REG, + WL_SPARE_MASK_8526); + + wl1271_top_reg_write(wl, SYS_CLK_CFG_REG, + WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); + + *is_ref_clk = true; + mdelay(15); + } + + /* Set bit 2 in spare register to avoid illegal access */ + wl1271_top_reg_write(wl, WL_SPARE_REG, WL_SPARE_VAL); + + /* working with TCXO clock */ + if ((*is_ref_clk == false) && + ((wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8) || + (wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6))) { + wl1271_debug(DEBUG_BOOT, "16_8_M or 33_6_M TCXO detected"); + + /* Manually Configure MCS PLL settings PG2.0 Only */ + wl1271_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); + wl1271_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); + wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, + MCS_PLL_CONFIG_REG_VAL); + } else { + int pll_config; + u16 mcs_pll_config_val; + + /* + * Configure MCS PLL settings to FREF Freq + * Set the values that determine the time elapse since the PLL's + * get their enable signal until the lock indication is set + */ + wl1271_top_reg_write(wl, PLL_LOCK_COUNTERS_REG, + PLL_LOCK_COUNTERS_COEX | PLL_LOCK_COUNTERS_MCS); + + mcs_pll_config_val = wl1271_top_reg_read(wl, + MCS_PLL_CONFIG_REG); + /* + * Set the MCS PLL input frequency value according to the + * reference clock value detected/read + */ + if (*is_ref_clk == false) { + if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_19_2) || + (wl->tcxo_clock == WL12XX_TCXOCLOCK_38_4)) + pll_config = 1; + else if ((wl->tcxo_clock == WL12XX_TCXOCLOCK_26) + || + (wl->tcxo_clock == WL12XX_TCXOCLOCK_52)) + pll_config = 2; + else + return -EINVAL; + } else { + if ((wl->ref_clock == CONF_REF_CLK_19_2_E) || + (wl->ref_clock == CONF_REF_CLK_38_4_E)) + pll_config = 1; + else if ((wl->ref_clock == CONF_REF_CLK_26_E) || + (wl->ref_clock == CONF_REF_CLK_52_E)) + pll_config = 2; + else + return -EINVAL; + } + + mcs_pll_config_val |= (pll_config << (MCS_SEL_IN_FREQ_SHIFT)) & + (MCS_SEL_IN_FREQ_MASK); + wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, + mcs_pll_config_val); + } + + return 0; +} + +static int wl127x_boot_clk(struct wl1271 *wl) +{ + u32 pause; + u32 clk; wl1271_boot_hw_version(wl); - if (wl->ref_clock == 0 || wl->ref_clock == 2 || wl->ref_clock == 4) + if (wl->ref_clock == CONF_REF_CLK_19_2_E || + wl->ref_clock == CONF_REF_CLK_38_4_E || + wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) /* ref clk: 19.2/38.4/38.4-XTAL */ clk = 0x3; - else if (wl->ref_clock == 1 || wl->ref_clock == 3) + else if (wl->ref_clock == CONF_REF_CLK_26_E || + wl->ref_clock == CONF_REF_CLK_52_E) /* ref clk: 26/52 */ clk = 0x5; else return -EINVAL; - if (wl->ref_clock != 0) { + if (wl->ref_clock != CONF_REF_CLK_19_2_E) { u16 val; /* Set clock type (open drain) */ val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); @@ -567,6 +703,26 @@ int wl1271_load_firmware(struct wl1271 *wl) pause |= WU_COUNTER_PAUSE_VAL; wl1271_write32(wl, WU_COUNTER_PAUSE, pause); + return 0; +} + +/* uploads NVS and firmware */ +int wl1271_load_firmware(struct wl1271 *wl) +{ + int ret = 0; + u32 tmp, clk; + bool is_ref_clk = false; + + if (wl->chip.id == CHIP_ID_1283_PG20) { + ret = wl128x_boot_clk(wl, &is_ref_clk); + if (ret < 0) + goto out; + } else { + ret = wl127x_boot_clk(wl); + if (ret < 0) + goto out; + } + /* Continue the ELP wake up sequence */ wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); @@ -582,7 +738,15 @@ int wl1271_load_firmware(struct wl1271 *wl) wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); - clk |= (wl->ref_clock << 1) << 4; + if (wl->chip.id == CHIP_ID_1283_PG20) { + if (is_ref_clk == false) + clk |= ((wl->tcxo_clock & 0x3) << 1) << 4; + else + clk |= ((wl->ref_clock & 0x3) << 1) << 4; + } else { + clk |= (wl->ref_clock << 1) << 4; + } + wl1271_write32(wl, DRPW_SCRATCH_START, clk); wl1271_set_partition(wl, &part_table[PART_WORK]); @@ -615,6 +779,9 @@ int wl1271_load_firmware(struct wl1271 *wl) /* WL1271: The reference driver skips steps 7 to 10 (jumps directly * to upload_fw) */ + if (wl->chip.id == CHIP_ID_1283_PG20) + wl1271_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA); + ret = wl1271_boot_upload_firmware(wl); if (ret < 0) goto out; diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 7491b3d8487a..f6dd3dea4f30 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -255,6 +255,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->irq = wlan_data->irq; wl->ref_clock = wlan_data->board_ref_clock; + wl->tcxo_clock = wlan_data->board_tcxo_clock; ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index e6e2ad63a1c1..c02c87046615 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -421,6 +421,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->irq = wlan_data->irq; wl->ref_clock = wlan_data->board_ref_clock; + wl->tcxo_clock = wlan_data->board_tcxo_clock; sdio_set_drvdata(func, wl_test); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index bfb1171176ca..f5525361f2f5 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -401,6 +401,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) } wl->ref_clock = pdata->board_ref_clock; + wl->tcxo_clock = pdata->board_tcxo_clock; wl->irq = spi->irq; if (wl->irq < 0) { diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index e59f5392e909..4b556776fd57 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -535,6 +535,7 @@ struct wl1271 { u8 ba_rx_bitmap; u32 block_size; + int tcxo_clock; /* * AP-mode - links indexed by HLID. The global and broadcast links -- cgit v1.2.3 From 13b107dd9808343d05627f0fba7fbc764c86738e Mon Sep 17 00:00:00 2001 From: Shahar Levi Date: Sun, 6 Mar 2011 16:32:12 +0200 Subject: wl12xx: 1281/1283 support - use dynamic memory for the RX/TX pools Separate the memory configuration to chip-specific structures and implement dynamic memory for wl128x. This feature allows us to move TX memory blocks to the RX pool when the RX path is overloaded. Thanks for Arik Nemtsov for helping simplify the wl1271_fw_status() code. [Rewrote the commit subject and message for clarity; improved some comments and changed "spare" to "padding" for consistency; added a FIXME for the AP memory configuration -- Luca] Signed-off-by: Shahar Levi Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 35 +++++++++++++++++--------- drivers/net/wireless/wl12xx/conf.h | 3 ++- drivers/net/wireless/wl12xx/main.c | 48 ++++++++++++++++++++++++++++++------ drivers/net/wireless/wl12xx/wl12xx.h | 5 ++++ 4 files changed, 70 insertions(+), 21 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 50676b36ad26..e005aa40ef84 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -965,10 +965,13 @@ int wl1271_acx_ap_mem_cfg(struct wl1271 *wl) } /* memory config */ - mem_conf->num_stations = wl->conf.mem.num_stations; - mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; - mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; - mem_conf->num_ssid_profiles = wl->conf.mem.ssid_profiles; + /* 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->total_tx_descriptors = cpu_to_le32(ACX_TX_DESCRIPTORS); ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, @@ -986,6 +989,7 @@ out: int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) { struct wl1271_acx_sta_config_memory *mem_conf; + struct conf_memory_settings *mem; int ret; wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); @@ -996,16 +1000,21 @@ int wl1271_acx_sta_mem_cfg(struct wl1271 *wl) goto out; } + if (wl->chip.id == CHIP_ID_1283_PG20) + mem = &wl->conf.mem_wl128x; + else + mem = &wl->conf.mem_wl127x; + /* memory config */ - mem_conf->num_stations = wl->conf.mem.num_stations; - mem_conf->rx_mem_block_num = wl->conf.mem.rx_block_num; - mem_conf->tx_min_mem_block_num = wl->conf.mem.tx_min_block_num; - mem_conf->num_ssid_profiles = wl->conf.mem.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); - mem_conf->dyn_mem_enable = wl->conf.mem.dynamic_memory; - mem_conf->tx_free_req = wl->conf.mem.min_req_tx_blocks; - mem_conf->rx_free_req = wl->conf.mem.min_req_rx_blocks; - mem_conf->tx_min = wl->conf.mem.tx_min; + mem_conf->dyn_mem_enable = mem->dynamic_memory; + mem_conf->tx_free_req = mem->min_req_tx_blocks; + mem_conf->rx_free_req = mem->min_req_rx_blocks; + mem_conf->tx_min = mem->tx_min; ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, sizeof(*mem_conf)); @@ -1072,6 +1081,8 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) wl1271_debug(DEBUG_TX, "available tx blocks: %d", wl->tx_blocks_available); + wl->tx_new_total = wl->tx_blocks_available; + return 0; } diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index a00f22c6c74f..743bd0beb63b 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1204,7 +1204,8 @@ struct conf_drv_settings { struct conf_scan_settings scan; struct conf_rf_settings rf; struct conf_ht_setting ht; - struct conf_memory_settings mem; + struct conf_memory_settings mem_wl127x; + struct conf_memory_settings mem_wl128x; }; #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fe0cf47a656c..3c381ceadb98 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -298,7 +298,7 @@ static struct conf_drv_settings default_conf = { .tx_ba_win_size = 64, .inactivity_timeout = 10000, }, - .mem = { + .mem_wl127x = { .num_stations = 1, .ssid_profiles = 1, .rx_block_num = 70, @@ -307,7 +307,17 @@ static struct conf_drv_settings default_conf = { .min_req_tx_blocks = 100, .min_req_rx_blocks = 22, .tx_min = 27, - } + }, + .mem_wl128x = { + .num_stations = 1, + .ssid_profiles = 1, + .rx_block_num = 40, + .tx_min_block_num = 40, + .dynamic_memory = 1, + .min_req_tx_blocks = 45, + .min_req_rx_blocks = 22, + .tx_min = 27, + }, }; static void __wl1271_op_remove_interface(struct wl1271 *wl); @@ -608,16 +618,27 @@ static void wl1271_fw_status(struct wl1271 *wl, { struct wl1271_fw_common_status *status = &full_status->common; struct timespec ts; + u32 old_tx_blk_count = wl->tx_blocks_available; u32 total = 0; int i; - if (wl->bss_type == BSS_TYPE_AP_BSS) + if (wl->bss_type == BSS_TYPE_AP_BSS) { wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(struct wl1271_fw_ap_status), false); - else + } else { wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(struct wl1271_fw_sta_status), false); + /* Update tx total blocks change */ + wl->tx_total_diff += + ((struct wl1271_fw_sta_status *)status)->tx_total - + wl->tx_new_total; + + /* Update total tx blocks */ + wl->tx_new_total = + ((struct wl1271_fw_sta_status *)status)->tx_total; + } + wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", status->intr, @@ -627,17 +648,28 @@ static void wl1271_fw_status(struct wl1271 *wl, /* update number of available TX blocks */ for (i = 0; i < NUM_TX_QUEUES; i++) { - u32 cnt = le32_to_cpu(status->tx_released_blks[i]) - + total += le32_to_cpu(status->tx_released_blks[i]) - wl->tx_blocks_freed[i]; wl->tx_blocks_freed[i] = le32_to_cpu(status->tx_released_blks[i]); - wl->tx_blocks_available += cnt; - total += cnt; + + } + + /* + * By adding the freed blocks to tx_total_diff we are actually + * moving them to the RX pool. + */ + wl->tx_total_diff += total; + + /* if we have positive difference, add the blocks to the TX pool */ + if (wl->tx_total_diff >= 0) { + wl->tx_blocks_available += wl->tx_total_diff; + wl->tx_total_diff = 0; } /* if more blocks are available now, tx work can be scheduled */ - if (total) + if (wl->tx_blocks_available > old_tx_blk_count) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); /* for AP update num of allocated TX blocks per link and ps status */ diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 4b556776fd57..ad04b8337a25 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -267,6 +267,8 @@ struct wl1271_fw_sta_status { u8 tx_total; u8 reserved1; __le16 reserved2; + /* Total structure size is 68 bytes */ + u32 padding; } __packed; struct wl1271_fw_full_status { @@ -397,6 +399,9 @@ struct wl1271 { u32 tx_blocks_freed[NUM_TX_QUEUES]; u32 tx_blocks_available; u32 tx_results_count; + /* Indicates how many memory blocks should be moved to the RX pool */ + int tx_total_diff; + u32 tx_new_total; /* Transmitted TX packets counter for chipset interface */ u32 tx_packets_count; -- cgit v1.2.3 From 1aed55fd784d000fb6741cefb68712d64817bd68 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 6 Mar 2011 16:32:18 +0200 Subject: wl12xx: 1281/1283 support - Use different FW file for AP mode wl127x/wl128x chips Choose a different FW for AP-mode wl127x and wl128x chips, base on chip ID at boot time. Signed-off-by: Arik Nemtsov Reviewed-by: Luciano Coelho Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 5 ++++- drivers/net/wireless/wl12xx/sdio.c | 3 ++- drivers/net/wireless/wl12xx/spi.c | 3 ++- drivers/net/wireless/wl12xx/wl12xx.h | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 54ac6757c39b..9a7ca6524d20 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -832,7 +832,10 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) switch (wl->bss_type) { case BSS_TYPE_AP_BSS: - fw_name = WL1271_AP_FW_NAME; + if (wl->chip.id == CHIP_ID_1283_PG20) + fw_name = WL128X_AP_FW_NAME; + else + fw_name = WL127X_AP_FW_NAME; break; case BSS_TYPE_IBSS: case BSS_TYPE_STA_BSS: diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index f6dd3dea4f30..5a2951ed6eda 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -361,4 +361,5 @@ MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME); -MODULE_FIRMWARE(WL1271_AP_FW_NAME); +MODULE_FIRMWARE(WL127X_AP_FW_NAME); +MODULE_FIRMWARE(WL128X_AP_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index f5525361f2f5..7b82b5f0e490 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -493,5 +493,6 @@ MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME); -MODULE_FIRMWARE(WL1271_AP_FW_NAME); +MODULE_FIRMWARE(WL127X_AP_FW_NAME); +MODULE_FIRMWARE(WL128X_AP_FW_NAME); MODULE_ALIAS("spi:wl1271"); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index ad04b8337a25..890c1a54382d 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -132,7 +132,8 @@ extern u32 wl12xx_debug_level; #define WL1271_FW_NAME "ti-connectivity/wl1271-fw-2.bin" #define WL128X_FW_NAME "ti-connectivity/wl128x-fw.bin" -#define WL1271_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin" +#define WL127X_AP_FW_NAME "ti-connectivity/wl1271-fw-ap.bin" +#define WL128X_AP_FW_NAME "ti-connectivity/wl128x-fw-ap.bin" /* * wl127x and wl128x are using the same NVS file name. However, the -- cgit v1.2.3 From e7ddf549f3f2da156f5c12921e6699024e80a3f4 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 10 Mar 2011 15:24:57 +0200 Subject: wl12xx: use 1 spare TX block instead of two All the new firmware versions (>=6.1.3.50.58 for STA and >=6.2.0.0.47 for AP) use 1 spare TX block. We still want to support older firmwares that require 2 spare blocks, so added a quirk to handle the difference. Also implemented a generic way of setting quirks that depend on the firmware revision. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 24 ++++++++++++++++++++++++ drivers/net/wireless/wl12xx/tx.c | 10 ++++++++-- drivers/net/wireless/wl12xx/tx.h | 1 - drivers/net/wireless/wl12xx/wl12xx.h | 28 +++++++++++++++++++++++++--- 4 files changed, 57 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0b9d41f14b28..db7ab856363e 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1040,6 +1040,24 @@ out: return ret; } +static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl) +{ + unsigned int quirks = 0; + unsigned int *fw_ver = wl->chip.fw_ver; + + /* Only for wl127x */ + if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) && + /* Check STA version */ + (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && + (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) || + /* Check AP version */ + ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) && + (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN)))) + quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS; + + return quirks; +} + int wl1271_plt_start(struct wl1271 *wl) { int retries = WL1271_BOOT_RETRIES; @@ -1075,6 +1093,9 @@ int wl1271_plt_start(struct wl1271 *wl) wl->state = WL1271_STATE_PLT; wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver_str); + + /* Check if any quirks are needed with older fw versions */ + wl->quirks |= wl1271_get_fw_ver_quirks(wl); goto out; irq_disable: @@ -1353,6 +1374,9 @@ power_off: strncpy(wiphy->fw_version, wl->chip.fw_ver_str, sizeof(wiphy->fw_version)); + /* Check if any quirks are needed with older fw versions */ + wl->quirks |= wl1271_get_fw_ver_quirks(wl); + /* * Now we know if 11a is supported (info from the NVS), so disable * 11a channels if not supported diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 75222a681296..109878c3246e 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -135,6 +135,12 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, u32 len; u32 total_blocks; int id, ret = -EBUSY; + u32 spare_blocks; + + if (unlikely(wl->quirks & WL12XX_QUIRK_USE_2_SPARE_BLOCKS)) + spare_blocks = 2; + else + spare_blocks = 1; if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE) return -EAGAIN; @@ -152,7 +158,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, len = total_len; total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE + - TX_HW_BLOCK_SPARE; + spare_blocks; if (total_blocks <= wl->tx_blocks_available) { desc = (struct wl1271_tx_hw_descr *)skb_push( @@ -162,7 +168,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, if (wl->chip.id == CHIP_ID_1283_PG20) { desc->wl128x_mem.total_mem_blocks = total_blocks; } else { - desc->wl127x_mem.extra_blocks = TX_HW_BLOCK_SPARE; + desc->wl127x_mem.extra_blocks = spare_blocks; desc->wl127x_mem.total_mem_blocks = total_blocks; } diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 6f45e9108d9a..a3877ba32d37 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -25,7 +25,6 @@ #ifndef __TX_H__ #define __TX_H__ -#define TX_HW_BLOCK_SPARE 2 #define TX_HW_BLOCK_SIZE 252 #define TX_HW_MGMT_PKT_LIFETIME_TU 2000 diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 890c1a54382d..b04481aadf9c 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -207,13 +207,29 @@ struct wl1271_partition_set { struct wl1271; -#define WL12XX_NUM_FW_VER 5 +enum { + FW_VER_CHIP, + FW_VER_IF_TYPE, + FW_VER_MAJOR, + FW_VER_SUBTYPE, + FW_VER_MINOR, + + NUM_FW_VER +}; + +#define FW_VER_CHIP_WL127X 6 +#define FW_VER_CHIP_WL128X 7 + +#define FW_VER_IF_TYPE_STA 1 +#define FW_VER_IF_TYPE_AP 2 + +#define FW_VER_MINOR_1_SPARE_STA_MIN 58 +#define FW_VER_MINOR_1_SPARE_AP_MIN 47 -/* FIXME: I'm not sure about this structure name */ struct wl1271_chip { u32 id; char fw_ver_str[ETHTOOL_BUSINFO_LEN]; - unsigned int fw_ver[WL12XX_NUM_FW_VER]; + unsigned int fw_ver[NUM_FW_VER]; }; struct wl1271_stats { @@ -594,4 +610,10 @@ int wl1271_plt_stop(struct wl1271 *wl); /* Each RX/TX transaction requires an end-of-transaction transfer */ #define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0) +/* + * Older firmwares use 2 spare TX blocks + * (for STA < 6.1.3.50.58 or for AP < 6.2.0.0.47) + */ +#define WL12XX_QUIRK_USE_2_SPARE_BLOCKS BIT(1) + #endif -- cgit v1.2.3 From 4a31c11c7d8c482598754a577a8fb71abb61ffa0 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Mon, 21 Mar 2011 23:16:14 +0200 Subject: wl12xx: use a bitmask instead of list of booleans in scanned_ch We were using an array of booleans to mark the channels we had already scanned. This was causing a sparse error, because bool is not a type with defined size. To fix this, use bitmasks instead, which is much cleaner anyway. Thanks Johannes Berg for the idea. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 7 +++++-- drivers/net/wireless/wl12xx/scan.c | 17 ++++++++++------- drivers/net/wireless/wl12xx/wl12xx.h | 3 ++- 3 files changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 85cb4daac9a0..9663326c0dfa 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1423,8 +1423,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) if (wl->scan.state != WL1271_SCAN_STATE_IDLE) { wl->scan.state = WL1271_SCAN_STATE_IDLE; - kfree(wl->scan.scanned_ch); - wl->scan.scanned_ch = NULL; + memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, true); } @@ -3502,6 +3501,10 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); + /* make sure all our channels fit in the scanned_ch bitmask */ + BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + + ARRAY_SIZE(wl1271_channels_5ghz) > + WL1271_MAX_CHANNELS); /* * We keep local copies of the band structs because we need to * modify them on a per-device basis. diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 420653a2859c..5d0544c8f3f5 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -48,8 +48,7 @@ void wl1271_scan_complete_work(struct work_struct *work) goto out; wl->scan.state = WL1271_SCAN_STATE_IDLE; - kfree(wl->scan.scanned_ch); - wl->scan.scanned_ch = NULL; + memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, false); @@ -87,7 +86,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, flags = req->channels[i]->flags; - if (!wl->scan.scanned_ch[i] && + if (!test_bit(i, wl->scan.scanned_ch) && !(flags & IEEE80211_CHAN_DISABLED) && ((!!(flags & IEEE80211_CHAN_PASSIVE_SCAN)) == passive) && (req->channels[i]->band == band)) { @@ -124,7 +123,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, memset(&channels[j].bssid_msb, 0xff, 2); /* Mark the channels we already used */ - wl->scan.scanned_ch[i] = true; + set_bit(i, wl->scan.scanned_ch); j++; } @@ -291,6 +290,12 @@ void wl1271_scan_stm(struct wl1271 *wl) int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, struct cfg80211_scan_request *req) { + /* + * cfg80211 should guarantee that we don't get more channels + * than what we have registered. + */ + BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); + if (wl->scan.state != WL1271_SCAN_STATE_IDLE) return -EBUSY; @@ -304,10 +309,8 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, } wl->scan.req = req; + memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); - wl->scan.scanned_ch = kcalloc(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, diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index b04481aadf9c..ba98e1853842 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -302,9 +302,10 @@ struct wl1271_rx_mem_pool_addr { u32 addr_extra; }; +#define WL1271_MAX_CHANNELS 64 struct wl1271_scan { struct cfg80211_scan_request *req; - bool *scanned_ch; + unsigned long scanned_ch[BITS_TO_LONGS(WL1271_MAX_CHANNELS)]; bool failed; u8 state; u8 ssid[IW_ESSID_MAX_SIZE+1]; -- cgit v1.2.3 From 13026decf7b74d0908df034dc6dc86c2caaec939 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 29 Mar 2011 16:43:50 +0300 Subject: wl12xx: Handle duplicate calling of remove interface Because of the hardware recovery mechanism, its possible the __wl1271_op_remove_interface is called twice. Currently, this leads to a kernel crash even before a kernel WARNing can be issued. Fix this. Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 29 ++++++++++++++++++++++++++--- drivers/net/wireless/wl12xx/wl12xx.h | 3 ++- 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 3f6517dda629..57d0af6cfa64 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1304,6 +1304,16 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } + /* + * in some very corner case HW recovery scenarios its possible to + * get here before __wl1271_op_remove_interface is complete, so + * opt out if that is the case. + */ + if (test_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags)) { + ret = -EBUSY; + goto out; + } + switch (vif->type) { case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; @@ -1372,6 +1382,7 @@ power_off: wl->vif = vif; wl->state = WL1271_STATE_ON; + set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags); wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str); /* update hw/fw version info in wiphy struct */ @@ -1409,14 +1420,16 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); + /* because of hardware recovery, we may get here twice */ + if (wl->state != WL1271_STATE_ON) + return; + wl1271_info("down"); mutex_lock(&wl_list_mutex); list_del(&wl->list); mutex_unlock(&wl_list_mutex); - WARN_ON(wl->state != WL1271_STATE_ON); - /* enable dyn ps just in case (if left on due to fw crash etc) */ if (wl->bss_type == BSS_TYPE_STA_BSS) ieee80211_enable_dyn_ps(wl->vif); @@ -1428,6 +1441,10 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) ieee80211_scan_completed(wl->hw, true); } + /* + * this must be before the cancel_work calls below, so that the work + * functions don't perform further work. + */ wl->state = WL1271_STATE_OFF; mutex_unlock(&wl->mutex); @@ -1464,7 +1481,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->time_offset = 0; wl->session_counter = 0; wl->rate_set = CONF_TX_RATE_MASK_BASIC; - wl->flags = 0; wl->vif = NULL; wl->filters = 0; wl1271_free_ap_keys(wl); @@ -1473,6 +1489,13 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->ap_ps_map = 0; wl->block_size = 0; + /* + * this is performed after the cancel_work calls and the associated + * mutex_lock, so that wl1271_op_add_interface does not accidentally + * get executed before all these vars have been reset. + */ + wl->flags = 0; + for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index ba98e1853842..c12945958848 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -354,7 +354,8 @@ enum wl12xx_flags { WL1271_FLAG_PSPOLL_FAILURE, WL1271_FLAG_STA_STATE_SENT, WL1271_FLAG_FW_TX_BUSY, - WL1271_FLAG_AP_STARTED + WL1271_FLAG_AP_STARTED, + WL1271_FLAG_IF_INITIALIZED, }; struct wl1271_link { -- cgit v1.2.3 From 0da13da767cd568c1fe2a7b5b936e86e521b5ae7 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:06:58 +0200 Subject: wl12xx: Clean up the block size alignment code Simplify and clean up the block size alignment code: 1. Set the block size according to the padding field type, as it cannot exceed the maximum value this field can hold. 2. Move the alignment code into a function instead of duplicating it in multiple places. 3. In the current implementation, the block_size member can be misleading because a zero value actually means that there's no need to align. Declare a block size alignment quirk instead. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/init.c | 2 +- drivers/net/wireless/wl12xx/io.c | 3 ++- drivers/net/wireless/wl12xx/main.c | 4 ++-- drivers/net/wireless/wl12xx/sdio.c | 12 ++---------- drivers/net/wireless/wl12xx/tx.c | 26 ++++++++++++++------------ drivers/net/wireless/wl12xx/tx.h | 9 +++++++++ drivers/net/wireless/wl12xx/wl12xx.h | 10 ++++++---- 7 files changed, 36 insertions(+), 30 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 2dbc08331ca9..cf466074237d 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -514,7 +514,7 @@ int wl1271_chip_specific_init(struct wl1271 *wl) if (wl->chip.id == CHIP_ID_1283_PG20) { u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE; - if (wl1271_set_block_size(wl)) + if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT) /* Enable SDIO padding */ host_cfg_bitmap |= HOST_IF_CFG_TX_PAD_TO_SDIO_BLK; diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index aa40c98e8fd3..da5c1ad942a4 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c @@ -29,6 +29,7 @@ #include "wl12xx.h" #include "wl12xx_80211.h" #include "io.h" +#include "tx.h" #define OCP_CMD_LOOP 32 @@ -46,7 +47,7 @@ bool wl1271_set_block_size(struct wl1271 *wl) { if (wl->if_ops->set_block_size) { - wl->if_ops->set_block_size(wl); + wl->if_ops->set_block_size(wl, WL12XX_BUS_BLOCK_SIZE); return true; } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 01205ea5e7e4..1cd396306e7b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1017,6 +1017,8 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) ret = wl1271_setup(wl); if (ret < 0) goto out; + if (wl1271_set_block_size(wl)) + wl->quirks |= WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT; break; case CHIP_ID_1283_PG10: default: @@ -1487,7 +1489,6 @@ 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->block_size = 0; /* * this is performed after the cancel_work calls and the associated @@ -3632,7 +3633,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; - wl->block_size = 0; 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/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 2ade222f7cbb..8246e9de4306 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -51,15 +51,10 @@ static const struct sdio_device_id wl1271_devices[] = { }; MODULE_DEVICE_TABLE(sdio, wl1271_devices); -/* The max SDIO block size is 256 when working with tx padding to SDIO block */ -#define TX_PAD_SDIO_BLK_SIZE 256 - -static void wl1271_sdio_set_block_size(struct wl1271 *wl) +static void wl1271_sdio_set_block_size(struct wl1271 *wl, unsigned int blksz) { - wl->block_size = TX_PAD_SDIO_BLK_SIZE; - sdio_claim_host(wl->if_priv); - sdio_set_block_size(wl->if_priv, TX_PAD_SDIO_BLK_SIZE); + sdio_set_block_size(wl->if_priv, blksz); sdio_release_host(wl->if_priv); } @@ -178,9 +173,6 @@ static int wl1271_sdio_power_on(struct wl1271 *wl) sdio_claim_host(func); sdio_enable_func(func); - /* Set the default block size in case it was modified */ - sdio_set_block_size(func, 0); - out: return ret; } diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 7686bc1ff16a..ba69ba7051fa 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -149,6 +149,15 @@ u8 wl1271_tx_get_hlid(struct sk_buff *skb) } } +static unsigned int wl12xx_calc_packet_alignment(struct wl1271 *wl, + unsigned int packet_length) +{ + if (wl->quirks & WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT) + return ALIGN(packet_length, WL12XX_BUS_BLOCK_SIZE); + else + return ALIGN(packet_length, WL1271_TX_ALIGN_TO); +} + static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, u32 buf_offset, u8 hlid) { @@ -174,10 +183,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, /* approximate the number of blocks required for this packet in the firmware */ - if (wl->block_size) - len = ALIGN(total_len, wl->block_size); - else - len = total_len; + len = wl12xx_calc_packet_alignment(wl, total_len); total_blocks = (len + TX_HW_BLOCK_SIZE - 1) / TX_HW_BLOCK_SIZE + spare_blocks; @@ -291,9 +297,9 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY; desc->reserved = 0; - if (wl->block_size) { - aligned_len = ALIGN(skb->len, wl->block_size); + aligned_len = wl12xx_calc_packet_alignment(wl, skb->len); + if (wl->chip.id == CHIP_ID_1283_PG20) { desc->wl128x_mem.extra_bytes = aligned_len - skb->len; desc->length = cpu_to_le16(aligned_len >> 2); @@ -306,8 +312,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, } else { int pad; - /* align the length (and store in terms of words) */ - aligned_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + /* Store the aligned length in terms of words */ desc->length = cpu_to_le16(aligned_len >> 2); /* calculate number of padding bytes */ @@ -386,10 +391,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, * In special cases, we want to align to a specific block size * (eg. for wl128x with SDIO we align to 256). */ - if (wl->block_size) - total_len = ALIGN(skb->len, wl->block_size); - else - total_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); + total_len = wl12xx_calc_packet_alignment(wl, skb->len); memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index a3877ba32d37..d6b05d980626 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -89,6 +89,15 @@ struct wl128x_tx_mem { u8 extra_bytes; } __packed; +/* + * On wl128x based devices, when TX packets are aggregated, each packet + * size must be aligned to the SDIO block size. The maximum block size + * is bounded by the type of the padded bytes field that is sent to the + * FW. Currently the type is u8, so the maximum block size is 256 bytes. + */ +#define WL12XX_BUS_BLOCK_SIZE min(512u, \ + (1u << (8 * sizeof(((struct wl128x_tx_mem *) 0)->extra_bytes)))) + struct wl1271_tx_hw_descr { /* Length of packet in words, including descriptor+header+data */ __le16 length; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index c12945958848..c7c42b687f5e 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -323,7 +323,7 @@ struct wl1271_if_operations { struct device* (*dev)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl); - void (*set_block_size) (struct wl1271 *wl); + void (*set_block_size) (struct wl1271 *wl, unsigned int blksz); }; #define MAX_NUM_KEYS 14 @@ -558,7 +558,6 @@ struct wl1271 { bool ba_support; u8 ba_rx_bitmap; - u32 block_size; int tcxo_clock; /* @@ -610,12 +609,15 @@ int wl1271_plt_stop(struct wl1271 *wl); /* Quirks */ /* Each RX/TX transaction requires an end-of-transaction transfer */ -#define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0) +#define WL12XX_QUIRK_END_OF_TRANSACTION BIT(0) /* * Older firmwares use 2 spare TX blocks * (for STA < 6.1.3.50.58 or for AP < 6.2.0.0.47) */ -#define WL12XX_QUIRK_USE_2_SPARE_BLOCKS BIT(1) +#define WL12XX_QUIRK_USE_2_SPARE_BLOCKS BIT(1) + +/* WL128X requires aggregated packets to be aligned to the SDIO block size */ +#define WL12XX_QUIRK_BLOCKSIZE_ALIGNMENT BIT(2) #endif -- cgit v1.2.3 From 990f5de7384f9e5922e4c7c7572cbf4f29a9441e Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:06:59 +0200 Subject: wl12xx: Clean up the dummy packet mechanism The current implementation allocates a skb each time one is requested by the firmware. Since dummy packets are handled differently than regular packets, the skb needs to be marked. Currently, this is done by setting the pkt_type member to 5. This might not be safe, as we cannot be sure that there won't be any other packets with this pkt_type value. Since the packet does not change from one request to another, we can simply allocate a dummy packet template and always send it. All changes to the skb done during packet preparation must be reverted, so the same skb can be reused. The dummy packets are not transmitted, therefore there's no need to set the BSSID or our own MAC address. In addition, the header portion of the packet was zeroed by mistake, so fix that as well. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 79 ++++++++++++++++++++++++------------ drivers/net/wireless/wl12xx/tx.c | 43 ++++++++++++++------ drivers/net/wireless/wl12xx/tx.h | 2 - drivers/net/wireless/wl12xx/wl12xx.h | 4 ++ 4 files changed, 89 insertions(+), 39 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1cd396306e7b..7dce24c0b331 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1212,20 +1212,46 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) spin_unlock_irqrestore(&wl->wl_lock, flags); } -#define TX_DUMMY_PACKET_SIZE 1400 int wl1271_tx_dummy_packet(struct wl1271 *wl) { - struct sk_buff *skb = NULL; + unsigned long flags; + + spin_lock_irqsave(&wl->wl_lock, flags); + set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); + wl->tx_queue_count++; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + /* The FW is low on RX memory blocks, so send the dummy packet asap */ + if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) + wl1271_tx_work_locked(wl); + + /* + * If the FW TX is busy, TX work will be scheduled by the threaded + * interrupt handler function + */ + return 0; +} + +/* + * The size of the dummy packet should be at least 1400 bytes. However, in + * order to minimize the number of bus transactions, aligning it to 512 bytes + * boundaries could be beneficial, performance wise + */ +#define TOTAL_TX_DUMMY_PACKET_SIZE (ALIGN(1400, 512)) + +struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) +{ + struct sk_buff *skb; struct ieee80211_hdr_3addr *hdr; - int ret = 0; + unsigned int dummy_packet_size; + + dummy_packet_size = TOTAL_TX_DUMMY_PACKET_SIZE - + sizeof(struct wl1271_tx_hw_descr) - sizeof(*hdr); - skb = dev_alloc_skb( - sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) + - TX_DUMMY_PACKET_SIZE); + skb = dev_alloc_skb(TOTAL_TX_DUMMY_PACKET_SIZE); if (!skb) { - wl1271_warning("failed to allocate buffer for dummy packet"); - ret = -ENOMEM; - goto out; + wl1271_warning("Failed to allocate a dummy packet skb"); + return NULL; } skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr)); @@ -1233,29 +1259,22 @@ int wl1271_tx_dummy_packet(struct wl1271 *wl) hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); memset(hdr, 0, sizeof(*hdr)); hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_FCTL_TODS | - IEEE80211_STYPE_NULLFUNC); - - memcpy(hdr->addr1, wl->bssid, ETH_ALEN); - memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN); - memcpy(hdr->addr3, wl->bssid, ETH_ALEN); + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); - skb_put(skb, TX_DUMMY_PACKET_SIZE); + memset(skb_put(skb, dummy_packet_size), 0, dummy_packet_size); - memset(skb->data, 0, TX_DUMMY_PACKET_SIZE); - - skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ; /* Dummy packets require the TID to be management */ skb->priority = WL1271_TID_MGMT; - /* CONF_TX_AC_VO */ - skb->queue_mapping = 0; - wl1271_op_tx(wl->hw, skb); + /* Initialize all fields that might be used */ + skb->queue_mapping = 0; + memset(IEEE80211_SKB_CB(skb), 0, sizeof(struct ieee80211_tx_info)); -out: - return ret; + return skb; } + static struct notifier_block wl1271_dev_notifier = { .notifier_call = wl1271_dev_notify, }; @@ -3653,11 +3672,17 @@ struct ieee80211_hw *wl1271_alloc_hw(void) goto err_hw; } + wl->dummy_packet = wl12xx_alloc_dummy_packet(wl); + if (!wl->dummy_packet) { + ret = -ENOMEM; + goto err_aggr; + } + /* Register platform device */ ret = platform_device_register(wl->plat_dev); if (ret) { wl1271_error("couldn't register platform device"); - goto err_aggr; + goto err_dummy_packet; } dev_set_drvdata(&wl->plat_dev->dev, wl); @@ -3683,6 +3708,9 @@ err_bt_coex_state: err_platform: platform_device_unregister(wl->plat_dev); +err_dummy_packet: + dev_kfree_skb(wl->dummy_packet); + err_aggr: free_pages((unsigned long)wl->aggr_buf, order); @@ -3702,6 +3730,7 @@ EXPORT_SYMBOL_GPL(wl1271_alloc_hw); int wl1271_free_hw(struct wl1271 *wl) { platform_device_unregister(wl->plat_dev); + dev_kfree_skb(wl->dummy_packet); free_pages((unsigned long)wl->aggr_buf, get_order(WL1271_AGGR_BUFFER_SIZE)); kfree(wl->plat_dev); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ba69ba7051fa..67245ad396ea 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -219,6 +219,11 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, return ret; } +static bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) +{ + return wl->dummy_packet == skb; +} + static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, u32 extra, struct ieee80211_tx_info *control, u8 hlid) @@ -253,7 +258,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); desc->tid = skb->priority; - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { + if (wl12xx_is_dummy_packet(wl, skb)) { /* * FW expects the dummy packet to have an invalid session id - * any session id that is different than the one set in the join @@ -396,6 +401,10 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); + /* Revert side effects in the dummy packet skb, so it can be reused */ + if (wl12xx_is_dummy_packet(wl, skb)) + skb_pull(skb, sizeof(struct wl1271_tx_hw_descr)); + return total_len; } @@ -508,10 +517,23 @@ out: static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) { + unsigned long flags; + struct sk_buff *skb = NULL; + if (wl->bss_type == BSS_TYPE_AP_BSS) - return wl1271_ap_skb_dequeue(wl); + skb = wl1271_ap_skb_dequeue(wl); + else + skb = wl1271_sta_skb_dequeue(wl); - return wl1271_sta_skb_dequeue(wl); + if (!skb && + test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) { + skb = wl->dummy_packet; + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count--; + spin_unlock_irqrestore(&wl->wl_lock, flags); + } + + return skb; } static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) @@ -519,7 +541,9 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) unsigned long flags; int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); - if (wl->bss_type == BSS_TYPE_AP_BSS) { + if (wl12xx_is_dummy_packet(wl, skb)) { + set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); + } else if (wl->bss_type == BSS_TYPE_AP_BSS) { u8 hlid = wl1271_tx_get_hlid(skb); skb_queue_head(&wl->links[hlid].tx_queue[q], skb); @@ -628,8 +652,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, skb = wl->tx_frames[id]; info = IEEE80211_SKB_CB(skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); + if (wl12xx_is_dummy_packet(wl, skb)) { wl1271_free_tx_id(wl, id); return; } @@ -764,9 +787,7 @@ void wl1271_tx_reset(struct wl1271 *wl) wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); - } else { + if (!wl12xx_is_dummy_packet(wl, skb)) { info = IEEE80211_SKB_CB(skb); info->status.rates[0].idx = -1; info->status.rates[0].count = 0; @@ -792,9 +813,7 @@ void wl1271_tx_reset(struct wl1271 *wl) wl1271_free_tx_id(wl, i); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - if (skb->pkt_type == TX_PKT_TYPE_DUMMY_REQ) { - dev_kfree_skb(skb); - } else { + if (!wl12xx_is_dummy_packet(wl, skb)) { /* * Remove private headers before passing the skb to * mac80211 diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index d6b05d980626..fc7835c4cf63 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -42,8 +42,6 @@ #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) -#define TX_PKT_TYPE_DUMMY_REQ 5 - #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_HEADER_PAD 1 #define TX_HW_ATTR_OFST_SESSION_COUNTER 2 diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index c7c42b687f5e..1b430d2aec4e 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -356,6 +356,7 @@ enum wl12xx_flags { WL1271_FLAG_FW_TX_BUSY, WL1271_FLAG_AP_STARTED, WL1271_FLAG_IF_INITIALIZED, + WL1271_FLAG_DUMMY_PACKET_PENDING, }; struct wl1271_link { @@ -461,6 +462,9 @@ struct wl1271 { /* Intermediate buffer, used for packet aggregation */ u8 *aggr_buf; + /* Reusable dummy packet template */ + struct sk_buff *dummy_packet; + /* Network stack work */ struct work_struct netstack_work; -- cgit v1.2.3 From d2f4d47d84f8c665ab9babb2cc84d2e7872a96e1 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:07:00 +0200 Subject: wl12xx: Simplify TX blocks accounting The total number of TX memory blocks may change when the dynamic memory option is enabled. The current implementation only tracks the available memory blocks, which over-complicates TX blocks accounting. By tracking the number of allocated blocks, calculation of the number of available blocks becomes simpler and cleaner. It simply equals the total number of TX memory blocks minus the allocated ones. Also, remove some unnecessary castings and use union member accesses instead. Signed-off-by: Ido Yariv Signed-off-by: Arik Nemtsov Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 2 -- drivers/net/wireless/wl12xx/main.c | 48 +++++++++++++++++------------------- drivers/net/wireless/wl12xx/tx.c | 1 + drivers/net/wireless/wl12xx/wl12xx.h | 4 +-- 4 files changed, 24 insertions(+), 31 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index e005aa40ef84..b277947400b5 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1081,8 +1081,6 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl) wl1271_debug(DEBUG_TX, "available tx blocks: %d", wl->tx_blocks_available); - wl->tx_new_total = wl->tx_blocks_available; - return 0; } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 7dce24c0b331..1feb9551ef8f 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -622,7 +622,7 @@ static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_common_status *status = &full_status->common; struct timespec ts; u32 old_tx_blk_count = wl->tx_blocks_available; - u32 total = 0; + u32 freed_blocks = 0; int i; if (wl->bss_type == BSS_TYPE_AP_BSS) { @@ -631,15 +631,6 @@ static void wl1271_fw_status(struct wl1271 *wl, } else { wl1271_raw_read(wl, FW_STATUS_ADDR, status, sizeof(struct wl1271_fw_sta_status), false); - - /* Update tx total blocks change */ - wl->tx_total_diff += - ((struct wl1271_fw_sta_status *)status)->tx_total - - wl->tx_new_total; - - /* Update total tx blocks */ - wl->tx_new_total = - ((struct wl1271_fw_sta_status *)status)->tx_total; } wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " @@ -651,34 +642,38 @@ static void wl1271_fw_status(struct wl1271 *wl, /* update number of available TX blocks */ for (i = 0; i < NUM_TX_QUEUES; i++) { - total += le32_to_cpu(status->tx_released_blks[i]) - - wl->tx_blocks_freed[i]; + freed_blocks += le32_to_cpu(status->tx_released_blks[i]) - + wl->tx_blocks_freed[i]; wl->tx_blocks_freed[i] = le32_to_cpu(status->tx_released_blks[i]); - } - /* - * By adding the freed blocks to tx_total_diff we are actually - * moving them to the RX pool. - */ - wl->tx_total_diff += total; + wl->tx_allocated_blocks -= freed_blocks; + + if (wl->bss_type == BSS_TYPE_AP_BSS) { + /* Update num of allocated TX blocks per link and ps status */ + wl1271_irq_update_links_status(wl, &full_status->ap); + wl->tx_blocks_available += freed_blocks; + } else { + int avail = full_status->sta.tx_total - wl->tx_allocated_blocks; - /* if we have positive difference, add the blocks to the TX pool */ - if (wl->tx_total_diff >= 0) { - wl->tx_blocks_available += wl->tx_total_diff; - wl->tx_total_diff = 0; + /* + * The FW might change the total number of TX memblocks before + * we get a notification about blocks being released. Thus, the + * available blocks calculation might yield a temporary result + * which is lower than the actual available blocks. Keeping in + * mind that only blocks that were allocated can be moved from + * TX to RX, tx_blocks_available should never decrease here. + */ + wl->tx_blocks_available = max((int)wl->tx_blocks_available, + avail); } /* if more blocks are available now, tx work can be scheduled */ if (wl->tx_blocks_available > old_tx_blk_count) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); - /* for AP update num of allocated TX blocks per link and ps status */ - if (wl->bss_type == BSS_TYPE_AP_BSS) - wl1271_irq_update_links_status(wl, &full_status->ap); - /* update the host-chipset time offset */ getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - @@ -1495,6 +1490,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->psm_entry_retry = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->tx_blocks_available = 0; + wl->tx_allocated_blocks = 0; wl->tx_results_count = 0; wl->tx_packets_count = 0; wl->tx_security_last_seq = 0; diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 67245ad396ea..7a3339fd3415 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -203,6 +203,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, desc->id = id; wl->tx_blocks_available -= total_blocks; + wl->tx_allocated_blocks += total_blocks; if (wl->bss_type == BSS_TYPE_AP_BSS) wl->links[hlid].allocated_blks += total_blocks; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 1b430d2aec4e..9ccbcfdd0802 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -418,10 +418,8 @@ struct wl1271 { /* Accounting for allocated / available TX blocks on HW */ u32 tx_blocks_freed[NUM_TX_QUEUES]; u32 tx_blocks_available; + u32 tx_allocated_blocks; u32 tx_results_count; - /* Indicates how many memory blocks should be moved to the RX pool */ - int tx_total_diff; - u32 tx_new_total; /* Transmitted TX packets counter for chipset interface */ u32 tx_packets_count; -- cgit v1.2.3 From 341b7cde6ccc60672fcd7fc84dd24a1b7c0b8d94 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Thu, 31 Mar 2011 10:07:01 +0200 Subject: wl12xx: Handle platforms without level trigger interrupts Some platforms are incapable of triggering on level interrupts. Add a platform quirks member in the platform data structure, as well as an edge interrupt quirk which can be set on such platforms. When the interrupt is requested with IRQF_TRIGGER_RISING, IRQF_ONESHOT cannot be used, as we might miss interrupts that occur after the FW status is cleared and before the threaded interrupt handler exits. Moreover, when IRQF_ONESHOT is not set, iterating more than once in the threaded interrupt handler introduces a few race conditions between this handler and the hardirq handler. Currently this is worked around by limiting the loop to one iteration only. This workaround has an impact on performance. To remove to this restriction, the race conditions will need to be addressed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 9 +++++++++ drivers/net/wireless/wl12xx/sdio.c | 9 ++++++++- drivers/net/wireless/wl12xx/spi.c | 9 ++++++++- drivers/net/wireless/wl12xx/wl12xx.h | 3 +++ include/linux/wl12xx.h | 4 ++++ 5 files changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1feb9551ef8f..7126506611c1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "wl12xx.h" #include "wl12xx_80211.h" @@ -719,6 +720,13 @@ irqreturn_t wl1271_irq(int irq, void *cookie) set_bit(WL1271_FLAG_TX_PENDING, &wl->flags); cancel_work_sync(&wl->tx_work); + /* + * In case edge triggered interrupt must be used, we cannot iterate + * more than once without introducing race conditions with the hardirq. + */ + if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) + loopcount = 1; + mutex_lock(&wl->mutex); wl1271_debug(DEBUG_IRQ, "IRQ work"); @@ -3648,6 +3656,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->ap_ps_map = 0; wl->ap_fw_ps_map = 0; wl->quirks = 0; + wl->platform_quirks = 0; 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/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 8246e9de4306..bcd4ad7ba90d 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -220,6 +220,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, struct ieee80211_hw *hw; const struct wl12xx_platform_data *wlan_data; struct wl1271 *wl; + unsigned long irqflags; int ret; /* We are only able to handle the wlan function */ @@ -251,9 +252,15 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->irq = wlan_data->irq; wl->ref_clock = wlan_data->board_ref_clock; wl->tcxo_clock = wlan_data->board_tcxo_clock; + wl->platform_quirks = wlan_data->platform_quirks; + + if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) + irqflags = IRQF_TRIGGER_RISING; + else + irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irqflags, DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 7b82b5f0e490..51662bb68019 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -364,6 +364,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) struct wl12xx_platform_data *pdata; struct ieee80211_hw *hw; struct wl1271 *wl; + unsigned long irqflags; int ret; pdata = spi->dev.platform_data; @@ -402,6 +403,12 @@ static int __devinit wl1271_probe(struct spi_device *spi) wl->ref_clock = pdata->board_ref_clock; wl->tcxo_clock = pdata->board_tcxo_clock; + wl->platform_quirks = pdata->platform_quirks; + + if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) + irqflags = IRQF_TRIGGER_RISING; + else + irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; wl->irq = spi->irq; if (wl->irq < 0) { @@ -411,7 +418,7 @@ static int __devinit wl1271_probe(struct spi_device *spi) } ret = request_threaded_irq(wl->irq, wl1271_hardirq, wl1271_irq, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + irqflags, DRIVER_NAME, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9ccbcfdd0802..fb2b79fa42b4 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -579,6 +579,9 @@ struct wl1271 { /* Quirks of specific hardware revisions */ unsigned int quirks; + + /* Platform limitations */ + unsigned int platform_quirks; }; struct wl1271_station { diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h index c1a743ea7470..4b697395326e 100644 --- a/include/linux/wl12xx.h +++ b/include/linux/wl12xx.h @@ -53,8 +53,12 @@ struct wl12xx_platform_data { bool use_eeprom; int board_ref_clock; int board_tcxo_clock; + unsigned long platform_quirks; }; +/* Platform does not support level trigger interrupts */ +#define WL12XX_PLATFORM_QUIRK_EDGE_IRQ BIT(0) + #ifdef CONFIG_WL12XX_PLATFORM_DATA int wl12xx_set_platform_data(const struct wl12xx_platform_data *data); -- cgit v1.2.3 From 47684808fd89d6809c0886e06f8ac324252499d8 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Tue, 26 Apr 2011 23:21:51 +0300 Subject: wl12xx: support FW TX inactivity triggers In AP mode we register for the MAX_TX_RETRY and INACTIVE_STA events. Both are reported to the upper layers as a TX failure in the offending stations. In STA mode we register only for the MAX_TX_RETRY event. A TX failure is interpreted as a loss of connection. Support for IEEE80211_HW_REPORTS_TX_ACK_STATUS has been removed to avoid the inherent race condition of a mac80211 TX failure counter in addition to the FW counter. This patch depends on "mac80211: allow low level driver to report packet loss" Signed-off-by: Arik Nemtsov Signed-off-by: John W. Linville --- 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, 125 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/wl12xx/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index b277947400b5..a5c9c0aff83f 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1524,22 +1524,46 @@ out: return ret; } -int wl1271_acx_max_tx_retry(struct wl1271 *wl) +int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl) { - struct wl1271_acx_max_tx_retry *acx = NULL; + struct wl1271_acx_ap_max_tx_retry *acx = NULL; int ret; - wl1271_debug(DEBUG_ACX, "acx max tx retry"); + wl1271_debug(DEBUG_ACX, "acx ap max tx retry"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); if (!acx) return -ENOMEM; - acx->max_tx_retry = cpu_to_le16(wl->conf.tx.ap_max_tx_retries); + acx->max_tx_retry = cpu_to_le16(wl->conf.tx.max_tx_retries); ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx)); if (ret < 0) { - wl1271_warning("acx max tx retry failed: %d", ret); + 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); goto out; } diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 0a40caeab2a2..942908cd53a3 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1145,7 +1145,7 @@ struct wl1271_acx_fw_tsf_information { u8 padding[3]; } __packed; -struct wl1271_acx_max_tx_retry { +struct wl1271_acx_ap_max_tx_retry { struct acx_header header; /* @@ -1156,6 +1156,13 @@ struct wl1271_acx_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; @@ -1307,7 +1314,8 @@ 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_max_tx_retry(struct wl1271 *wl); +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); diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 2b0cf85788b3..d263ebb6f974 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -478,10 +478,12 @@ 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 | + MAX_TX_RETRY_EVENT_ID; if (wl->bss_type == BSS_TYPE_AP_BSS) - wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; + wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID | + INACTIVE_STA_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 246804428517..d48331682e7e 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1070,7 +1070,7 @@ int wl1271_cmd_start_bss(struct wl1271 *wl) memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN); - cmd->aging_period = cpu_to_le16(WL1271_AP_DEF_INACTIV_SEC); + cmd->aging_period = cpu_to_le16(wl->conf.tx.ap_aging_period); 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 6c9e3a673e6c..d16094f2604f 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -683,10 +683,18 @@ struct conf_tx_settings { struct conf_tx_rate_class ap_bcst_conf; /* - * AP-mode - allow this number of TX retries to a station before an + * Allow this number of TX retries to a connected station/AP 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. */ - u16 ap_max_tx_retries; + u8 max_tx_retries; + + /* + * AP-mode - after this number of seconds a connected station is + * considered inactive. + */ + u16 ap_aging_period; /* * Configuration for TID parameters. diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index ae69330e807c..d7be3aec6fc3 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -174,6 +174,8 @@ 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); @@ -235,9 +237,54 @@ 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 b6cf06e565a4..7ae5a0821241 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -58,13 +58,16 @@ enum { CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), - ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20), + 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), @@ -119,7 +122,11 @@ 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]; @@ -130,4 +137,7 @@ 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 cf466074237d..ab3b1e21de29 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -375,6 +375,10 @@ 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; @@ -441,7 +445,7 @@ static int wl1271_ap_hw_init(struct wl1271 *wl) if (ret < 0) return ret; - ret = wl1271_acx_max_tx_retry(wl); + ret = wl1271_acx_ap_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 866453bc1d1f..0c69e959d0de 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -192,7 +192,8 @@ static struct conf_drv_settings default_conf = { .long_retry_limit = 10, .aflags = 0, }, - .ap_max_tx_retries = 100, + .max_tx_retries = 100, + .ap_aging_period = 300, .tid_conf_count = 4, .tid_conf = { [CONF_TX_AC_BE] = { @@ -2953,6 +2954,12 @@ 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) @@ -3535,7 +3542,6 @@ 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 fb2b79fa42b4..7c521af58e7d 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -172,7 +172,6 @@ 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 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/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index b8deada5d020..b59b67711a17 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 7c521af58e7d..f3de96212b96 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 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/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index ec0156b3e276..c6ee530e5bf7 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 75338f9947cf..9a895e3cc613 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 d263ebb6f974..2b0cf85788b3 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 d8596ae3a64f..2116a376c3f3 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 c0045f0b8b4e..1f947368f9ee 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 d7be3aec6fc3..ae69330e807c 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 7ae5a0821241..b6cf06e565a4 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 b1242a6de27b..a8f4f156c055 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 6dd42c987663..6dab6f0c91bc 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 f3de96212b96..b7601438ecac 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 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/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index dc110e8a618e..1e4bd6a2c396 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 88d2e9052a0d..a14a035aa440 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 d78044f00810..668ff46a6824 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 b7601438ecac..10f076770fed 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 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/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 10f076770fed..ab0c2f155b8c 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 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/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a14a035aa440..4b421d801873 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 ab0c2f155b8c..9629e90d9b55 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 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/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 4b421d801873..8f9e6152f3b7 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 5b03fd5ee33f..41183db34836 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 9629e90d9b55..2218b9c63844 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 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/wl12xx.h') diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 1e4bd6a2c396..c3c554cd6580 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 8f9e6152f3b7..610be03a198b 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 c41bd0a711bc..25eb9bc9b628 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 2218b9c63844..fbe8f46d1232 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