From 059625e757754459d8adb370e3d751abdd51723b Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 21 Dec 2011 22:36:27 +0200 Subject: [PATCH 01/50] wl1251: fix sparse warning The wl1251 driver was generating the following warning: drivers/net/wireless/wl1251/boot.c:467:21: warning: incorrect type in assignment (different base types) drivers/net/wireless/wl1251/boot.c:467:21: expected unsigned int [unsigned] [assigned] [usertype] val drivers/net/wireless/wl1251/boot.c:467:21: got restricted __le32 [usertype] Fix this by removing one cpu_to_le32() call in the wrong place. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl1251/boot.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/wl1251/boot.c b/drivers/net/wireless/wl1251/boot.c index d729daf8e841..a2e5241382da 100644 --- a/drivers/net/wireless/wl1251/boot.c +++ b/drivers/net/wireless/wl1251/boot.c @@ -464,8 +464,6 @@ static int wl1251_boot_upload_nvs(struct wl1251 *wl) val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); - val = cpu_to_le32(val); - wl1251_debug(DEBUG_BOOT, "nvs write table 0x%x: 0x%x", nvs_start, val); From ac9e2d9afa90ecb7ee1b419cce6969f31a138f77 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 21 Dec 2011 22:36:28 +0200 Subject: [PATCH 02/50] wl1251: convert 32-bit values to le32 before writing to the chip The 32-bit values were not converted before writing them to the chip. Change the wl1251_read32() and wl1251_write32() so that they always read and write le32 values and convert to and from the CPU endianess. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl1251/io.h | 9 ++++----- drivers/net/wireless/wl1251/wl1251.h | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/wl1251/io.h b/drivers/net/wireless/wl1251/io.h index c545e9d5f512..d382877c34cc 100644 --- a/drivers/net/wireless/wl1251/io.h +++ b/drivers/net/wireless/wl1251/io.h @@ -36,16 +36,15 @@ static inline u32 wl1251_read32(struct wl1251 *wl, int addr) { - u32 response; + wl->if_ops->read(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32)); - wl->if_ops->read(wl, addr, &response, sizeof(u32)); - - return response; + return le32_to_cpu(wl->buffer_32); } static inline void wl1251_write32(struct wl1251 *wl, int addr, u32 val) { - wl->if_ops->write(wl, addr, &val, sizeof(u32)); + wl->buffer_32 = cpu_to_le32(val); + wl->if_ops->write(wl, addr, &wl->buffer_32, sizeof(wl->buffer_32)); } static inline u32 wl1251_read_elp(struct wl1251 *wl, int addr) diff --git a/drivers/net/wireless/wl1251/wl1251.h b/drivers/net/wireless/wl1251/wl1251.h index a77f1bbbed0a..9d8f5816c6f9 100644 --- a/drivers/net/wireless/wl1251/wl1251.h +++ b/drivers/net/wireless/wl1251/wl1251.h @@ -380,7 +380,7 @@ struct wl1251 { struct wl1251_stats stats; struct wl1251_debugfs debugfs; - u32 buffer_32; + __le32 buffer_32; u32 buffer_cmd; u8 buffer_busyword[WL1251_BUSY_WORD_LEN]; struct wl1251_rx_descriptor *rx_descriptor; From ebff1bae9445af019e7e3c5b62964ec99b5a8250 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 21 Dec 2011 22:36:29 +0200 Subject: [PATCH 03/50] wl1251: enable sparse endianess check by default Following the good example of the Intel (and more recently Atheros) drivers, enable endianess check by default when running sparse. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl1251/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/wl1251/Makefile b/drivers/net/wireless/wl1251/Makefile index 58b4f935a3f6..a5c6328b5f72 100644 --- a/drivers/net/wireless/wl1251/Makefile +++ b/drivers/net/wireless/wl1251/Makefile @@ -6,3 +6,5 @@ wl1251_sdio-objs += sdio.o obj-$(CONFIG_WL1251) += wl1251.o obj-$(CONFIG_WL1251_SPI) += wl1251_spi.o obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o + +ccflags-y += -D__CHECK_ENDIAN__ From 15944aef29b6a3d96c52f3140c08b5a9d5fda0e9 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 21 Dec 2011 21:52:31 +0200 Subject: [PATCH 04/50] wl12xx: enable sparse endianess check by default Following the good example of the Intel (and more recently Atheros) drivers, enable endianess check by default when running sparse. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index fe67262ba19f..98f289c907a9 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o # small builtin driver bit obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o + +ccflags-y += -D__CHECK_ENDIAN__ From 9e0dc890ce23e2b28ce906bb8f466ca66f420911 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Wed, 11 Jan 2012 14:22:42 +0100 Subject: [PATCH 05/50] wl12xx: Make sure HW is available in sched scan ops The sched_scan_(stop|start) ops fails to check for WL1271_STATE_OFF. This can lead to a race where the driver tries to access the HW while it's off. Fix this by checking for WL1271_STATE_OFF before accessing the HW. Signed-off-by: Pontus Fuchs Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f8748cedbae1..f2a958c9537a 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3108,6 +3108,11 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, mutex_lock(&wl->mutex); + if (wl->state == WL1271_STATE_OFF) { + ret = -EAGAIN; + goto out; + } + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; @@ -3139,6 +3144,9 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, mutex_lock(&wl->mutex); + if (wl->state == WL1271_STATE_OFF) + goto out; + ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; From f3df1331f25f782e838a3ecb72cec86b539ac02f Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Wed, 11 Jan 2012 09:42:39 +0200 Subject: [PATCH 06/50] wl12xx: Acquire lock before stopping plt __wl1271_plt_stop is called from both wl1271_plt_stop and wl1271_unregister_hw. While wl1271_plt_stop acquires a mutex, wl1271_unregister_hw does not. Fix this by calling wl1271_plt_stop instead of __wl1271_plt_stop from wl1271_unregister_hw. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f2a958c9537a..ac6866469073 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1391,13 +1391,15 @@ int wl1271_plt_start(struct wl1271 *wl) return ret; } -static int __wl1271_plt_stop(struct wl1271 *wl) +int wl1271_plt_stop(struct wl1271 *wl) { int ret = 0; wl1271_notice("power down"); + mutex_lock(&wl->mutex); if (wl->state != WL1271_STATE_PLT) { + mutex_unlock(&wl->mutex); wl1271_error("cannot power down because not in PLT " "state: %d", wl->state); ret = -EBUSY; @@ -1410,25 +1412,15 @@ static int __wl1271_plt_stop(struct wl1271 *wl) wl->rx_counter = 0; mutex_unlock(&wl->mutex); + wl1271_disable_interrupts(wl); wl1271_flush_deferred_work(wl); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); - mutex_lock(&wl->mutex); out: return ret; } -int wl1271_plt_stop(struct wl1271 *wl) -{ - int ret; - - mutex_lock(&wl->mutex); - ret = __wl1271_plt_stop(wl); - mutex_unlock(&wl->mutex); - return ret; -} - static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; @@ -4881,7 +4873,7 @@ static int wl1271_register_hw(struct wl1271 *wl) static void wl1271_unregister_hw(struct wl1271 *wl) { if (wl->state == WL1271_STATE_PLT) - __wl1271_plt_stop(wl); + wl1271_plt_stop(wl); unregister_netdevice_notifier(&wl1271_dev_notifier); ieee80211_unregister_hw(wl->hw); From a454969ede024a209e6a755d5f7a928ab81ed00c Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Wed, 11 Jan 2012 09:42:40 +0200 Subject: [PATCH 07/50] wl12xx: Power off after flushing work When stopping plt, the chip is powered off before all current work items are flushed and interrupts are disabled. This might introduce a race in which the driver tries to communicate with a powered off chip. Fix this by powering off the device only after interrupts are disabled and all work items are flushed. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index ac6866469073..9e9fb3fb8f01 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1406,8 +1406,6 @@ int wl1271_plt_stop(struct wl1271 *wl) goto out; } - wl1271_power_off(wl); - wl->state = WL1271_STATE_OFF; wl->rx_counter = 0; @@ -1417,6 +1415,11 @@ int wl1271_plt_stop(struct wl1271 *wl) wl1271_flush_deferred_work(wl); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); + + mutex_lock(&wl->mutex); + wl1271_power_off(wl); + mutex_unlock(&wl->mutex); + out: return ret; } From 46b0cc9ffce356fbcb2135219a3ed8500714a9e4 Mon Sep 17 00:00:00 2001 From: Ido Yariv Date: Wed, 11 Jan 2012 09:42:41 +0200 Subject: [PATCH 08/50] wl12xx: Fix potential interrupt storm The interrupt threaded handler exits immediately if the driver's state is WL1271_STATE_OFF. As a result, the interrupt status is not read. If the interrupt is level triggered, it will be fired again. Fix this by disabling interrupts before setting the state to OFF. Signed-off-by: Ido Yariv Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 9e9fb3fb8f01..3a1636b68cd2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1397,9 +1397,23 @@ int wl1271_plt_stop(struct wl1271 *wl) wl1271_notice("power down"); + /* + * Interrupts must be disabled before setting the state to OFF. + * Otherwise, the interrupt handler might be called and exit without + * reading the interrupt status. + */ + wl1271_disable_interrupts(wl); mutex_lock(&wl->mutex); if (wl->state != WL1271_STATE_PLT) { mutex_unlock(&wl->mutex); + + /* + * This will not necessarily enable interrupts as interrupts + * may have been disabled when op_stop was called. It will, + * however, balance the above call to disable_interrupts(). + */ + wl1271_enable_interrupts(wl); + wl1271_error("cannot power down because not in PLT " "state: %d", wl->state); ret = -EBUSY; @@ -1411,7 +1425,6 @@ int wl1271_plt_stop(struct wl1271 *wl) mutex_unlock(&wl->mutex); - wl1271_disable_interrupts(wl); wl1271_flush_deferred_work(wl); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); @@ -1773,11 +1786,25 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); + /* + * Interrupts must be disabled before setting the state to OFF. + * Otherwise, the interrupt handler might be called and exit without + * reading the interrupt status. + */ + wl1271_disable_interrupts(wl); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { mutex_unlock(&wl->mutex); + + /* + * This will not necessarily enable interrupts as interrupts + * may have been disabled when op_stop was called. It will, + * however, balance the above call to disable_interrupts(). + */ + wl1271_enable_interrupts(wl); return; } + /* * this must be before the cancel_work calls below, so that the work * functions don't perform further work. @@ -1789,7 +1816,6 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) list_del(&wl->list); mutex_unlock(&wl_list_mutex); - wl1271_disable_interrupts(wl); wl1271_flush_deferred_work(wl); cancel_delayed_work_sync(&wl->scan_complete_work); cancel_work_sync(&wl->netstack_work); From f6fbeccd3e513c23de9cd8562f2b2e78d4d17912 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 11 Jan 2012 09:42:42 +0200 Subject: [PATCH 09/50] wl12xx: cancel delayed elp work and clear flags when stopping PLT In some cases a race condition can happen if we don't cancel any pending ELP work before stopping PLT. With this commit we cancel ELP work and clear the wl->flags bitmask. Also clean up the wl elements after powering off. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 3a1636b68cd2..3587afcd6469 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1420,17 +1420,18 @@ int wl1271_plt_stop(struct wl1271 *wl) goto out; } - wl->state = WL1271_STATE_OFF; - wl->rx_counter = 0; - mutex_unlock(&wl->mutex); wl1271_flush_deferred_work(wl); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->recovery_work); + cancel_delayed_work_sync(&wl->elp_work); mutex_lock(&wl->mutex); wl1271_power_off(wl); + wl->flags = 0; + wl->state = WL1271_STATE_OFF; + wl->rx_counter = 0; mutex_unlock(&wl->mutex); out: From 0becb14ab42c2435402437a9dbc279599f6af8fb Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Thu, 12 Jan 2012 14:45:34 +0200 Subject: [PATCH 10/50] wl12xx: move partition table definition to io.c Up till now we only needed to access the partition table in boot.c. But to add support for reading the MAC address from the FUSE in testmode, we will have to change the partition in testmode.c. Thus, we move the partition table to io.c and export it via io.h. It makes more sense to have it in the io part anyway. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 73 +++--------------------------- drivers/net/wireless/wl12xx/io.c | 59 ++++++++++++++++++++++++ drivers/net/wireless/wl12xx/io.h | 2 + 3 files changed, 68 insertions(+), 66 deletions(-) diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 8f9cf5a816ea..84d295126edf 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -33,65 +33,6 @@ #include "event.h" #include "rx.h" -static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { - [PART_DOWN] = { - .mem = { - .start = 0x00000000, - .size = 0x000177c0 - }, - .reg = { - .start = REGISTERS_BASE, - .size = 0x00008800 - }, - .mem2 = { - .start = 0x00000000, - .size = 0x00000000 - }, - .mem3 = { - .start = 0x00000000, - .size = 0x00000000 - }, - }, - - [PART_WORK] = { - .mem = { - .start = 0x00040000, - .size = 0x00014fc0 - }, - .reg = { - .start = REGISTERS_BASE, - .size = 0x0000a000 - }, - .mem2 = { - .start = 0x003004f8, - .size = 0x00000004 - }, - .mem3 = { - .start = 0x00040404, - .size = 0x00000000 - }, - }, - - [PART_DRPW] = { - .mem = { - .start = 0x00040000, - .size = 0x00014fc0 - }, - .reg = { - .start = DRPW_BASE, - .size = 0x00006000 - }, - .mem2 = { - .start = 0x00000000, - .size = 0x00000000 - }, - .mem3 = { - .start = 0x00000000, - .size = 0x00000000 - } - } -}; - static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) { u32 cpu_ctrl; @@ -181,13 +122,13 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, return -ENOMEM; } - memcpy(&partition, &part_table[PART_DOWN], sizeof(partition)); + memcpy(&partition, &wl12xx_part_table[PART_DOWN], sizeof(partition)); partition.mem.start = dest; wl1271_set_partition(wl, &partition); /* 10.1 set partition limit and chunk num */ chunk_num = 0; - partition_limit = part_table[PART_DOWN].mem.size; + partition_limit = wl12xx_part_table[PART_DOWN].mem.size; while (chunk_num < fw_data_len / CHUNK_SIZE) { /* 10.2 update partition, if needed */ @@ -195,7 +136,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, if (addr > partition_limit) { addr = dest + chunk_num * CHUNK_SIZE; partition_limit = chunk_num * CHUNK_SIZE + - part_table[PART_DOWN].mem.size; + wl12xx_part_table[PART_DOWN].mem.size; partition.mem.start = addr; wl1271_set_partition(wl, &partition); } @@ -383,7 +324,7 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) nvs_len -= nvs_ptr - (u8 *)wl->nvs; /* Now we must set the partition correctly */ - wl1271_set_partition(wl, &part_table[PART_WORK]); + wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]); /* Copy the NVS tables to a new block to ensure alignment */ nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); @@ -492,7 +433,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) wl->event_box_addr = wl1271_read32(wl, REG_EVENT_MAILBOX_PTR); /* set the working partition to its "running" mode offset */ - wl1271_set_partition(wl, &part_table[PART_WORK]); + wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]); wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", wl->cmd_box_addr, wl->event_box_addr); @@ -769,7 +710,7 @@ int wl1271_load_firmware(struct wl1271 *wl) wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); udelay(500); - wl1271_set_partition(wl, &part_table[PART_DRPW]); + wl1271_set_partition(wl, &wl12xx_part_table[PART_DRPW]); /* Read-modify-write DRPW_SCRATCH_START register (see next state) to be used by DRPw FW. The RTRIM value will be added by the FW @@ -788,7 +729,7 @@ int wl1271_load_firmware(struct wl1271 *wl) wl1271_write32(wl, DRPW_SCRATCH_START, clk); - wl1271_set_partition(wl, &part_table[PART_WORK]); + wl1271_set_partition(wl, &wl12xx_part_table[PART_WORK]); /* Disable interrupts */ wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index 079ad380e8ff..c574a3b31e31 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c @@ -45,6 +45,65 @@ #define OCP_STATUS_REQ_FAILED 0x20000 #define OCP_STATUS_RESP_ERROR 0x30000 +struct wl1271_partition_set wl12xx_part_table[PART_TABLE_LEN] = { + [PART_DOWN] = { + .mem = { + .start = 0x00000000, + .size = 0x000177c0 + }, + .reg = { + .start = REGISTERS_BASE, + .size = 0x00008800 + }, + .mem2 = { + .start = 0x00000000, + .size = 0x00000000 + }, + .mem3 = { + .start = 0x00000000, + .size = 0x00000000 + }, + }, + + [PART_WORK] = { + .mem = { + .start = 0x00040000, + .size = 0x00014fc0 + }, + .reg = { + .start = REGISTERS_BASE, + .size = 0x0000a000 + }, + .mem2 = { + .start = 0x003004f8, + .size = 0x00000004 + }, + .mem3 = { + .start = 0x00040404, + .size = 0x00000000 + }, + }, + + [PART_DRPW] = { + .mem = { + .start = 0x00040000, + .size = 0x00014fc0 + }, + .reg = { + .start = DRPW_BASE, + .size = 0x00006000 + }, + .mem2 = { + .start = 0x00000000, + .size = 0x00000000 + }, + .mem3 = { + .start = 0x00000000, + .size = 0x00000000 + } + } +}; + bool wl1271_set_block_size(struct wl1271 *wl) { if (wl->if_ops->set_block_size) { diff --git a/drivers/net/wireless/wl12xx/io.h b/drivers/net/wireless/wl12xx/io.h index d398cbcea986..4fb3dab8c3b2 100644 --- a/drivers/net/wireless/wl12xx/io.h +++ b/drivers/net/wireless/wl12xx/io.h @@ -43,6 +43,8 @@ #define HW_ACCESS_PRAM_MAX_RANGE 0x3c000 +extern struct wl1271_partition_set wl12xx_part_table[PART_TABLE_LEN]; + struct wl1271; void wl1271_disable_interrupts(struct wl1271 *wl); From 30c5dbd1ee191dca1c04c70df0ba1adf0a9769c3 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 18 Jan 2012 14:53:22 +0200 Subject: [PATCH 11/50] wl12xx: read chip ID and HW PG version during probe In order to read the MAC addresses from the fuse ROM, we need to know the chip ID and the HW PG version. We need to know the MAC address during probe, because that's when we register our HW with mac80211. To prepare for that, this patch reads the chip ID and HW PG version during probe instead of doing it at boot time. We power the chip on briefly in order to do that. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 15 -------- drivers/net/wireless/wl12xx/debug.h | 1 + drivers/net/wireless/wl12xx/main.c | 59 ++++++++++++++++++++++------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 84d295126edf..599919e2a60d 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -488,19 +488,6 @@ static int wl1271_boot_write_irq_polarity(struct wl1271 *wl) return 0; } -static void wl1271_boot_hw_version(struct wl1271 *wl) -{ - u32 fuse; - - if (wl->chip.id == CHIP_ID_1283_PG20) - fuse = wl1271_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1); - else - fuse = wl1271_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1); - fuse = (fuse & PG_VER_MASK) >> PG_VER_OFFSET; - - wl->hw_pg_ver = (s8)fuse; -} - static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) { u16 spare_reg; @@ -694,8 +681,6 @@ int wl1271_load_firmware(struct wl1271 *wl) u32 tmp, clk; int selected_clock = -1; - wl1271_boot_hw_version(wl); - if (wl->chip.id == CHIP_ID_1283_PG20) { ret = wl128x_boot_clk(wl, &selected_clock); if (ret < 0) diff --git a/drivers/net/wireless/wl12xx/debug.h b/drivers/net/wireless/wl12xx/debug.h index b85fd8c41e8f..ec0fdc25b280 100644 --- a/drivers/net/wireless/wl12xx/debug.h +++ b/drivers/net/wireless/wl12xx/debug.h @@ -51,6 +51,7 @@ enum { DEBUG_FILTERS = BIT(15), DEBUG_ADHOC = BIT(16), DEBUG_AP = BIT(17), + DEBUG_PROBE = BIT(18), DEBUG_MASTER = (DEBUG_ADHOC | DEBUG_AP), DEBUG_ALL = ~0, }; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 3587afcd6469..a41af84f649d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1232,10 +1232,9 @@ static int wl1271_setup(struct wl1271 *wl) return 0; } -static int wl1271_chip_wakeup(struct wl1271 *wl) +static int wl12xx_set_power_on(struct wl1271 *wl) { - struct wl1271_partition_set partition; - int ret = 0; + int ret; msleep(WL1271_PRE_POWER_ON_SLEEP); ret = wl1271_power_on(wl); @@ -1245,20 +1244,22 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) wl1271_io_reset(wl); wl1271_io_init(wl); - /* We don't need a real memory partition here, because we only want - * to use the registers at this point. */ - memset(&partition, 0, sizeof(partition)); - partition.reg.start = REGISTERS_BASE; - partition.reg.size = REGISTERS_DOWN_SIZE; - wl1271_set_partition(wl, &partition); + wl1271_set_partition(wl, &wl12xx_part_table[PART_DOWN]); /* ELP module wake up */ wl1271_fw_wakeup(wl); - /* whal_FwCtrl_BootSm() */ +out: + return ret; +} - /* 0. read chip id from CHIP_ID */ - wl->chip.id = wl1271_read32(wl, CHIP_ID_B); +static int wl1271_chip_wakeup(struct wl1271 *wl) +{ + int ret = 0; + + ret = wl12xx_set_power_on(wl); + if (ret < 0) + goto out; /* * For wl127x based devices we could use the default block @@ -4858,6 +4859,29 @@ static struct bin_attribute fwlog_attr = { .read = wl1271_sysfs_read_fwlog, }; +static int wl12xx_get_hw_info(struct wl1271 *wl) +{ + int ret; + u32 die_info; + + ret = wl12xx_set_power_on(wl); + if (ret < 0) + goto out; + + wl->chip.id = wl1271_read32(wl, CHIP_ID_B); + + if (wl->chip.id == CHIP_ID_1283_PG20) + die_info = wl1271_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1); + else + die_info = wl1271_top_reg_read(wl, WL127X_REG_FUSE_DATA_2_1); + + wl->hw_pg_ver = (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET; + + wl1271_power_off(wl); +out: + return ret; +} + static int wl1271_register_hw(struct wl1271 *wl) { int ret; @@ -4865,6 +4889,12 @@ static int wl1271_register_hw(struct wl1271 *wl) if (wl->mac80211_registered) return 0; + ret = wl12xx_get_hw_info(wl); + if (ret < 0) { + wl1271_error("couldn't get hw info"); + goto out; + } + ret = wl1271_fetch_nvs(wl); if (ret == 0) { /* NOTE: The wl->nvs->nvs element must be first, in @@ -4886,7 +4916,7 @@ static int wl1271_register_hw(struct wl1271 *wl) ret = ieee80211_register_hw(wl->hw); if (ret < 0) { wl1271_error("unable to register mac80211 hw: %d", ret); - return ret; + goto out; } wl->mac80211_registered = true; @@ -4897,7 +4927,8 @@ static int wl1271_register_hw(struct wl1271 *wl) wl1271_notice("loaded"); - return 0; +out: + return ret; } static void wl1271_unregister_hw(struct wl1271 *wl) From 5e037e74802a10a71dddbb05585753b2fcbd8ad7 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 23 Dec 2011 09:32:17 +0200 Subject: [PATCH 12/50] wl12xx: use two MAC addresses based on the NVS or from fuse ROM Add support for two MAC addresses. If the NVS has a valid MAC address, that takes precedence and we use two sequential address starting from the one specified. If the NVS doesn't contain a valid MAC address (ie. if it is set to 00:00:00:00:00:00), we check if the HW PG version in use has the BD_ADDR written in the fuse ROM. If it does, we read it and derive the two subsequent addresses for WLAN. During production, 3 addresses are reserved per device. The first for Bluetooth (burnt in the fuse ROM) and the following two for WLAN. This patch has some code by Igal and Arik (squashed from internal patches). Signed-off-by: Arik Nemtsov Signed-off-by: Igal Chernobelsky Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 14 ++-- drivers/net/wireless/wl12xx/boot.h | 10 --- drivers/net/wireless/wl12xx/main.c | 99 +++++++++++++++++++++++++--- drivers/net/wireless/wl12xx/reg.h | 27 ++++++++ drivers/net/wireless/wl12xx/wl12xx.h | 7 +- 5 files changed, 131 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 599919e2a60d..32a7f9b69d7c 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -258,12 +258,12 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) } /* update current MAC address to NVS */ - nvs_ptr[11] = wl->mac_addr[0]; - nvs_ptr[10] = wl->mac_addr[1]; - nvs_ptr[6] = wl->mac_addr[2]; - nvs_ptr[5] = wl->mac_addr[3]; - nvs_ptr[4] = wl->mac_addr[4]; - nvs_ptr[3] = wl->mac_addr[5]; + nvs_ptr[11] = wl->addresses[0].addr[0]; + nvs_ptr[10] = wl->addresses[0].addr[1]; + nvs_ptr[6] = wl->addresses[0].addr[2]; + nvs_ptr[5] = wl->addresses[0].addr[3]; + nvs_ptr[4] = wl->addresses[0].addr[4]; + nvs_ptr[3] = wl->addresses[0].addr[5]; /* * Layout before the actual NVS tables: @@ -626,7 +626,7 @@ static int wl127x_boot_clk(struct wl1271 *wl) u32 pause; u32 clk; - if (((wl->hw_pg_ver & PG_MAJOR_VER_MASK) >> PG_MAJOR_VER_OFFSET) < 3) + if (WL127X_PG_GET_MAJOR(wl->hw_pg_ver) < 3) wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; if (wl->ref_clock == CONF_REF_CLK_19_2_E || diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h index 06dad9380fa7..c3adc09f403d 100644 --- a/drivers/net/wireless/wl12xx/boot.h +++ b/drivers/net/wireless/wl12xx/boot.h @@ -55,16 +55,6 @@ struct wl1271_static_data { #define OCP_REG_CLK_POLARITY 0x0cb2 #define OCP_REG_CLK_PULL 0x0cb4 -#define WL127X_REG_FUSE_DATA_2_1 0x050a -#define WL128X_REG_FUSE_DATA_2_1 0x2152 -#define PG_VER_MASK 0x3c -#define PG_VER_OFFSET 2 - -#define PG_MAJOR_VER_MASK 0x3 -#define PG_MAJOR_VER_OFFSET 0x0 -#define PG_MINOR_VER_MASK 0xc -#define PG_MINOR_VER_OFFSET 0x2 - #define CMD_MBOX_ADDRESS 0x407B4 #define POLARITY_LOW BIT(1) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a41af84f649d..6c92f86217cd 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2132,7 +2132,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, * we still need this in order to configure the fw * while uploading the nvs */ - memcpy(wl->mac_addr, vif->addr, ETH_ALEN); + memcpy(wl->addresses[0].addr, vif->addr, ETH_ALEN); booted = wl12xx_init_fw(wl); if (!booted) { @@ -4859,6 +4859,76 @@ static struct bin_attribute fwlog_attr = { .read = wl1271_sysfs_read_fwlog, }; +static bool wl12xx_mac_in_fuse(struct wl1271 *wl) +{ + bool supported = false; + u8 major, minor; + + if (wl->chip.id == CHIP_ID_1283_PG20) { + major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver); + minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver); + + /* in wl128x we have the MAC address if the PG is >= (2, 1) */ + if (major > 2 || (major == 2 && minor >= 1)) + supported = true; + } else { + major = WL127X_PG_GET_MAJOR(wl->hw_pg_ver); + minor = WL127X_PG_GET_MINOR(wl->hw_pg_ver); + + /* in wl127x we have the MAC address if the PG is >= (3, 1) */ + if (major == 3 && minor >= 1) + supported = true; + } + + wl1271_debug(DEBUG_PROBE, + "PG Ver major = %d minor = %d, MAC %s present", + major, minor, supported ? "is" : "is not"); + + return supported; +} + +static void wl12xx_derive_mac_addresses(struct wl1271 *wl, + u32 oui, u32 nic, int n) +{ + int i; + + wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x, n %d", + oui, nic, n); + + if (nic + n - 1 > 0xffffff) + wl1271_warning("NIC part of the MAC address wraps around!"); + + for (i = 0; i < n; i++) { + wl->addresses[i].addr[0] = (u8)(oui >> 16); + wl->addresses[i].addr[1] = (u8)(oui >> 8); + wl->addresses[i].addr[2] = (u8) oui; + wl->addresses[i].addr[3] = (u8)(nic >> 16); + wl->addresses[i].addr[4] = (u8)(nic >> 8); + wl->addresses[i].addr[5] = (u8) nic; + nic++; + } + + wl->hw->wiphy->n_addresses = n; + wl->hw->wiphy->addresses = wl->addresses; +} + +static void wl12xx_get_fuse_mac(struct wl1271 *wl) +{ + u32 mac1, mac2; + + wl1271_set_partition(wl, &wl12xx_part_table[PART_DRPW]); + + mac1 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_1); + mac2 = wl1271_read32(wl, WL12XX_REG_FUSE_BD_ADDR_2); + + /* these are the two parts of the BD_ADDR */ + wl->fuse_oui_addr = ((mac2 & 0xffff) << 8) + + ((mac1 & 0xff000000) >> 24); + wl->fuse_nic_addr = mac1 & 0xffffff; + + wl1271_set_partition(wl, &wl12xx_part_table[PART_DOWN]); +} + static int wl12xx_get_hw_info(struct wl1271 *wl) { int ret; @@ -4877,6 +4947,13 @@ static int wl12xx_get_hw_info(struct wl1271 *wl) wl->hw_pg_ver = (s8) (die_info & PG_VER_MASK) >> PG_VER_OFFSET; + if (!wl12xx_mac_in_fuse(wl)) { + wl->fuse_oui_addr = 0; + wl->fuse_nic_addr = 0; + } else { + wl12xx_get_fuse_mac(wl); + } + wl1271_power_off(wl); out: return ret; @@ -4885,6 +4962,7 @@ static int wl12xx_get_hw_info(struct wl1271 *wl) static int wl1271_register_hw(struct wl1271 *wl) { int ret; + u32 oui_addr = 0, nic_addr = 0; if (wl->mac80211_registered) return 0; @@ -4903,15 +4981,20 @@ static int wl1271_register_hw(struct wl1271 *wl) */ u8 *nvs_ptr = (u8 *)wl->nvs; - wl->mac_addr[0] = nvs_ptr[11]; - wl->mac_addr[1] = nvs_ptr[10]; - wl->mac_addr[2] = nvs_ptr[6]; - wl->mac_addr[3] = nvs_ptr[5]; - wl->mac_addr[4] = nvs_ptr[4]; - wl->mac_addr[5] = nvs_ptr[3]; + oui_addr = + (nvs_ptr[11] << 16) + (nvs_ptr[10] << 8) + nvs_ptr[6]; + nic_addr = + (nvs_ptr[5] << 16) + (nvs_ptr[4] << 8) + nvs_ptr[3]; } - SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); + /* if the MAC address is zeroed in the NVS derive from fuse */ + if (oui_addr == 0 && nic_addr == 0) { + oui_addr = wl->fuse_oui_addr; + /* fuse has the BD_ADDR, the WLAN addresses are the next two */ + nic_addr = wl->fuse_nic_addr + 1; + } + + wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr, 2); ret = ieee80211_register_hw(wl->hw); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/reg.h b/drivers/net/wireless/wl12xx/reg.h index df34d5977b98..340db324bc26 100644 --- a/drivers/net/wireless/wl12xx/reg.h +++ b/drivers/net/wireless/wl12xx/reg.h @@ -525,4 +525,31 @@ b12-b0 - Supported Rate indicator bits as defined below. */ #define INTR_TRIG_TX_PROC1 BIT(18) +#define WL127X_REG_FUSE_DATA_2_1 0x050a +#define WL128X_REG_FUSE_DATA_2_1 0x2152 +#define PG_VER_MASK 0x3c +#define PG_VER_OFFSET 2 + +#define WL127X_PG_MAJOR_VER_MASK 0x3 +#define WL127X_PG_MAJOR_VER_OFFSET 0x0 +#define WL127X_PG_MINOR_VER_MASK 0xc +#define WL127X_PG_MINOR_VER_OFFSET 0x2 + +#define WL128X_PG_MAJOR_VER_MASK 0xc +#define WL128X_PG_MAJOR_VER_OFFSET 0x2 +#define WL128X_PG_MINOR_VER_MASK 0x3 +#define WL128X_PG_MINOR_VER_OFFSET 0x0 + +#define WL127X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL127X_PG_MAJOR_VER_MASK) >> \ + WL127X_PG_MAJOR_VER_OFFSET) +#define WL127X_PG_GET_MINOR(pg_ver) ((pg_ver & WL127X_PG_MINOR_VER_MASK) >> \ + WL127X_PG_MINOR_VER_OFFSET) +#define WL128X_PG_GET_MAJOR(pg_ver) ((pg_ver & WL128X_PG_MAJOR_VER_MASK) >> \ + WL128X_PG_MAJOR_VER_OFFSET) +#define WL128X_PG_GET_MINOR(pg_ver) ((pg_ver & WL128X_PG_MINOR_VER_MASK) >> \ + WL128X_PG_MINOR_VER_OFFSET) + +#define WL12XX_REG_FUSE_BD_ADDR_1 0x00310eb4 +#define WL12XX_REG_FUSE_BD_ADDR_2 0x00310eb8 + #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index b2b09cd02022..1f629fac02e4 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -313,7 +313,12 @@ struct wl1271 { s8 hw_pg_ver; - u8 mac_addr[ETH_ALEN]; + /* address read from the fuse ROM */ + u32 fuse_oui_addr; + u32 fuse_nic_addr; + + /* we have up to 2 MAC addresses */ + struct mac_address addresses[2]; int channel; u8 system_hlid; From 75f25548bea11d35b7cb0879eea595d3f86a0cc4 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 13 Jan 2012 14:04:29 +0200 Subject: [PATCH 13/50] wl12xx: add testmode operation to read the BD_ADDR from Fuse ROM Add a testmode command to retrieve the BD_ADDR that is stored in the Fuse ROM in newer PGs. In old PGs this operation is not supported. The caller can then derive the MAC addresses from it. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/testmode.c | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index 25093c0cb0ed..b6b433e5562f 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -30,6 +30,7 @@ #include "acx.h" #include "reg.h" #include "ps.h" +#include "io.h" #define WL1271_TM_MAX_DATA_LENGTH 1024 @@ -41,6 +42,7 @@ enum wl1271_tm_commands { WL1271_TM_CMD_NVS_PUSH, /* Not in use. Keep to not break ABI */ WL1271_TM_CMD_SET_PLT_MODE, WL1271_TM_CMD_RECOVER, + WL1271_TM_CMD_GET_MAC, __WL1271_TM_CMD_AFTER_LAST }; @@ -264,6 +266,52 @@ static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[]) return 0; } +static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[]) +{ + struct sk_buff *skb; + u8 mac_addr[ETH_ALEN]; + int ret = 0; + + mutex_lock(&wl->mutex); + + if (wl->state != WL1271_STATE_PLT) { + ret = -EINVAL; + goto out; + } + + if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) { + ret = -EOPNOTSUPP; + goto out; + } + + mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16); + mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8); + mac_addr[2] = (u8) wl->fuse_oui_addr; + mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16); + mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8); + mac_addr[5] = (u8) wl->fuse_nic_addr; + + skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + NLA_PUT(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr); + ret = cfg80211_testmode_reply(skb); + if (ret < 0) + goto out; + +out: + mutex_unlock(&wl->mutex); + return ret; + +nla_put_failure: + kfree_skb(skb); + ret = -EMSGSIZE; + goto out; +} + int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) { struct wl1271 *wl = hw->priv; @@ -288,6 +336,8 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) return wl1271_tm_cmd_set_plt_mode(wl, tb); case WL1271_TM_CMD_RECOVER: return wl1271_tm_cmd_recover(wl, tb); + case WL1271_TM_CMD_GET_MAC: + return wl12xx_tm_cmd_get_mac(wl, tb); default: return -EOPNOTSUPP; } From 775e1a4bd57a117ae181afebf59e26fa072db6a8 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 24 Jan 2012 11:46:32 +0200 Subject: [PATCH 14/50] wl12xx: remove unnecessary shadow declaration The vif variable was being declared inside one of the internal blocks of wl1271_event_process. This is not necessary, since this variable is already declared in the function context. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/event.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index d3280df68f5d..cfc38ea21e4a 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -344,7 +344,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) /* TODO: configure only the relevant vif */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); bool success; if (!test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, @@ -352,6 +351,8 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) continue; success = mbox->channel_switch_status ? false : true; + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_chswitch_done(vif, success); } } From 2c882fa47502ef201e0533fc7cc21b097f4bcdca Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 7 Feb 2012 12:37:33 +0200 Subject: [PATCH 15/50] wl12xx: fix typo in fwlog module param description Fix a copy and paste bug in the MODULE_PARAM_DESC for fwlog. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 6c92f86217cd..ce0a233d2cd1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -5444,7 +5444,7 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); module_param_named(fwlog, fwlog_param, charp, 0); -MODULE_PARM_DESC(keymap, +MODULE_PARM_DESC(fwlog, "FW logger options: continuous, ondemand, dbgpins or disable"); module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR); From 7db4ee6d0a932bb95f839d9b427899b74139a8d5 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 24 Jan 2012 18:18:42 +0200 Subject: [PATCH 16/50] wl12xx: check bss_conf->assoc on CHANGED_BSSID with the new auth/assoc redesign, we get CHANGED_BSSID indication before CHANGED_ASSOC indication, while our CHANGED_BSSID handling block assumes we are already associated. Fix it by checking we are either in ibss mode, or already associated. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index ce0a233d2cd1..bba2a52256b6 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3665,7 +3665,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, wlvif->rssi_thold = bss_conf->cqm_rssi_thold; } - if (changed & BSS_CHANGED_BSSID) + if (changed & BSS_CHANGED_BSSID && + (is_ibss || bss_conf->assoc)) if (!is_zero_ether_addr(bss_conf->bssid)) { ret = wl12xx_cmd_build_null_data(wl, wlvif); if (ret < 0) From 2801d69e84d04714c22f8cc22fbe8535ac5f1eb7 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 24 Jan 2012 18:18:43 +0200 Subject: [PATCH 17/50] wl12xx: remove wl1271_tx_update_filters wl1271_tx_update_filters() is used as some workaround to open filters while roaming on the same channel. However, it doesn't handle roaming to a different channel, and it might also sleep in the tx path, which is a bug. With the new auth/assoc redesign, roaming is much simpler, and this function is not needed anymore. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/tx.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 4508ccd78328..2bf43b86a3f9 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -77,35 +77,6 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id) } } -static int wl1271_tx_update_filters(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct sk_buff *skb) -{ - struct ieee80211_hdr *hdr; - int ret; - - hdr = (struct ieee80211_hdr *)skb->data; - - /* - * stop bssid-based filtering before transmitting authentication - * requests. this way the hw will never drop authentication - * responses coming from BSSIDs it isn't familiar with (e.g. on - * roaming) - */ - if (!ieee80211_is_auth(hdr->frame_control)) - return 0; - - if (wlvif->dev_hlid != WL12XX_INVALID_LINK_ID) - goto out; - - wl1271_debug(DEBUG_CMD, "starting device role for roaming"); - ret = wl12xx_start_dev(wl, wlvif); - if (ret < 0) - goto out; -out: - return 0; -} - static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, struct sk_buff *skb) { @@ -187,8 +158,6 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl12xx_tx_get_hlid_ap(wl, wlvif, skb); - wl1271_tx_update_filters(wl, wlvif, skb); - if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) || test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) && !ieee80211_is_auth(hdr->frame_control) && From b4748306368a61b64b97b16f11cb6a82eebb60f9 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 26 Jan 2012 14:12:31 +0200 Subject: [PATCH 18/50] Revert "wl12xx: Change claiming of the SDIO bus" This reverts commit 393fb560d328cc06e6a5c7b7473901ad724f82e7. Commit b6ad726 ("mmc: core: Prevent too long response times for suspend") fails the suspend if the mmc host can't be claimed before suspend. As the host is claimed by us as long as the chip is powered on, suspend will always fail. Revert to claiming the sdio bus only when needed. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 468a50553fac..099c2c9c1a2d 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -74,6 +74,8 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func = dev_to_sdio_func(glue->dev); + sdio_claim_host(func); + if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); dev_dbg(child->parent, "sdio read 52 addr 0x%x, byte 0x%02x\n", @@ -88,6 +90,8 @@ static void wl12xx_sdio_raw_read(struct device *child, int addr, void *buf, addr, len); } + sdio_release_host(func); + if (ret) dev_err(child->parent, "sdio read failed (%d)\n", ret); } @@ -99,6 +103,8 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, struct wl12xx_sdio_glue *glue = dev_get_drvdata(child->parent); struct sdio_func *func = dev_to_sdio_func(glue->dev); + sdio_claim_host(func); + if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); dev_dbg(child->parent, "sdio write 52 addr 0x%x, byte 0x%02x\n", @@ -113,6 +119,8 @@ static void wl12xx_sdio_raw_write(struct device *child, int addr, void *buf, ret = sdio_memcpy_toio(func, addr, buf, len); } + sdio_release_host(func); + if (ret) dev_err(child->parent, "sdio write failed (%d)\n", ret); } @@ -136,6 +144,7 @@ static int wl12xx_sdio_power_on(struct wl12xx_sdio_glue *glue) sdio_claim_host(func); sdio_enable_func(func); + sdio_release_host(func); out: return ret; @@ -146,6 +155,7 @@ static int wl12xx_sdio_power_off(struct wl12xx_sdio_glue *glue) int ret; struct sdio_func *func = dev_to_sdio_func(glue->dev); + sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); From 1ebaa2d041746949c291fff17be9880820f70e55 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 26 Jan 2012 14:12:32 +0200 Subject: [PATCH 19/50] wl12xx: don't release/claim sdio on suspend/resume Since we reverted to claiming the host only when needed, we no longer need to release/claim the host on suspend/resume. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/sdio.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 099c2c9c1a2d..ce3b9a99463e 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -324,9 +324,6 @@ static int wl1271_suspend(struct device *dev) dev_err(dev, "error while trying to keep power\n"); goto out; } - - /* release host */ - sdio_release_host(func); } out: return ret; @@ -334,15 +331,7 @@ static int wl1271_suspend(struct device *dev) static int wl1271_resume(struct device *dev) { - struct sdio_func *func = dev_to_sdio_func(dev); - struct wl12xx_sdio_glue *glue = sdio_get_drvdata(func); - struct wl1271 *wl = platform_get_drvdata(glue->core); - dev_dbg(dev, "wl1271 resume\n"); - if (wl->wow_enabled) { - /* claim back host */ - sdio_claim_host(func); - } return 0; } From cdaac6281170ee2934ad443cb60fbdd6cf318b64 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 11:57:16 +0200 Subject: [PATCH 20/50] wl12xx: fw api change - add role_id to set_template The set_template commands now takes the role_id as parameter. Usually, we'll use the vif's main role_id. However, sometimes we'll want to use wlvif->dev_role_id instead of wlvif->role_id, so pass the wanted role_id as param. Update WL127X_FW_NAME/WL128X_FW_NAME. (This commit starts a series of fw update patches, and changes the start() callback to return an error in order to prevent the use of the driver during the transition. This change will be reverted in the last patch of series) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 37 +++++++++++++++-------- drivers/net/wireless/wl12xx/cmd.h | 18 +++++++---- drivers/net/wireless/wl12xx/init.c | 45 ++++++++++++++++++---------- drivers/net/wireless/wl12xx/main.c | 15 ++++++---- drivers/net/wireless/wl12xx/scan.c | 28 ++++++++++------- drivers/net/wireless/wl12xx/wl12xx.h | 4 +-- 6 files changed, 94 insertions(+), 53 deletions(-) diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 25990bd38be6..28e0a56d794f 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1022,8 +1022,9 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; } -int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, - void *buf, size_t buf_len, int index, u32 rates) +int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, + u16 template_id, void *buf, size_t buf_len, + int index, u32 rates) { struct wl1271_cmd_template_set *cmd; int ret = 0; @@ -1039,6 +1040,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, goto out; } + /* during initialization wlvif is NULL */ + cmd->role_id = role_id; cmd->len = cpu_to_le16(buf_len); cmd->template_type = template_id; cmd->enabled_rates = cpu_to_le32(rates); @@ -1082,7 +1085,8 @@ int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif) ptr = skb->data; } - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size, 0, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_NULL_DATA, ptr, size, 0, wlvif->basic_rate); out: @@ -1105,7 +1109,7 @@ int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl, if (!skb) goto out; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_KLV, skb->data, skb->len, CMD_TEMPL_KLV_IDX_NULL_DATA, wlvif->basic_rate); @@ -1130,7 +1134,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (!skb) goto out; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_PS_POLL, skb->data, skb->len, 0, wlvif->basic_rate_set); out: @@ -1138,9 +1143,10 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; } -int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, +int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len, u8 band) + const u8 *ie, size_t ie_len) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); struct sk_buff *skb; @@ -1158,10 +1164,12 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); if (band == IEEE80211_BAND_2GHZ) - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + ret = wl1271_cmd_template_set(wl, role_id, + CMD_TEMPL_CFG_PROBE_REQ_2_4, skb->data, skb->len, 0, rate); else - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + ret = wl1271_cmd_template_set(wl, role_id, + CMD_TEMPL_CFG_PROBE_REQ_5, skb->data, skb->len, 0, rate); out: @@ -1186,10 +1194,12 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]); if (wlvif->band == IEEE80211_BAND_2GHZ) - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_CFG_PROBE_REQ_2_4, skb->data, skb->len, 0, rate); else - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_CFG_PROBE_REQ_5, skb->data, skb->len, 0, rate); if (ret < 0) @@ -1235,7 +1245,7 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN); tmpl.sender_ip = ip_addr; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP, &tmpl, sizeof(tmpl), 0, wlvif->basic_rate); @@ -1260,7 +1270,8 @@ int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif) /* FIXME: not sure what priority to use here */ template.qos_ctrl = cpu_to_le16(0); - return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, + return wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_QOS_NULL_DATA, &template, sizeof(template), 0, wlvif->basic_rate); } diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 3f7d0b93c24d..8e8035d33068 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -54,14 +54,16 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 ps_mode); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); -int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, - void *buf, size_t buf_len, int index, u32 rates); +int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, + u16 template_id, void *buf, size_t buf_len, + int index, u32 rates); int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 aid); -int wl1271_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, +int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 role_id, u8 band, const u8 *ssid, size_t ssid_len, - const u8 *ie, size_t ie_len, u8 band); + const u8 *ie, size_t ie_len); struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb); @@ -191,7 +193,7 @@ enum cmd_templ { /* unit ms */ #define WL1271_COMMAND_TIMEOUT 2000 #define WL1271_CMD_TEMPL_DFLT_SIZE 252 -#define WL1271_CMD_TEMPL_MAX_SIZE 548 +#define WL1271_CMD_TEMPL_MAX_SIZE 512 #define WL1271_EVENT_TIMEOUT 750 struct wl1271_cmd_header { @@ -364,14 +366,18 @@ struct cmd_enabledisable_path { struct wl1271_cmd_template_set { struct wl1271_cmd_header header; - __le16 len; + u8 role_id; u8 template_type; + __le16 len; u8 index; /* relevant only for KLV_TEMPLATE type */ + u8 padding[3]; + __le32 enabled_rates; u8 short_retry_limit; u8 long_retry_limit; u8 aflags; u8 reserved; + u8 template_data[WL1271_CMD_TEMPL_MAX_SIZE]; } __packed; diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index ca7ee59e4505..fcf2b128f9a4 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -39,50 +39,58 @@ int wl1271_init_templates_config(struct wl1271 *wl) int ret, i; /* send empty templates for fw memory reservation */ - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_CFG_PROBE_REQ_5, NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_NULL_DATA, NULL, sizeof(struct wl12xx_null_data_template), 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_PS_POLL, NULL, sizeof(struct wl12xx_ps_poll_template), 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_QOS_NULL_DATA, NULL, sizeof (struct ieee80211_qos_hdr), 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_PROBE_RESPONSE, NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_BEACON, NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_ARP_RSP, NULL, sizeof (struct wl12xx_arp_rsp_template), 0, WL1271_RATE_AUTOMATIC); @@ -93,19 +101,22 @@ int wl1271_init_templates_config(struct wl1271 *wl) * Put very large empty placeholders for all templates. These * reserve memory for later. */ - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_AP_PROBE_RESPONSE, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_AP_BEACON, NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_DEAUTH_AP, NULL, sizeof (struct wl12xx_disconn_template), 0, WL1271_RATE_AUTOMATIC); @@ -113,7 +124,8 @@ int wl1271_init_templates_config(struct wl1271 *wl) return ret; for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL, + ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, + CMD_TEMPL_KLV, NULL, sizeof(struct ieee80211_qos_hdr), i, WL1271_RATE_AUTOMATIC); if (ret < 0) @@ -140,7 +152,8 @@ static int wl1271_ap_init_deauth_template(struct wl1271 *wl, IEEE80211_STYPE_DEAUTH); rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_DEAUTH_AP, tmpl, sizeof(*tmpl), 0, rate); out: @@ -172,7 +185,8 @@ static int wl1271_ap_init_null_template(struct wl1271 *wl, memcpy(nullfunc->addr3, vif->addr, ETH_ALEN); rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_NULL_DATA, nullfunc, sizeof(*nullfunc), 0, rate); out: @@ -204,7 +218,8 @@ static int wl1271_ap_init_qos_null_template(struct wl1271 *wl, memcpy(qosnull->addr3, vif->addr, ETH_ALEN); rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, + CMD_TEMPL_QOS_NULL_DATA, qosnull, sizeof(*qosnull), 0, rate); out: diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index bba2a52256b6..e4549dfb70d4 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1778,7 +1778,9 @@ static int wl1271_op_start(struct ieee80211_hw *hw) * is added. That is where we will initialize the hardware. */ - return 0; + wl1271_error("wl12xx is in an ustable state (fw api update is " + "taking place). skip this commit when bisecting"); + return -EBUSY; } static void wl1271_op_stop(struct ieee80211_hw *hw) @@ -3297,6 +3299,7 @@ static void wl12xx_remove_vendor_ie(struct sk_buff *skb, static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates, struct ieee80211_vif *vif) { + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct sk_buff *skb; int ret; @@ -3304,7 +3307,7 @@ static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, u32 rates, if (!skb) return -EOPNOTSUPP; - ret = wl1271_cmd_template_set(wl, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_AP_PROBE_RESPONSE, skb->data, skb->len, 0, @@ -3328,7 +3331,7 @@ static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl, /* no need to change probe response if the SSID is set correctly */ if (wlvif->ssid_len > 0) - return wl1271_cmd_template_set(wl, + return wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_AP_PROBE_RESPONSE, probe_rsp_data, probe_rsp_len, 0, @@ -3365,7 +3368,7 @@ static int wl1271_ap_set_probe_resp_tmpl_legacy(struct wl1271 *wl, ptr, probe_rsp_len - (ptr - probe_rsp_data)); templ_len += probe_rsp_len - (ptr - probe_rsp_data); - return wl1271_cmd_template_set(wl, + return wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_AP_PROBE_RESPONSE, probe_rsp_templ, templ_len, 0, @@ -3462,7 +3465,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, min_rate = wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set); tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON : CMD_TEMPL_BEACON; - ret = wl1271_cmd_template_set(wl, tmpl_id, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, tmpl_id, beacon->data, beacon->len, 0, min_rate); @@ -3501,7 +3504,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, beacon->len, min_rate); else - ret = wl1271_cmd_template_set(wl, + ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_PROBE_RESPONSE, beacon->data, beacon->len, 0, diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index e24111ececc5..416ae9145d61 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -217,9 +217,11 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, memcpy(cmd->addr, vif->addr, ETH_ALEN); - ret = wl1271_cmd_build_probe_req(wl, wlvif, wl->scan.ssid, - wl->scan.ssid_len, wl->scan.req->ie, - wl->scan.req->ie_len, band); + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + wlvif->role_id, band, + wl->scan.ssid, wl->scan.ssid_len, + wl->scan.req->ie, + wl->scan.req->ie_len); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; @@ -658,11 +660,13 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, } if (!force_passive && cfg->active[0]) { - ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid, + u8 band = IEEE80211_BAND_2GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + wlvif->role_id, band, + req->ssids[0].ssid, req->ssids[0].ssid_len, - ies->ie[IEEE80211_BAND_2GHZ], - ies->len[IEEE80211_BAND_2GHZ], - IEEE80211_BAND_2GHZ); + ies->ie[band], + ies->len[band]); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); goto out; @@ -670,11 +674,13 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, } if (!force_passive && cfg->active[1]) { - ret = wl1271_cmd_build_probe_req(wl, wlvif, req->ssids[0].ssid, + u8 band = IEEE80211_BAND_5GHZ; + ret = wl12xx_cmd_build_probe_req(wl, wlvif, + wlvif->role_id, band, + req->ssids[0].ssid, req->ssids[0].ssid_len, - ies->ie[IEEE80211_BAND_5GHZ], - ies->len[IEEE80211_BAND_5GHZ], - IEEE80211_BAND_5GHZ); + ies->ie[band], + ies->len[band]); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); goto out; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 1f629fac02e4..b9bfbfffc5c0 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -35,8 +35,8 @@ #include "conf.h" #include "ini.h" -#define WL127X_FW_NAME "ti-connectivity/wl127x-fw-3.bin" -#define WL128X_FW_NAME "ti-connectivity/wl128x-fw-3.bin" +#define WL127X_FW_NAME "ti-connectivity/wl127x-fw-4-sr.bin" +#define WL128X_FW_NAME "ti-connectivity/wl128x-fw-4-sr.bin" /* * wl127x and wl128x are using the same NVS file name. However, the From c059beb2adbf632748ac63ea829c69437575761a Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 11:57:17 +0200 Subject: [PATCH 21/50] wl12xx: use dev_role_id for scans Use device role for scans when the sta is not associated. sched_scan is used only when the sta is not associated, and thus should use the dev role (instead of sta role). Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 3 ++- drivers/net/wireless/wl12xx/main.c | 2 +- drivers/net/wireless/wl12xx/scan.c | 20 ++++++++++++++------ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 28e0a56d794f..10dba19f3eb3 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1029,7 +1029,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, struct wl1271_cmd_template_set *cmd; int ret = 0; - wl1271_debug(DEBUG_CMD, "cmd template_set %d", template_id); + wl1271_debug(DEBUG_CMD, "cmd template_set %d (role %d)", + template_id, role_id); WARN_ON(buf_len > WL1271_CMD_TEMPL_MAX_SIZE); buf_len = min_t(size_t, buf_len, WL1271_CMD_TEMPL_MAX_SIZE); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index e4549dfb70d4..d16ea7a528a2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3070,7 +3070,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, /* cancel ROC before scanning */ if (wl12xx_dev_role_started(wlvif)) - wl12xx_stop_dev(wl, wlvif); + wl12xx_croc(wl, wlvif->dev_role_id); ret = wl1271_scan(hw->priv, vif, ssid, len, req); out_sleep: diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 416ae9145d61..6adc7333b7e6 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -77,7 +77,10 @@ void wl1271_scan_complete_work(struct work_struct *work) (is_ibss && !test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))) && !test_bit(wlvif->dev_role_id, wl->roc_map)) { /* restore remain on channel */ - wl12xx_start_dev(wl, wlvif); + if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) + wl12xx_start_dev(wl, wlvif); + else + wl12xx_roc(wl, wlvif, wlvif->dev_role_id); } wl1271_ps_elp_sleep(wl); @@ -185,11 +188,16 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, if (passive) scan_options |= WL1271_SCAN_OPT_PASSIVE; - if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID)) { + if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID || + wlvif->dev_role_id == WL12XX_INVALID_ROLE_ID)) { ret = -EINVAL; goto out; } - cmd->params.role_id = wlvif->role_id; + if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + cmd->params.role_id = wlvif->role_id; + else + cmd->params.role_id = wlvif->dev_role_id; + cmd->params.scan_options = cpu_to_le16(scan_options); cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, @@ -218,7 +226,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, memcpy(cmd->addr, vif->addr, ETH_ALEN); ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, + cmd->params.role_id, band, wl->scan.ssid, wl->scan.ssid_len, wl->scan.req->ie, wl->scan.req->ie_len); @@ -662,7 +670,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, if (!force_passive && cfg->active[0]) { u8 band = IEEE80211_BAND_2GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, + wlvif->dev_role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, ies->ie[band], @@ -676,7 +684,7 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, if (!force_passive && cfg->active[1]) { u8 band = IEEE80211_BAND_5GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, - wlvif->role_id, band, + wlvif->dev_role_id, band, req->ssids[0].ssid, req->ssids[0].ssid_len, ies->ie[band], From 9c531149af5b6e81d204c3f633827338ad9b327e Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 11:57:18 +0200 Subject: [PATCH 22/50] wl12xx: fw api change - add role_id to tsf_info The ACX_TSF_INFO command now takes role_id as param. change the struct accordingly, and pass the wlvif to the wl1271_acx_tsf_info() function. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 7 +++++-- drivers/net/wireless/wl12xx/acx.h | 9 ++++++--- drivers/net/wireless/wl12xx/main.c | 3 ++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 7537c401a448..af2c3123d1d7 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1459,9 +1459,10 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, return ret; } -int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime) +int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u64 *mactime) { - struct wl1271_acx_fw_tsf_information *tsf_info; + struct wl12xx_acx_fw_tsf_information *tsf_info; int ret; tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL); @@ -1470,6 +1471,8 @@ int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime) goto out; } + tsf_info->role_id = wlvif->role_id; + ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info, sizeof(*tsf_info)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 69892b40c2df..a5bf761c83d2 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -995,15 +995,17 @@ struct wl1271_acx_ba_receiver_setup { u8 padding[2]; } __packed; -struct wl1271_acx_fw_tsf_information { +struct wl12xx_acx_fw_tsf_information { struct acx_header header; + u8 role_id; + u8 padding1[3]; __le32 current_tsf_high; __le32 current_tsf_low; __le32 last_bttt_high; __le32 last_tbtt_low; u8 last_dtim_count; - u8 padding[3]; + u8 padding2[3]; } __packed; struct wl1271_acx_ps_rx_streaming { @@ -1296,7 +1298,8 @@ int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable, u8 peer_hlid); -int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); +int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u64 *mactime); int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable); int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index d16ea7a528a2..1cdffa88eb02 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -4047,6 +4047,7 @@ static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw, { struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); u64 mactime = ULLONG_MAX; int ret; @@ -4061,7 +4062,7 @@ static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw, if (ret < 0) goto out; - ret = wl1271_acx_tsf_info(wl, &mactime); + ret = wl12xx_acx_tsf_info(wl, wlvif, &mactime); if (ret < 0) goto out_sleep; From 8332f0f6dc017aef9aa4a95a6a2bd6eba4c82d0a Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 11:57:19 +0200 Subject: [PATCH 23/50] wl12xx: fw api change - update cmd/acx/event enums Update enums/structs to the new fw api. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.h | 137 ++++++++++++++-------------- drivers/net/wireless/wl12xx/boot.c | 2 +- drivers/net/wireless/wl12xx/cmd.c | 12 ++- drivers/net/wireless/wl12xx/cmd.h | 118 +++++++++++++----------- drivers/net/wireless/wl12xx/event.h | 15 ++- drivers/net/wireless/wl12xx/main.c | 2 +- 6 files changed, 154 insertions(+), 132 deletions(-) diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index a5bf761c83d2..0749df58c85d 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1153,74 +1153,75 @@ struct wl12xx_acx_config_hangover { } __packed; enum { - ACX_WAKE_UP_CONDITIONS = 0x0002, - ACX_MEM_CFG = 0x0003, - ACX_SLOT = 0x0004, - ACX_AC_CFG = 0x0007, - ACX_MEM_MAP = 0x0008, - ACX_AID = 0x000A, - ACX_MEDIUM_USAGE = 0x000F, - ACX_TX_QUEUE_CFG = 0x0011, /* FIXME: only used by wl1251 */ - ACX_STATISTICS = 0x0013, /* Debug API */ - ACX_PWR_CONSUMPTION_STATISTICS = 0x0014, - ACX_FEATURE_CFG = 0x0015, - ACX_TID_CFG = 0x001A, - ACX_PS_RX_STREAMING = 0x001B, - ACX_BEACON_FILTER_OPT = 0x001F, - ACX_NOISE_HIST = 0x0021, - ACX_HDK_VERSION = 0x0022, /* ??? */ - ACX_PD_THRESHOLD = 0x0023, - ACX_TX_CONFIG_OPT = 0x0024, - ACX_CCA_THRESHOLD = 0x0025, - ACX_EVENT_MBOX_MASK = 0x0026, - ACX_CONN_MONIT_PARAMS = 0x002D, - ACX_BCN_DTIM_OPTIONS = 0x0031, - ACX_SG_ENABLE = 0x0032, - ACX_SG_CFG = 0x0033, - ACX_FM_COEX_CFG = 0x0034, - ACX_BEACON_FILTER_TABLE = 0x0038, - ACX_ARP_IP_FILTER = 0x0039, - ACX_ROAMING_STATISTICS_TBL = 0x003B, - ACX_RATE_POLICY = 0x003D, - ACX_CTS_PROTECTION = 0x003E, - ACX_SLEEP_AUTH = 0x003F, - ACX_PREAMBLE_TYPE = 0x0040, - ACX_ERROR_CNT = 0x0041, - ACX_IBSS_FILTER = 0x0044, - ACX_SERVICE_PERIOD_TIMEOUT = 0x0045, - ACX_TSF_INFO = 0x0046, - ACX_CONFIG_PS_WMM = 0x0049, - ACX_ENABLE_RX_DATA_FILTER = 0x004A, - ACX_SET_RX_DATA_FILTER = 0x004B, - ACX_GET_DATA_FILTER_STATISTICS = 0x004C, - ACX_RX_CONFIG_OPT = 0x004E, - ACX_FRAG_CFG = 0x004F, - ACX_BET_ENABLE = 0x0050, - ACX_RSSI_SNR_TRIGGER = 0x0051, - ACX_RSSI_SNR_WEIGHTS = 0x0052, - ACX_KEEP_ALIVE_MODE = 0x0053, - ACX_SET_KEEP_ALIVE_CONFIG = 0x0054, - ACX_BA_SESSION_INIT_POLICY = 0x0055, - ACX_BA_SESSION_RX_SETUP = 0x0056, - ACX_PEER_HT_CAP = 0x0057, - ACX_HT_BSS_OPERATION = 0x0058, - ACX_COEX_ACTIVITY = 0x0059, - ACX_BURST_MODE = 0x005C, - ACX_SET_RATE_MGMT_PARAMS = 0x005D, - ACX_SET_RATE_ADAPT_PARAMS = 0x0060, - ACX_SET_DCO_ITRIM_PARAMS = 0x0061, - ACX_GEN_FW_CMD = 0x0070, - ACX_HOST_IF_CFG_BITMAP = 0x0071, - ACX_MAX_TX_FAILURE = 0x0072, - ACX_UPDATE_INCONNECTION_STA_LIST = 0x0073, - DOT11_RX_MSDU_LIFE_TIME = 0x1004, - DOT11_CUR_TX_PWR = 0x100D, - DOT11_RX_DOT11_MODE = 0x1012, - DOT11_RTS_THRESHOLD = 0x1013, - DOT11_GROUP_ADDRESS_TBL = 0x1014, - ACX_PM_CONFIG = 0x1016, - ACX_CONFIG_PS = 0x1017, - ACX_CONFIG_HANGOVER = 0x1018, + ACX_WAKE_UP_CONDITIONS = 0x0000, + ACX_MEM_CFG = 0x0001, + ACX_SLOT = 0x0002, + ACX_AC_CFG = 0x0003, + ACX_MEM_MAP = 0x0004, + ACX_AID = 0x0005, + ACX_MEDIUM_USAGE = 0x0006, + ACX_STATISTICS = 0x0007, + ACX_PWR_CONSUMPTION_STATISTICS = 0x0008, + ACX_TID_CFG = 0x0009, + ACX_PS_RX_STREAMING = 0x000A, + ACX_BEACON_FILTER_OPT = 0x000B, + ACX_NOISE_HIST = 0x000C, + ACX_HDK_VERSION = 0x000D, + ACX_PD_THRESHOLD = 0x000E, + ACX_TX_CONFIG_OPT = 0x000F, + ACX_CCA_THRESHOLD = 0x0010, + ACX_EVENT_MBOX_MASK = 0x0011, + ACX_CONN_MONIT_PARAMS = 0x0012, + ACX_DISABLE_BROADCASTS = 0x0013, + ACX_BCN_DTIM_OPTIONS = 0x0014, + ACX_SG_ENABLE = 0x0015, + ACX_SG_CFG = 0x0016, + ACX_FM_COEX_CFG = 0x0017, + ACX_BEACON_FILTER_TABLE = 0x0018, + ACX_ARP_IP_FILTER = 0x0019, + ACX_ROAMING_STATISTICS_TBL = 0x001A, + ACX_RATE_POLICY = 0x001B, + ACX_CTS_PROTECTION = 0x001C, + ACX_SLEEP_AUTH = 0x001D, + ACX_PREAMBLE_TYPE = 0x001E, + ACX_ERROR_CNT = 0x001F, + ACX_IBSS_FILTER = 0x0020, + ACX_SERVICE_PERIOD_TIMEOUT = 0x0021, + ACX_TSF_INFO = 0x0022, + ACX_CONFIG_PS_WMM = 0x0023, + ACX_ENABLE_RX_DATA_FILTER = 0x0024, + ACX_SET_RX_DATA_FILTER = 0x0025, + ACX_GET_DATA_FILTER_STATISTICS = 0x0026, + ACX_RX_CONFIG_OPT = 0x0027, + ACX_FRAG_CFG = 0x0028, + ACX_BET_ENABLE = 0x0029, + ACX_RSSI_SNR_TRIGGER = 0x002A, + ACX_RSSI_SNR_WEIGHTS = 0x002B, + ACX_KEEP_ALIVE_MODE = 0x002C, + ACX_SET_KEEP_ALIVE_CONFIG = 0x002D, + ACX_BA_SESSION_INIT_POLICY = 0x002E, + ACX_BA_SESSION_RX_SETUP = 0x002F, + ACX_PEER_HT_CAP = 0x0030, + ACX_HT_BSS_OPERATION = 0x0031, + ACX_COEX_ACTIVITY = 0x0032, + ACX_BURST_MODE = 0x0033, + ACX_SET_RATE_MGMT_PARAMS = 0x0034, + ACX_GET_RATE_MGMT_PARAMS = 0x0035, + ACX_SET_RATE_ADAPT_PARAMS = 0x0036, + ACX_SET_DCO_ITRIM_PARAMS = 0x0037, + ACX_GEN_FW_CMD = 0x0038, + ACX_HOST_IF_CFG_BITMAP = 0x0039, + ACX_MAX_TX_FAILURE = 0x003A, + ACX_UPDATE_INCONNECTION_STA_LIST = 0x003B, + DOT11_RX_MSDU_LIFE_TIME = 0x003C, + DOT11_CUR_TX_PWR = 0x003D, + DOT11_RTS_THRESHOLD = 0x003E, + DOT11_GROUP_ADDRESS_TBL = 0x003F, + ACX_PM_CONFIG = 0x0040, + ACX_CONFIG_PS = 0x0041, + ACX_CONFIG_HANGOVER = 0x0042, + ACX_FEATURE_CFG = 0x0043, + ACX_PROTECTION_CFG = 0x0044, }; diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 32a7f9b69d7c..eea2dda6e6e7 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -449,7 +449,7 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) wl->event_mask = BSS_LOSE_EVENT_ID | SCAN_COMPLETE_EVENT_ID | PS_REPORT_EVENT_ID | - DISCONNECT_EVENT_COMPLETE_ID | + ROLE_STOP_COMPLETE_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID | SOFT_GEMINI_SENSE_EVENT_ID | diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 10dba19f3eb3..88e94c5938a4 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -566,7 +566,7 @@ static int wl12xx_cmd_role_stop_dev(struct wl1271 *wl, goto out_free; } - ret = wl1271_cmd_wait_for_event(wl, DISCONNECT_EVENT_COMPLETE_ID); + ret = wl1271_cmd_wait_for_event(wl, ROLE_STOP_COMPLETE_EVENT_ID); if (ret < 0) { wl1271_error("cmd role stop dev event completion error"); goto out_free; @@ -715,6 +715,8 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) cmd->ap.beacon_interval = cpu_to_le16(wlvif->beacon_int); cmd->ap.dtim_interval = bss_conf->dtim_period; cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP; + /* FIXME: Change when adding DFS */ + cmd->ap.reset_tsf = 1; /* By default reset AP TSF */ cmd->channel = wlvif->channel; if (!bss_conf->hidden_ssid) { @@ -1756,6 +1758,7 @@ int wl12xx_croc(struct wl1271 *wl, u8 role_id) } int wl12xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch) { struct wl12xx_cmd_channel_switch *cmd; @@ -1769,10 +1772,13 @@ int wl12xx_cmd_channel_switch(struct wl1271 *wl, goto out; } + cmd->role_id = wlvif->role_id; cmd->channel = ch_switch->channel->hw_value; cmd->switch_time = ch_switch->count; - cmd->tx_suspend = ch_switch->block_tx; - cmd->flush = 0; /* this value is ignored by the FW */ + cmd->stop_tx = ch_switch->block_tx; + + /* FIXME: control from mac80211 in the future */ + cmd->post_switch_tx_disable = 0; /* Enable TX on the target channel */ ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 8e8035d33068..edd240db0dcc 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -91,6 +91,7 @@ int wl12xx_cmd_config_fwlog(struct wl1271 *wl); int wl12xx_cmd_start_fwlog(struct wl1271 *wl); int wl12xx_cmd_stop_fwlog(struct wl1271 *wl); int wl12xx_cmd_channel_switch(struct wl1271 *wl, + struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl); int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -98,62 +99,65 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); enum wl1271_commands { - CMD_INTERROGATE = 1, /*use this to read information elements*/ - CMD_CONFIGURE = 2, /*use this to write information elements*/ - CMD_ENABLE_RX = 3, - CMD_ENABLE_TX = 4, - CMD_DISABLE_RX = 5, - CMD_DISABLE_TX = 6, - CMD_SCAN = 8, - CMD_STOP_SCAN = 9, - CMD_SET_KEYS = 12, - CMD_READ_MEMORY = 13, - CMD_WRITE_MEMORY = 14, - CMD_SET_TEMPLATE = 19, - CMD_TEST = 23, - CMD_NOISE_HIST = 28, - CMD_QUIET_ELEMENT_SET_STATE = 29, - CMD_SET_BCN_MODE = 33, - CMD_MEASUREMENT = 34, - CMD_STOP_MEASUREMENT = 35, - CMD_SET_PS_MODE = 37, - CMD_CHANNEL_SWITCH = 38, - CMD_STOP_CHANNEL_SWICTH = 39, - CMD_AP_DISCOVERY = 40, - CMD_STOP_AP_DISCOVERY = 41, - CMD_HEALTH_CHECK = 45, - CMD_DEBUG = 46, - CMD_TRIGGER_SCAN_TO = 47, - CMD_CONNECTION_SCAN_CFG = 48, - CMD_CONNECTION_SCAN_SSID_CFG = 49, - CMD_START_PERIODIC_SCAN = 50, - CMD_STOP_PERIODIC_SCAN = 51, - CMD_SET_PEER_STATE = 52, - CMD_REMAIN_ON_CHANNEL = 53, - CMD_CANCEL_REMAIN_ON_CHANNEL = 54, + CMD_INTERROGATE = 1, /* use this to read information elements */ + CMD_CONFIGURE = 2, /* use this to write information elements */ + CMD_ENABLE_RX = 3, + CMD_ENABLE_TX = 4, + CMD_DISABLE_RX = 5, + CMD_DISABLE_TX = 6, + CMD_SCAN = 7, + CMD_STOP_SCAN = 8, + CMD_SET_KEYS = 9, + CMD_READ_MEMORY = 10, + CMD_WRITE_MEMORY = 11, + CMD_SET_TEMPLATE = 12, + CMD_TEST = 13, + CMD_NOISE_HIST = 14, + CMD_QUIET_ELEMENT_SET_STATE = 15, + CMD_SET_BCN_MODE = 16, - CMD_CONFIG_FWLOGGER = 55, - CMD_START_FWLOGGER = 56, - CMD_STOP_FWLOGGER = 57, + CMD_MEASUREMENT = 17, + CMD_STOP_MEASUREMENT = 18, + CMD_SET_PS_MODE = 19, + CMD_CHANNEL_SWITCH = 20, + CMD_STOP_CHANNEL_SWICTH = 21, + CMD_AP_DISCOVERY = 22, + CMD_STOP_AP_DISCOVERY = 23, + CMD_HEALTH_CHECK = 24, + CMD_DEBUG = 25, + CMD_TRIGGER_SCAN_TO = 26, + CMD_CONNECTION_SCAN_CFG = 27, + CMD_CONNECTION_SCAN_SSID_CFG = 28, + CMD_START_PERIODIC_SCAN = 29, + CMD_STOP_PERIODIC_SCAN = 30, + CMD_SET_PEER_STATE = 31, + CMD_REMAIN_ON_CHANNEL = 32, + CMD_CANCEL_REMAIN_ON_CHANNEL = 33, + CMD_CONFIG_FWLOGGER = 34, + CMD_START_FWLOGGER = 35, + CMD_STOP_FWLOGGER = 36, - /* AP commands */ - CMD_ADD_PEER = 62, - CMD_REMOVE_PEER = 63, + /* Access point commands */ + CMD_ADD_PEER = 37, + CMD_REMOVE_PEER = 38, /* Role API */ - CMD_ROLE_ENABLE = 70, - CMD_ROLE_DISABLE = 71, - CMD_ROLE_START = 72, - CMD_ROLE_STOP = 73, + CMD_ROLE_ENABLE = 39, + CMD_ROLE_DISABLE = 40, + CMD_ROLE_START = 41, + CMD_ROLE_STOP = 42, + + /* DFS */ + CMD_START_RADAR_DETECTION = 43, + CMD_STOP_RADAR_DETECTION = 44, /* WIFI Direct */ - CMD_WFD_START_DISCOVERY = 80, - CMD_WFD_STOP_DISCOVERY = 81, - CMD_WFD_ATTRIBUTE_CONFIG = 82, + CMD_WFD_START_DISCOVERY = 45, + CMD_WFD_STOP_DISCOVERY = 46, + CMD_WFD_ATTRIBUTE_CONFIG = 47, + CMD_NOP = 48, + CMD_LAST_COMMAND, - CMD_NOP = 100, - - NUM_COMMANDS, MAX_COMMAND_ID = 0xFFFF, }; @@ -341,7 +345,9 @@ struct wl12xx_cmd_role_start { u8 ssid_len; u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 padding_1[5]; + u8 reset_tsf; + + u8 padding_1[4]; } __packed ap; }; } __packed; @@ -701,14 +707,18 @@ struct wl12xx_cmd_stop_fwlog { struct wl12xx_cmd_channel_switch { struct wl1271_cmd_header header; + u8 role_id; + /* The new serving channel */ u8 channel; /* Relative time of the serving channel switch in TBTT units */ u8 switch_time; - /* 1: Suspend TX till switch time; 0: Do not suspend TX */ - u8 tx_suspend; - /* 1: Flush TX at switch time; 0: Do not flush */ - u8 flush; + /* Stop the role TX, should expect it after radar detection */ + u8 stop_tx; + /* The target channel tx status 1-stopped 0-open*/ + u8 post_switch_tx_disable; + + u8 padding[3]; } __packed; struct wl12xx_cmd_stop_channel_switch { diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index 1d878ba47bf4..b41f730f11dd 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -53,8 +53,8 @@ enum { AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), PS_REPORT_EVENT_ID = BIT(13), PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), - DISCONNECT_EVENT_COMPLETE_ID = BIT(15), - /* BIT(16) is reserved */ + ROLE_STOP_COMPLETE_EVENT_ID = BIT(15), + RADAR_DETECTED_EVENT_ID = BIT(16), CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), BSS_LOSE_EVENT_ID = BIT(18), REGAINED_BSS_EVENT_ID = BIT(19), @@ -94,7 +94,7 @@ struct event_mailbox { u8 soft_gemini_sense_info; u8 soft_gemini_protective_info; s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; - u8 channel_switch_status; + u8 change_auto_mode_timeout; u8 scheduled_scan_status; u8 ps_status; /* tuned channel (roc) */ @@ -119,12 +119,17 @@ struct event_mailbox { u8 rx_ba_allowed; u8 reserved_6[2]; + /* Channel switch results */ + + u8 channel_switch_role_id; + u8 channel_switch_status; + u8 reserved_7[2]; + u8 ps_poll_delivery_failure_role_ids; u8 stopped_role_ids; u8 started_role_ids; - u8 change_auto_mode_timeout; - u8 reserved_7[12]; + u8 reserved_8[9]; } __packed; int wl1271_event_unmask(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 1cdffa88eb02..a339a85ecfb0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -4409,7 +4409,7 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, /* TODO: change mac80211 to pass vif as param */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - ret = wl12xx_cmd_channel_switch(wl, ch_switch); + ret = wl12xx_cmd_channel_switch(wl, wlvif, ch_switch); if (!ret) set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); From d6bf9ada92c113e56151b6a993b9b9d5d03f1365 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:20 +0200 Subject: [PATCH 24/50] wl12xx: remove PS management code Removal of PS management code from the driver as PS is handled by the FW (dynamic PS) Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 1 - drivers/net/wireless/wl12xx/conf.h | 7 -- drivers/net/wireless/wl12xx/debugfs.c | 1 - drivers/net/wireless/wl12xx/event.c | 143 -------------------------- drivers/net/wireless/wl12xx/event.h | 5 +- drivers/net/wireless/wl12xx/main.c | 81 +-------------- drivers/net/wireless/wl12xx/wl12xx.h | 6 -- 7 files changed, 5 insertions(+), 239 deletions(-) diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index eea2dda6e6e7..954101d03f06 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -448,7 +448,6 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) /* unmask required mbox events */ wl->event_mask = BSS_LOSE_EVENT_ID | SCAN_COMPLETE_EVENT_ID | - PS_REPORT_EVENT_ID | ROLE_STOP_COMPLETE_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PSPOLL_DELIVERY_FAILURE_EVENT_ID | diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 1bcfb017058d..47cf80f0b036 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -867,13 +867,6 @@ struct conf_conn_settings { */ u8 ps_poll_threshold; - /* - * PS Poll failure recovery ACTIVE period length - * - * Range: u32 (ms) - */ - u32 ps_poll_recovery_period; - /* * Configuration of signal average weights. */ diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 15eb3a9c30ca..5e96e059f2b5 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -471,7 +471,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(default_key); VIF_STATE_PRINT_INT(aid); VIF_STATE_PRINT_INT(session_counter); - VIF_STATE_PRINT_INT(ps_poll_failures); VIF_STATE_PRINT_INT(psm_entry_retry); VIF_STATE_PRINT_INT(power_level); VIF_STATE_PRINT_INT(rssi_thold); diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index cfc38ea21e4a..05cd2ce927c6 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -30,133 +30,6 @@ #include "scan.h" #include "wl12xx_80211.h" -void wl1271_pspoll_work(struct work_struct *work) -{ - struct ieee80211_vif *vif; - struct wl12xx_vif *wlvif; - struct delayed_work *dwork; - struct wl1271 *wl; - int ret; - - dwork = container_of(work, struct delayed_work, work); - wlvif = container_of(dwork, struct wl12xx_vif, pspoll_work); - vif = container_of((void *)wlvif, struct ieee80211_vif, drv_priv); - wl = wlvif->wl; - - wl1271_debug(DEBUG_EVENT, "pspoll work"); - - mutex_lock(&wl->mutex); - - if (unlikely(wl->state == WL1271_STATE_OFF)) - goto out; - - if (!test_and_clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags)) - goto out; - - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out; - - /* - * if we end up here, then we were in powersave when the pspoll - * delivery failure occurred, and no-one changed state since, so - * we should go back to powersave. - */ - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - - wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE, - wlvif->basic_rate, true); - - wl1271_ps_elp_sleep(wl); -out: - mutex_unlock(&wl->mutex); -}; - -static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl, - struct wl12xx_vif *wlvif) -{ - int delay = wl->conf.conn.ps_poll_recovery_period; - int ret; - - wlvif->ps_poll_failures++; - if (wlvif->ps_poll_failures == 1) - wl1271_info("AP with dysfunctional ps-poll, " - "trying to work around it."); - - /* force active mode receive data from the AP */ - if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) { - ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE, - wlvif->basic_rate, true); - if (ret < 0) - return; - set_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags); - ieee80211_queue_delayed_work(wl->hw, &wlvif->pspoll_work, - msecs_to_jiffies(delay)); - } - - /* - * If already in active mode, lets we should be getting data from - * the AP right away. If we enter PSM too fast after this, and data - * remains on the AP, we will get another event like this, and we'll - * go into active once more. - */ -} - -static int wl1271_event_ps_report(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct event_mailbox *mbox, - bool *beacon_loss) -{ - int ret = 0; - u32 total_retries = wl->conf.conn.psm_entry_retries; - - wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); - - switch (mbox->ps_status) { - case EVENT_ENTER_POWER_SAVE_FAIL: - wl1271_debug(DEBUG_PSM, "PSM entry failed"); - - if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) { - /* remain in active mode */ - wlvif->psm_entry_retry = 0; - break; - } - - if (wlvif->psm_entry_retry < total_retries) { - wlvif->psm_entry_retry++; - ret = wl1271_ps_set_mode(wl, wlvif, - STATION_POWER_SAVE_MODE, - wlvif->basic_rate, true); - } else { - wl1271_info("No ack to nullfunc from AP."); - wlvif->psm_entry_retry = 0; - *beacon_loss = true; - } - break; - case EVENT_ENTER_POWER_SAVE_SUCCESS: - wlvif->psm_entry_retry = 0; - - /* - * BET has only a minor effect in 5GHz and masks - * channel switch IEs, so we only enable BET on 2.4GHz - */ - if (wlvif->band == IEEE80211_BAND_2GHZ) - /* enable beacon early termination */ - ret = wl1271_acx_bet_enable(wl, wlvif, true); - - if (wlvif->ps_compl) { - complete(wlvif->ps_compl); - wlvif->ps_compl = NULL; - } - break; - default: - break; - } - - return ret; -} - static void wl1271_event_rssi_trigger(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct event_mailbox *mbox) @@ -237,7 +110,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; - int ret; u32 vector; bool beacon_loss = false; bool disconnect_sta = false; @@ -293,21 +165,6 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) beacon_loss = true; } - if (vector & PS_REPORT_EVENT_ID) { - wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); - wl12xx_for_each_wlvif_sta(wl, wlvif) { - ret = wl1271_event_ps_report(wl, wlvif, - mbox, &beacon_loss); - if (ret < 0) - return ret; - } - } - - if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) - wl12xx_for_each_wlvif_sta(wl, wlvif) { - wl1271_event_pspoll_delivery_fail(wl, wlvif); - } - if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { /* TODO: check actual multi-role support */ wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h index b41f730f11dd..057d193d3525 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/event.h @@ -51,7 +51,7 @@ enum { SCAN_COMPLETE_EVENT_ID = BIT(10), WFD_DISCOVERY_COMPLETE_EVENT_ID = BIT(11), AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), - PS_REPORT_EVENT_ID = BIT(13), + RESERVED1 = BIT(13), PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), ROLE_STOP_COMPLETE_EVENT_ID = BIT(15), RADAR_DETECTED_EVENT_ID = BIT(16), @@ -96,7 +96,7 @@ struct event_mailbox { s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; u8 change_auto_mode_timeout; u8 scheduled_scan_status; - u8 ps_status; + u8 reserved4; /* tuned channel (roc) */ u8 roc_channel; @@ -135,6 +135,5 @@ struct event_mailbox { int wl1271_event_unmask(struct wl1271 *wl); 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); #endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a339a85ecfb0..74d4abb25ab1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -235,7 +235,6 @@ static struct conf_drv_settings default_conf = { .broadcast_timeout = 20000, .rx_broadcast_in_ps = 1, .ps_poll_threshold = 10, - .ps_poll_recovery_period = 700, .bet_enable = CONF_BET_MODE_ENABLE, .bet_max_consecutive = 50, .psm_entry_retries = 8, @@ -1570,57 +1569,6 @@ static struct notifier_block wl1271_dev_notifier = { }; #ifdef CONFIG_PM -static int wl1271_configure_suspend_sta(struct wl1271 *wl, - struct wl12xx_vif *wlvif) -{ - int ret = 0; - - mutex_lock(&wl->mutex); - - if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out_unlock; - - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out_unlock; - - /* enter psm if needed*/ - if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) { - DECLARE_COMPLETION_ONSTACK(compl); - - wlvif->ps_compl = &compl; - ret = wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE, - wlvif->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)); - - mutex_lock(&wl->mutex); - if (ret <= 0) { - wl1271_warning("couldn't enter ps mode!"); - ret = -EBUSY; - goto out_cleanup; - } - - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out_cleanup; - } -out_sleep: - wl1271_ps_elp_sleep(wl); -out_cleanup: - wlvif->ps_compl = NULL; -out_unlock: - mutex_unlock(&wl->mutex); - return ret; - -} static int wl1271_configure_suspend_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) @@ -1648,8 +1596,6 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl, static int wl1271_configure_suspend(struct wl1271 *wl, struct wl12xx_vif *wlvif) { - if (wlvif->bss_type == BSS_TYPE_STA_BSS) - return wl1271_configure_suspend_sta(wl, wlvif); if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl1271_configure_suspend_ap(wl, wlvif); return 0; @@ -1659,10 +1605,9 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; - bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; - if (!is_sta && !is_ap) + if (!is_ap) return; mutex_lock(&wl->mutex); @@ -1670,14 +1615,7 @@ static void wl1271_configure_resume(struct wl1271 *wl, if (ret < 0) goto out; - if (is_sta) { - /* exit psm if it wasn't configured */ - if (!test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) - wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE, - wlvif->basic_rate, true); - } else if (is_ap) { - wl1271_acx_beacon_filter_opt(wl, wlvif, false); - } + wl1271_acx_beacon_filter_opt(wl, wlvif, false); wl1271_ps_elp_sleep(wl); out: @@ -1719,9 +1657,6 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, wl1271_enable_interrupts(wl); flush_work(&wl->tx_work); - wl12xx_for_each_wlvif(wl, wlvif) { - flush_delayed_work(&wlvif->pspoll_work); - } flush_delayed_work(&wl->elp_work); return 0; @@ -1994,7 +1929,6 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wl1271_rx_streaming_enable_work); INIT_WORK(&wlvif->rx_streaming_disable_work, wl1271_rx_streaming_disable_work); - INIT_DELAYED_WORK(&wlvif->pspoll_work, wl1271_pspoll_work); INIT_LIST_HEAD(&wlvif->list); setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer, @@ -2278,10 +2212,10 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl->sta_count--; mutex_unlock(&wl->mutex); + del_timer_sync(&wlvif->rx_streaming_timer); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); - cancel_delayed_work_sync(&wlvif->pspoll_work); mutex_lock(&wl->mutex); } @@ -2528,13 +2462,6 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, } } - /* - * if mac80211 changes the PSM mode, make sure the mode is not - * incorrectly changed after the pspoll failure active window. - */ - if (changed & IEEE80211_CONF_CHANGE_PS) - clear_bit(WLVIF_FLAG_PSPOLL_FAILURE, &wlvif->flags); - if (conf->flags & IEEE80211_CONF_PS && !test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) { set_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags); @@ -3708,8 +3635,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, wlvif->aid = bss_conf->aid; set_assoc = true; - wlvif->ps_poll_failures = 0; - /* * use basic rates from AP, and determine lowest rate * to use with control frames. diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index b9bfbfffc5c0..e18e6160fdab 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -565,12 +565,6 @@ struct wl12xx_vif { /* Session counter for the chipset */ int session_counter; - struct completion *ps_compl; - struct delayed_work pspoll_work; - - /* counter for ps-poll delivery failures */ - int ps_poll_failures; - /* retry counter for PSM entries */ u8 psm_entry_retry; From f1d63a59635feef3481ed1972a883a5d6be7f9bb Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:21 +0200 Subject: [PATCH 25/50] wl12xx: add support for HW dynamic PS FW now supports dynamic PS so we don't need to use mac80211 support. FW will go to PSM after a specified timeout with no Rx/Tx traffic. - Changed FW API to include new PS mode (AUTO_MODE) and including timeout parameter - The default PS mode would be dynamic PS - Default timeout is 100ms (same as it used to be in mac80211) - Avoid using mac80211 APIs to disable/enable dynamic PS as we're not using mac80211 PS control anymore. - COEX is handled by the FW while in dynamic PS so removed handling of SOFT_GEMINI Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 3 ++- drivers/net/wireless/wl12xx/cmd.h | 5 +++-- drivers/net/wireless/wl12xx/conf.h | 6 ++++++ drivers/net/wireless/wl12xx/event.c | 8 -------- drivers/net/wireless/wl12xx/main.c | 15 +++++---------- drivers/net/wireless/wl12xx/ps.c | 15 ++++++++++----- 6 files changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 88e94c5938a4..bf3c37bd1f12 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -996,7 +996,7 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable) } int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 ps_mode) + u8 ps_mode, u16 auto_ps_timeout) { struct wl1271_cmd_ps_params *ps_params = NULL; int ret = 0; @@ -1011,6 +1011,7 @@ int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, ps_params->role_id = wlvif->role_id; ps_params->ps_mode = ps_mode; + ps_params->auto_ps_timeout = auto_ps_timeout; ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, sizeof(*ps_params), 0); diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index edd240db0dcc..83a2a2edb261 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -51,7 +51,7 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_ps_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, - u8 ps_mode); + u8 ps_mode, u16 auto_ps_timeout); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); int wl1271_cmd_template_set(struct wl1271 *wl, u8 role_id, @@ -400,6 +400,7 @@ struct wl1271_tim { } __packed; enum wl1271_cmd_ps_mode { + STATION_AUTO_PS_MODE, /* Dynamic Power Save */ STATION_ACTIVE_MODE, STATION_POWER_SAVE_MODE }; @@ -409,7 +410,7 @@ struct wl1271_cmd_ps_params { u8 role_id; u8 ps_mode; /* STATION_* */ - u8 padding[2]; + u16 auto_ps_timeout; } __packed; /* HW encryption keys */ diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 47cf80f0b036..10e5e3df4b95 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -914,6 +914,12 @@ struct conf_conn_settings { */ u8 psm_entry_nullfunc_retries; + /* + * Specifies the dynamic PS timeout in ms that will be used + * by the FW when in AUTO_PS mode + */ + u16 dynamic_ps_timeout; + /* * * Specifies the interval of the connection keep-alive null-func diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 05cd2ce927c6..c953717f38eb 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -78,21 +78,13 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, u8 enable) { - struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; if (enable) { - /* disable dynamic PS when requested by the firmware */ - wl12xx_for_each_wlvif_sta(wl, wlvif) { - vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_disable_dyn_ps(vif); - } set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); } else { clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); wl12xx_for_each_wlvif_sta(wl, wlvif) { - vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_enable_dyn_ps(vif); wl1271_recalc_rx_streaming(wl, wlvif); } } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 74d4abb25ab1..6b240868ac75 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1,3 +1,4 @@ + /* * This file is part of wl1271 * @@ -240,6 +241,7 @@ static struct conf_drv_settings default_conf = { .psm_entry_retries = 8, .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, + .dynamic_ps_timeout = 100, .keep_alive_interval = 55000, .max_listen_interval = 20, }, @@ -2142,10 +2144,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, wl1271_info("down"); - /* enable dyn ps just in case (if left on due to fw crash etc) */ - if (wlvif->bss_type == BSS_TYPE_STA_BSS) - ieee80211_enable_dyn_ps(vif); - if (wl->scan.state != WL1271_SCAN_STATE_IDLE && wl->scan_vif == vif) { wl->scan.state = WL1271_SCAN_STATE_IDLE; @@ -3694,9 +3692,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, dev_kfree_skb(wlvif->probereq); wlvif->probereq = NULL; - /* re-enable dynamic ps - just in case */ - ieee80211_enable_dyn_ps(vif); - /* revert back to minimum rates for the current band */ wl1271_set_band_rate(wl, wlvif); wlvif->basic_rate = @@ -3827,10 +3822,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, /* If we want to go in PSM but we're not there yet */ if (test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags) && !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) { - enum wl1271_cmd_ps_mode mode; - mode = STATION_POWER_SAVE_MODE; - ret = wl1271_ps_set_mode(wl, wlvif, mode, + ret = wl1271_ps_set_mode(wl, wlvif, + STATION_AUTO_PS_MODE, wlvif->basic_rate, true); if (ret < 0) @@ -4976,6 +4970,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index a2bdacdd7e1d..60f03c4dfbe7 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -163,10 +163,12 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum wl1271_cmd_ps_mode mode, u32 rates, bool send) { int ret; + u16 timeout = wl->conf.conn.dynamic_ps_timeout; switch (mode) { - case STATION_POWER_SAVE_MODE: - wl1271_debug(DEBUG_PSM, "entering psm"); + case STATION_AUTO_PS_MODE: + wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", + mode, timeout); ret = wl1271_acx_wake_up_conditions(wl, wlvif); if (ret < 0) { @@ -174,14 +176,13 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; } - ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_POWER_SAVE_MODE); + ret = wl1271_cmd_ps_mode(wl, wlvif, mode, timeout); if (ret < 0) return ret; set_bit(WLVIF_FLAG_PSM, &wlvif->flags); break; case STATION_ACTIVE_MODE: - default: wl1271_debug(DEBUG_PSM, "leaving psm"); /* disable beacon early termination */ @@ -191,12 +192,16 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; } - ret = wl1271_cmd_ps_mode(wl, wlvif, STATION_ACTIVE_MODE); + ret = wl1271_cmd_ps_mode(wl, wlvif, mode, 0); if (ret < 0) return ret; clear_bit(WLVIF_FLAG_PSM, &wlvif->flags); break; + case STATION_POWER_SAVE_MODE: + default: + wl1271_warning("trying to set ps to unsupported mode %d", mode); + ret = -EINVAL; } return ret; From 1faff895df6a5213ee16a241951dc79c7ec5b5cb Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:22 +0200 Subject: [PATCH 26/50] wl12xx: add dynamic_ps_timeout debugfs file Enable read/write of dynamic_ps_timeout which controls the timeout of the dynamic PS implemented in the FW. dynamic_ps_timeout is the timeout (in msec) until going back to PS when there's no Rx/Tx Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 5e96e059f2b5..156a7741df34 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -312,6 +312,72 @@ static const struct file_operations start_recovery_ops = { .llseek = default_llseek, }; +static ssize_t dynamic_ps_timeout_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + return wl1271_format_buffer(user_buf, count, + ppos, "%d\n", + wl->conf.conn.dynamic_ps_timeout); +} + +static ssize_t dynamic_ps_timeout_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + struct wl12xx_vif *wlvif; + unsigned long value; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 10, &value); + if (ret < 0) { + wl1271_warning("illegal value in dynamic_ps"); + return -EINVAL; + } + + if (value < 1 || value > 65535) { + wl1271_warning("dyanmic_ps_timeout is not in valid range"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + wl->conf.conn.dynamic_ps_timeout = value; + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* In case we're already in PSM, trigger it again to set new timeout + * immediately without waiting for re-association + */ + + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) + wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE, + wlvif->basic_rate, true); + + } + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations dynamic_ps_timeout_ops = { + .read = dynamic_ps_timeout_read, + .write = dynamic_ps_timeout_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static ssize_t driver_state_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -887,6 +953,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(dtim_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir); DEBUGFS_ADD(beacon_filtering, rootdir); + DEBUGFS_ADD(dynamic_ps_timeout, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) From 248a0018f365c1fa51aa381e7b6f6e97eacf61ff Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:23 +0200 Subject: [PATCH 27/50] wl12xx: remove 2 unused parameters in wl1271_ps_set_mode() cleanup 2 unused parameters of wl1271_ps_set_mode Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 4 +--- drivers/net/wireless/wl12xx/main.c | 10 +++------- drivers/net/wireless/wl12xx/ps.c | 2 +- drivers/net/wireless/wl12xx/ps.h | 2 +- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 156a7741df34..4436851a4fa7 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -359,9 +359,7 @@ static ssize_t dynamic_ps_timeout_write(struct file *file, wl12xx_for_each_wlvif_sta(wl, wlvif) { if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) - wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE, - wlvif->basic_rate, true); - + wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); } wl1271_ps_elp_sleep(wl); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 6b240868ac75..97f7591c3b39 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2472,8 +2472,7 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { wl1271_debug(DEBUG_PSM, "psm enabled"); ret = wl1271_ps_set_mode(wl, wlvif, - STATION_POWER_SAVE_MODE, - wlvif->basic_rate, true); + STATION_AUTO_PS_MODE); } } else if (!(conf->flags & IEEE80211_CONF_PS) && test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) { @@ -2483,8 +2482,7 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) ret = wl1271_ps_set_mode(wl, wlvif, - STATION_ACTIVE_MODE, - wlvif->basic_rate, true); + STATION_ACTIVE_MODE); } if (conf->power_level != wlvif->power_level) { @@ -3824,9 +3822,7 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) { ret = wl1271_ps_set_mode(wl, wlvif, - STATION_AUTO_PS_MODE, - wlvif->basic_rate, - true); + STATION_AUTO_PS_MODE); if (ret < 0) goto out; } diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 60f03c4dfbe7..5074c2af17c0 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -160,7 +160,7 @@ int wl1271_ps_elp_wakeup(struct wl1271 *wl) } int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, - enum wl1271_cmd_ps_mode mode, u32 rates, bool send) + enum wl1271_cmd_ps_mode mode) { int ret; u16 timeout = wl->conf.conn.dynamic_ps_timeout; diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h index a12052f02026..5f19d4fbbf27 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/ps.h @@ -28,7 +28,7 @@ #include "acx.h" int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, - enum wl1271_cmd_ps_mode mode, u32 rates, bool send); + enum wl1271_cmd_ps_mode mode); void wl1271_ps_elp_sleep(struct wl1271 *wl); int wl1271_ps_elp_wakeup(struct wl1271 *wl); void wl1271_elp_work(struct work_struct *work); From ed471d3402b0fa77e52007c6f8d79b16c4194000 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:24 +0200 Subject: [PATCH 28/50] wl12xx: enable/disable BET with AUTO_PS/ACTIVE While the FW with dynamic PS controls BET when going to PSM and back internally within the FW, there's still a need to enable it from the driver at least once (so enable on every entry to AUTO_PS) and disable it once we explicitly go back to STATION_ACTIVE_MODE. BET isn't relevant for 5GHz Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/ps.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 5074c2af17c0..d7a91d3c398d 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -181,6 +181,13 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, return ret; set_bit(WLVIF_FLAG_PSM, &wlvif->flags); + + /* enable beacon early termination. Not relevant for 5GHz */ + if (wlvif->band == IEEE80211_BAND_2GHZ) { + ret = wl1271_acx_bet_enable(wl, wlvif, true); + if (ret < 0) + return ret; + } break; case STATION_ACTIVE_MODE: wl1271_debug(DEBUG_PSM, "leaving psm"); From d18da7fcca449f09c91a209b4f5006959c5a7656 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Tue, 31 Jan 2012 11:57:25 +0200 Subject: [PATCH 29/50] wl12xx: change WLVIF_FLAG_PSM name and remove WLVIF_FLAG_PSM_REQUESTED WLVIF_FLAG_PSM turned to WLVIF_FLAG_IN_AUTO_PS which marks that this vif is in AUTO PS. WLVIF_FLAG_PSM_REQUESTED is not required as mac80211 calls op_config with CONF_PS after association. wl12xx_config_vif() handling of CONF_PS was simplified and cleaned up. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 2 +- drivers/net/wireless/wl12xx/main.c | 46 ++++++++++----------------- drivers/net/wireless/wl12xx/ps.c | 8 ++--- drivers/net/wireless/wl12xx/wl12xx.h | 3 +- 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 4436851a4fa7..4dcb3f758fe2 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -358,7 +358,7 @@ static ssize_t dynamic_ps_timeout_write(struct file *file, */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) + if (test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 97f7591c3b39..4ca7278201ec 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1715,9 +1715,7 @@ static int wl1271_op_start(struct ieee80211_hw *hw) * is added. That is where we will initialize the hardware. */ - wl1271_error("wl12xx is in an ustable state (fw api update is " - "taking place). skip this commit when bisecting"); - return -EBUSY; + return 0; } static void wl1271_op_stop(struct ieee80211_hw *hw) @@ -2460,29 +2458,29 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, } } - if (conf->flags & IEEE80211_CONF_PS && - !test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) { - set_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags); + if ((changed & IEEE80211_CONF_CHANGE_PS) && !is_ap) { + + if ((conf->flags & IEEE80211_CONF_PS) && + test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && + !test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) { + + wl1271_debug(DEBUG_PSM, "auto ps enabled"); - /* - * We enter PSM only if we're already associated. - * If we're not, we'll enter it when joining an SSID, - * through the bss_info_changed() hook. - */ - if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { - wl1271_debug(DEBUG_PSM, "psm enabled"); ret = wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); - } - } else if (!(conf->flags & IEEE80211_CONF_PS) && - test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags)) { - wl1271_debug(DEBUG_PSM, "psm disabled"); + if (ret < 0) + wl1271_warning("enter auto ps failed %d", ret); - clear_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags); + } else if (!(conf->flags & IEEE80211_CONF_PS) && + test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) { + + wl1271_debug(DEBUG_PSM, "auto ps disabled"); - if (test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) ret = wl1271_ps_set_mode(wl, wlvif, STATION_ACTIVE_MODE); + if (ret < 0) + wl1271_warning("exit auto ps failed %d", ret); + } } if (conf->power_level != wlvif->power_level) { @@ -3816,16 +3814,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) goto out; } - - /* If we want to go in PSM but we're not there yet */ - if (test_bit(WLVIF_FLAG_PSM_REQUESTED, &wlvif->flags) && - !test_bit(WLVIF_FLAG_PSM, &wlvif->flags)) { - - ret = wl1271_ps_set_mode(wl, wlvif, - STATION_AUTO_PS_MODE); - if (ret < 0) - goto out; - } } /* Handle new association with HT. Do this after join. */ diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index d7a91d3c398d..e209e29ffb45 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -56,7 +56,7 @@ void wl1271_elp_work(struct work_struct *work) if (wlvif->bss_type == BSS_TYPE_AP_BSS) goto out; - if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) && + if (!test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) goto out; } @@ -84,7 +84,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) if (wlvif->bss_type == BSS_TYPE_AP_BSS) return; - if (!test_bit(WLVIF_FLAG_PSM, &wlvif->flags) && + if (!test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) return; } @@ -180,7 +180,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; - set_bit(WLVIF_FLAG_PSM, &wlvif->flags); + set_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags); /* enable beacon early termination. Not relevant for 5GHz */ if (wlvif->band == IEEE80211_BAND_2GHZ) { @@ -203,7 +203,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; - clear_bit(WLVIF_FLAG_PSM, &wlvif->flags); + clear_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags); break; case STATION_POWER_SAVE_MODE: default: diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index e18e6160fdab..9baed6be6a31 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -254,8 +254,7 @@ enum wl12xx_vif_flags { WLVIF_FLAG_STA_ASSOCIATED, WLVIF_FLAG_IBSS_JOINED, WLVIF_FLAG_AP_STARTED, - WLVIF_FLAG_PSM, - WLVIF_FLAG_PSM_REQUESTED, + WLVIF_FLAG_IN_AUTO_PS, WLVIF_FLAG_STA_STATE_SENT, WLVIF_FLAG_RX_STREAMING_STARTED, WLVIF_FLAG_PSPOLL_FAILURE, From d2e2d769e3d328ba7cbf08e8ed85e3f817915843 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Tue, 31 Jan 2012 17:54:40 +0200 Subject: [PATCH 30/50] wl12xx: Set IEEE80211_TX_RC_MCS on MCS rates on TX complete. IEEE80211_TX_RC_MCS was not set correctly leading to incorrect link speed calculation. Signed-off-by: Pontus Fuchs Signed-off-by: Ido Reis Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 4 ++++ drivers/net/wireless/wl12xx/tx.c | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 10e5e3df4b95..209f05312909 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -91,6 +91,10 @@ enum { CONF_HW_RXTX_RATE_UNSUPPORTED = 0xff }; +/* Rates between and including these are MCS rates */ +#define CONF_HW_RXTX_RATE_MCS_MIN CONF_HW_RXTX_RATE_MCS7 +#define CONF_HW_RXTX_RATE_MCS_MAX CONF_HW_RXTX_RATE_MCS0 + enum { CONF_SG_DISABLE = 0, CONF_SG_PROTECTIVE, diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 2bf43b86a3f9..f95112f6769f 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -764,6 +764,14 @@ void wl1271_tx_work(struct work_struct *work) mutex_unlock(&wl->mutex); } +static u8 wl1271_tx_get_rate_flags(u8 rate_class_index) +{ + if (rate_class_index >= CONF_HW_RXTX_RATE_MCS_MIN && + rate_class_index <= CONF_HW_RXTX_RATE_MCS_MAX) + return IEEE80211_TX_RC_MCS; + return 0; +} + static void wl1271_tx_complete_packet(struct wl1271 *wl, struct wl1271_tx_hw_res_descr *result) { @@ -773,6 +781,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, struct sk_buff *skb; int id = result->id; int rate = -1; + u8 rate_flags = 0; u8 retries = 0; /* check for id legality */ @@ -799,6 +808,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, info->flags |= IEEE80211_TX_STAT_ACK; rate = wl1271_rate_to_idx(result->rate_class_index, wlvif->band); + rate_flags = wl1271_tx_get_rate_flags(result->rate_class_index); retries = result->ack_failures; } else if (result->status == TX_RETRY_EXCEEDED) { wl->stats.excessive_retries++; @@ -807,7 +817,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, info->status.rates[0].idx = rate; info->status.rates[0].count = retries; - info->status.rates[0].flags = 0; + info->status.rates[0].flags = rate_flags; info->status.ack_signal = -1; wl->stats.retry_count += result->ack_failures; From defe02c720d54fc9bda3e5e625be70adac347a61 Mon Sep 17 00:00:00 2001 From: Pontus Fuchs Date: Tue, 31 Jan 2012 17:54:41 +0200 Subject: [PATCH 31/50] wl12xx: Set IEEE80211_TX_RC_SHORT_GI if short GI was used on New FW reports usage of short GI as a rate class index. Check for this rate and set the IEEE80211_TX_RC_SHORT_GI if used. Signed-off-by: Pontus Fuchs Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 5 +++-- drivers/net/wireless/wl12xx/main.c | 2 ++ drivers/net/wireless/wl12xx/tx.c | 8 ++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 209f05312909..1147396bf7eb 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -66,7 +66,8 @@ enum { }; enum { - CONF_HW_RXTX_RATE_MCS7 = 0, + CONF_HW_RXTX_RATE_MCS7_SGI = 0, + CONF_HW_RXTX_RATE_MCS7, CONF_HW_RXTX_RATE_MCS6, CONF_HW_RXTX_RATE_MCS5, CONF_HW_RXTX_RATE_MCS4, @@ -92,7 +93,7 @@ enum { }; /* Rates between and including these are MCS rates */ -#define CONF_HW_RXTX_RATE_MCS_MIN CONF_HW_RXTX_RATE_MCS7 +#define CONF_HW_RXTX_RATE_MCS_MIN CONF_HW_RXTX_RATE_MCS7_SGI #define CONF_HW_RXTX_RATE_MCS_MAX CONF_HW_RXTX_RATE_MCS0 enum { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 4ca7278201ec..fa2bf307aa0d 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -4406,6 +4406,7 @@ static struct ieee80211_channel wl1271_channels[] = { /* mapping to indexes for wl1271_rates */ static const u8 wl1271_rate_to_idx_2ghz[] = { /* MCS rates are used only with 11n */ + 7, /* CONF_HW_RXTX_RATE_MCS7_SGI */ 7, /* CONF_HW_RXTX_RATE_MCS7 */ 6, /* CONF_HW_RXTX_RATE_MCS6 */ 5, /* CONF_HW_RXTX_RATE_MCS5 */ @@ -4527,6 +4528,7 @@ static struct ieee80211_channel wl1271_channels_5ghz[] = { /* mapping to indexes for wl1271_rates_5ghz */ static const u8 wl1271_rate_to_idx_5ghz[] = { /* MCS rates are used only with 11n */ + 7, /* CONF_HW_RXTX_RATE_MCS7_SGI */ 7, /* CONF_HW_RXTX_RATE_MCS7 */ 6, /* CONF_HW_RXTX_RATE_MCS6 */ 5, /* CONF_HW_RXTX_RATE_MCS5 */ diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index f95112f6769f..ee313e7ce8fe 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -766,10 +766,14 @@ void wl1271_tx_work(struct work_struct *work) static u8 wl1271_tx_get_rate_flags(u8 rate_class_index) { + u8 flags = 0; + if (rate_class_index >= CONF_HW_RXTX_RATE_MCS_MIN && rate_class_index <= CONF_HW_RXTX_RATE_MCS_MAX) - return IEEE80211_TX_RC_MCS; - return 0; + flags |= IEEE80211_TX_RC_MCS; + if (rate_class_index == CONF_HW_RXTX_RATE_MCS7_SGI) + flags |= IEEE80211_TX_RC_SHORT_GI; + return flags; } static void wl1271_tx_complete_packet(struct wl1271 *wl, From f4f57943f265d80fe5f0fedf6964f8056e753cf3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 17:54:42 +0200 Subject: [PATCH 32/50] wl12xx: add TX_HW_ATTR_HOST_ENCRYPT flag In WEP shared authentication, we encrypt the auth frame in the host, and we want the fw to pass it as-is. Use the TX_HW_ATTR_HOST_ENCRYPT flag in order to indicate it to the fw. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/tx.c | 17 +++++++++++++---- drivers/net/wireless/wl12xx/tx.h | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ee313e7ce8fe..1f5cc2af5bc6 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -255,16 +255,20 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, int aligned_len, ac, rate_idx; s64 hosttime; u16 tx_attr = 0; + __le16 frame_control; + struct ieee80211_hdr *hdr; + u8 *frame_start; bool is_dummy; desc = (struct wl1271_tx_hw_descr *) skb->data; + frame_start = (u8 *)(desc + 1); + hdr = (struct ieee80211_hdr *)(frame_start + extra); + frame_control = hdr->frame_control; /* relocate space for security header */ if (extra) { - void *framestart = skb->data + sizeof(*desc); - u16 fc = *(u16 *)(framestart + extra); - int hdrlen = ieee80211_hdrlen(cpu_to_le16(fc)); - memmove(framestart, framestart + extra, hdrlen); + int hdrlen = ieee80211_hdrlen(frame_control); + memmove(frame_start, hdr, hdrlen); } /* configure packet life time */ @@ -353,6 +357,11 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, desc->wl127x_mem.total_mem_blocks); } + /* for WEP shared auth - no fw encryption is needed */ + if (ieee80211_is_auth(frame_control) && + ieee80211_has_protected(frame_control)) + tx_attr |= TX_HW_ATTR_HOST_ENCRYPT; + desc->tx_attr = cpu_to_le16(tx_attr); } diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 2dbb24e6d541..7ceb3ceaa648 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -39,6 +39,7 @@ #define TX_HW_ATTR_LAST_WORD_PAD (BIT(10) | BIT(11)) #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) +#define TX_HW_ATTR_HOST_ENCRYPT BIT(14) #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_HEADER_PAD 1 From 26612c435b34bd99fdf79f5c1d8826ebd0ea86f3 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 31 Jan 2012 17:54:43 +0200 Subject: [PATCH 33/50] wl12xx: add new coex params new params were added to the coex params. Add them with default value of 0. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 4 ++++ drivers/net/wireless/wl12xx/main.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 1147396bf7eb..18637eec1e21 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -317,6 +317,10 @@ enum { CONF_AP_BT_ACL_VAL_BT_SERVE_TIME, CONF_AP_BT_ACL_VAL_WL_SERVE_TIME, + /* CTS Diluting params */ + CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH, + CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER, + CONF_SG_TEMP_PARAM_1, CONF_SG_TEMP_PARAM_2, CONF_SG_TEMP_PARAM_3, diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fa2bf307aa0d..aa5d512c8f82 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -116,6 +116,9 @@ static struct conf_drv_settings default_conf = { [CONF_AP_CONNECTION_PROTECTION_TIME] = 0, [CONF_AP_BT_ACL_VAL_BT_SERVE_TIME] = 25, [CONF_AP_BT_ACL_VAL_WL_SERVE_TIME] = 25, + /* CTS Diluting params */ + [CONF_SG_CTS_DILUTED_BAD_RX_PACKETS_TH] = 0, + [CONF_SG_CTS_CHOP_IN_DUAL_ANT_SCO_MASTER] = 0, }, .state = CONF_SG_PROTECTIVE, }, From dae728fe67c7c5ab10f91366bd9d1f8db203e123 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 12:03:39 +0200 Subject: [PATCH 34/50] wl12xx: Set different wake up conditions in case of suspend Added ability to set different wake up conditions for suspend/resume. Set default values to wake up every 3 DTIMs while suspended and every 1 DTIM while resumed Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 10 +++--- drivers/net/wireless/wl12xx/acx.h | 3 +- drivers/net/wireless/wl12xx/conf.h | 13 ++++++++ drivers/net/wireless/wl12xx/main.c | 51 ++++++++++++++++++++++++++++-- drivers/net/wireless/wl12xx/ps.c | 4 ++- 5 files changed, 72 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index af2c3123d1d7..bc96db0683a5 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -34,12 +34,14 @@ #include "reg.h" #include "ps.h" -int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif) +int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 wake_up_event, u8 listen_interval) { struct acx_wake_up_condition *wake_up; int ret; - wl1271_debug(DEBUG_ACX, "acx wake up conditions"); + wl1271_debug(DEBUG_ACX, "acx wake up conditions (wake_up_event %d listen_interval %d)", + wake_up_event, listen_interval); wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); if (!wake_up) { @@ -48,8 +50,8 @@ int wl1271_acx_wake_up_conditions(struct wl1271 *wl, struct wl12xx_vif *wlvif) } wake_up->role_id = wlvif->role_id; - wake_up->wake_up_event = wl->conf.conn.wake_up_event; - wake_up->listen_interval = wl->conf.conn.listen_interval; + wake_up->wake_up_event = wake_up_event; + wake_up->listen_interval = listen_interval; ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, wake_up, sizeof(*wake_up)); diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 0749df58c85d..a28fc044034c 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1226,7 +1226,8 @@ enum { int wl1271_acx_wake_up_conditions(struct wl1271 *wl, - struct wl12xx_vif *wlvif); + struct wl12xx_vif *wlvif, + u8 wake_up_event, u8 listen_interval); int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth); int wl1271_acx_tx_power(struct wl1271 *wl, struct wl12xx_vif *wlvif, int power); diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 18637eec1e21..80eafadc389d 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -818,6 +818,19 @@ struct conf_conn_settings { */ u8 listen_interval; + /* + * Firmware wakeup conditions during suspend + * Range: CONF_WAKE_UP_EVENT_* + */ + u8 suspend_wake_up_event; + + /* + * Listen interval during suspend. + * Currently will be in DTIMs (1-10) + * + */ + u8 suspend_listen_interval; + /* * Enable or disable the beacon filtering. * diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index aa5d512c8f82..fa61dfd2084c 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -221,6 +221,8 @@ static struct conf_drv_settings default_conf = { .conn = { .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, .listen_interval = 1, + .suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM, + .suspend_listen_interval = 3, .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, .bcn_filt_ie_count = 2, .bcn_filt_ie = { @@ -1574,6 +1576,35 @@ static struct notifier_block wl1271_dev_notifier = { }; #ifdef CONFIG_PM +static int wl1271_configure_suspend_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif) +{ + int ret = 0; + + mutex_lock(&wl->mutex); + + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + goto out_unlock; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + + ret = wl1271_acx_wake_up_conditions(wl, wlvif, + wl->conf.conn.suspend_wake_up_event, + wl->conf.conn.suspend_listen_interval); + + if (ret < 0) + wl1271_error("suspend: set wake up conditions failed: %d", ret); + + + wl1271_ps_elp_sleep(wl); + +out_unlock: + mutex_unlock(&wl->mutex); + return ret; + +} static int wl1271_configure_suspend_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif) @@ -1601,6 +1632,8 @@ static int wl1271_configure_suspend_ap(struct wl1271 *wl, static int wl1271_configure_suspend(struct wl1271 *wl, struct wl12xx_vif *wlvif) { + if (wlvif->bss_type == BSS_TYPE_STA_BSS) + return wl1271_configure_suspend_sta(wl, wlvif); if (wlvif->bss_type == BSS_TYPE_AP_BSS) return wl1271_configure_suspend_ap(wl, wlvif); return 0; @@ -1609,10 +1642,11 @@ static int wl1271_configure_suspend(struct wl1271 *wl, static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) { - int ret; + int ret = 0; bool is_ap = wlvif->bss_type == BSS_TYPE_AP_BSS; + bool is_sta = wlvif->bss_type == BSS_TYPE_STA_BSS; - if (!is_ap) + if ((!is_ap) && (!is_sta)) return; mutex_lock(&wl->mutex); @@ -1620,7 +1654,18 @@ static void wl1271_configure_resume(struct wl1271 *wl, if (ret < 0) goto out; - wl1271_acx_beacon_filter_opt(wl, wlvif, false); + if (is_sta) { + ret = wl1271_acx_wake_up_conditions(wl, wlvif, + wl->conf.conn.wake_up_event, + wl->conf.conn.listen_interval); + + if (ret < 0) + wl1271_error("resume: wake up conditions failed: %d", + ret); + + } else if (is_ap) { + ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); + } wl1271_ps_elp_sleep(wl); out: diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index e209e29ffb45..d1979223ad28 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -170,7 +170,9 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", mode, timeout); - ret = wl1271_acx_wake_up_conditions(wl, wlvif); + ret = wl1271_acx_wake_up_conditions(wl, wlvif, + wl->conf.conn.wake_up_event, + wl->conf.conn.listen_interval); if (ret < 0) { wl1271_error("couldn't set wake up conditions"); return ret; From 59a10c66d0a1970d3f8a7e65bb1f03dec3aae3c5 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 12:03:40 +0200 Subject: [PATCH 35/50] wl12xx: add suspend_listen_interval debugfs file Add read/write to suspend_dtim_interval file which controls the number of DTIM periods between wakeups while the host is suspended. The value while the host is resumed is controlled by the file dtim_interval which existed previously. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 4dcb3f758fe2..15353fac0707 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -625,6 +625,64 @@ static const struct file_operations dtim_interval_ops = { .llseek = default_llseek, }; + + +static ssize_t suspend_dtim_interval_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + u8 value; + + if (wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_DTIM || + wl->conf.conn.suspend_wake_up_event == CONF_WAKE_UP_EVENT_N_DTIM) + value = wl->conf.conn.suspend_listen_interval; + else + value = 0; + + return wl1271_format_buffer(user_buf, count, ppos, "%d\n", value); +} + +static ssize_t suspend_dtim_interval_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + unsigned long value; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 10, &value); + if (ret < 0) { + wl1271_warning("illegal value for suspend_dtim_interval"); + return -EINVAL; + } + + if (value < 1 || value > 10) { + wl1271_warning("suspend_dtim value is not in valid range"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + wl->conf.conn.suspend_listen_interval = value; + /* for some reason there are different event types for 1 and >1 */ + if (value == 1) + wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_DTIM; + else + wl->conf.conn.suspend_wake_up_event = CONF_WAKE_UP_EVENT_N_DTIM; + + mutex_unlock(&wl->mutex); + return count; +} + + +static const struct file_operations suspend_dtim_interval_ops = { + .read = suspend_dtim_interval_read, + .write = suspend_dtim_interval_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static ssize_t beacon_interval_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -949,6 +1007,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(driver_state, rootdir); DEBUGFS_ADD(vifs_state, rootdir); DEBUGFS_ADD(dtim_interval, rootdir); + DEBUGFS_ADD(suspend_dtim_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir); DEBUGFS_ADD(beacon_filtering, rootdir); DEBUGFS_ADD(dynamic_ps_timeout, rootdir); From 5c0dc2fcfec606cf9f2d28ff31bbeb0a6225b27a Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 19:06:45 +0200 Subject: [PATCH 36/50] wl12xx: add forced_ps mode For certain WiFi certification tests forcing PS is necessary. Since DPS is now enabled in the FW and this can't be achieved by using netlatency this required a new config option. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 6 ++++++ drivers/net/wireless/wl12xx/debugfs.c | 2 +- drivers/net/wireless/wl12xx/main.c | 25 +++++++++++++++++++------ drivers/net/wireless/wl12xx/ps.c | 10 +++++----- drivers/net/wireless/wl12xx/wl12xx.h | 2 +- 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 80eafadc389d..823535cdf150 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -942,6 +942,12 @@ struct conf_conn_settings { */ u16 dynamic_ps_timeout; + /* + * Specifies whether dynamic PS should be disabled and PSM forced. + * This is required for certain WiFi certification tests. + */ + u8 forced_ps; + /* * * Specifies the interval of the connection keep-alive null-func diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 15353fac0707..02da445ea98a 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -358,7 +358,7 @@ static ssize_t dynamic_ps_timeout_write(struct file *file, */ wl12xx_for_each_wlvif_sta(wl, wlvif) { - if (test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) + if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) wl1271_ps_set_mode(wl, wlvif, STATION_AUTO_PS_MODE); } diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fa61dfd2084c..b828149a1655 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -247,6 +247,7 @@ static struct conf_drv_settings default_conf = { .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, .dynamic_ps_timeout = 100, + .forced_ps = false, .keep_alive_interval = 55000, .max_listen_interval = 20, }, @@ -2510,17 +2511,29 @@ static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, if ((conf->flags & IEEE80211_CONF_PS) && test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) && - !test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) { + !test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { - wl1271_debug(DEBUG_PSM, "auto ps enabled"); + int ps_mode; + char *ps_mode_str; + + if (wl->conf.conn.forced_ps) { + ps_mode = STATION_POWER_SAVE_MODE; + ps_mode_str = "forced"; + } else { + ps_mode = STATION_AUTO_PS_MODE; + ps_mode_str = "auto"; + } + + wl1271_debug(DEBUG_PSM, "%s ps enabled", ps_mode_str); + + ret = wl1271_ps_set_mode(wl, wlvif, ps_mode); - ret = wl1271_ps_set_mode(wl, wlvif, - STATION_AUTO_PS_MODE); if (ret < 0) - wl1271_warning("enter auto ps failed %d", ret); + wl1271_warning("enter %s ps failed %d", + ps_mode_str, ret); } else if (!(conf->flags & IEEE80211_CONF_PS) && - test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags)) { + test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) { wl1271_debug(DEBUG_PSM, "auto ps disabled"); diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index d1979223ad28..23d67501c50a 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -56,7 +56,7 @@ void wl1271_elp_work(struct work_struct *work) if (wlvif->bss_type == BSS_TYPE_AP_BSS) goto out; - if (!test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags) && + if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) goto out; } @@ -84,7 +84,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) if (wlvif->bss_type == BSS_TYPE_AP_BSS) return; - if (!test_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags) && + if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) return; } @@ -167,6 +167,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, switch (mode) { case STATION_AUTO_PS_MODE: + case STATION_POWER_SAVE_MODE: wl1271_debug(DEBUG_PSM, "entering psm (mode=%d,timeout=%u)", mode, timeout); @@ -182,7 +183,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; - set_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags); + set_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); /* enable beacon early termination. Not relevant for 5GHz */ if (wlvif->band == IEEE80211_BAND_2GHZ) { @@ -205,9 +206,8 @@ int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ret < 0) return ret; - clear_bit(WLVIF_FLAG_IN_AUTO_PS, &wlvif->flags); + clear_bit(WLVIF_FLAG_IN_PS, &wlvif->flags); break; - case STATION_POWER_SAVE_MODE: default: wl1271_warning("trying to set ps to unsupported mode %d", mode); ret = -EINVAL; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9baed6be6a31..61c58c13fa90 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -254,7 +254,7 @@ enum wl12xx_vif_flags { WLVIF_FLAG_STA_ASSOCIATED, WLVIF_FLAG_IBSS_JOINED, WLVIF_FLAG_AP_STARTED, - WLVIF_FLAG_IN_AUTO_PS, + WLVIF_FLAG_IN_PS, WLVIF_FLAG_STA_STATE_SENT, WLVIF_FLAG_RX_STREAMING_STARTED, WLVIF_FLAG_PSPOLL_FAILURE, From 20ae7e5e4b13937da6882bf84b080eb31feb9a7b Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 12:03:42 +0200 Subject: [PATCH 37/50] wl12xx: add forced_ps debugfs file Added control over forced_ps option through debugfs. This can be either 1 or 0. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 70 +++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 02da445ea98a..1c2623850eae 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -376,6 +376,75 @@ static const struct file_operations dynamic_ps_timeout_ops = { .llseek = default_llseek, }; +static ssize_t forced_ps_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + return wl1271_format_buffer(user_buf, count, + ppos, "%d\n", + wl->conf.conn.forced_ps); +} + +static ssize_t forced_ps_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + struct wl12xx_vif *wlvif; + unsigned long value; + int ret, ps_mode; + + ret = kstrtoul_from_user(user_buf, count, 10, &value); + if (ret < 0) { + wl1271_warning("illegal value in forced_ps"); + return -EINVAL; + } + + if (value != 1 && value != 0) { + wl1271_warning("forced_ps should be either 0 or 1"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + if (wl->conf.conn.forced_ps == value) + goto out; + + wl->conf.conn.forced_ps = value; + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* In case we're already in PSM, trigger it again to switch mode + * immediately without waiting for re-association + */ + + ps_mode = value ? STATION_POWER_SAVE_MODE : STATION_AUTO_PS_MODE; + + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags)) + wl1271_ps_set_mode(wl, wlvif, ps_mode); + } + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations forced_ps_ops = { + .read = forced_ps_read, + .write = forced_ps_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static ssize_t driver_state_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1011,6 +1080,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(beacon_interval, rootdir); DEBUGFS_ADD(beacon_filtering, rootdir); DEBUGFS_ADD(dynamic_ps_timeout, rootdir); + DEBUGFS_ADD(forced_ps, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) From 5ec8a448e0e978103bc5ca7136084b5e2b36989e Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 2 Feb 2012 12:22:09 +0200 Subject: [PATCH 38/50] wl12xx: consider encryption and QoS in auto arp template When configuring the arp response template, and encryption is enabled, we should add some space and set the protected flag bit in the fc. In order to track the encryption type, set wlvif->encryption_type when setting an encryption key, and reconfigure the arp response. Clear this field on wl1271_join, as keys have to be re-configured anyway after a join command. Similarly, track whether QoS is configured. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 88 +++++++++++++++++----- drivers/net/wireless/wl12xx/cmd.h | 3 +- drivers/net/wireless/wl12xx/debugfs.c | 1 + drivers/net/wireless/wl12xx/init.c | 6 +- drivers/net/wireless/wl12xx/main.c | 31 +++++++- drivers/net/wireless/wl12xx/tx.c | 11 +-- drivers/net/wireless/wl12xx/tx.h | 4 +- drivers/net/wireless/wl12xx/wl12xx.h | 6 ++ drivers/net/wireless/wl12xx/wl12xx_80211.h | 2 +- 9 files changed, 117 insertions(+), 35 deletions(-) diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index bf3c37bd1f12..b776d9d5efe8 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -1213,32 +1213,34 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, return skb; } -int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, - __be32 ip_addr) +int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif) { - int ret; + int ret, extra; + u16 fc; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); - struct wl12xx_arp_rsp_template tmpl; + struct sk_buff *skb; + struct wl12xx_arp_rsp_template *tmpl; struct ieee80211_hdr_3addr *hdr; struct arphdr *arp_hdr; - memset(&tmpl, 0, sizeof(tmpl)); + skb = dev_alloc_skb(sizeof(*hdr) + sizeof(__le16) + sizeof(*tmpl) + + WL1271_EXTRA_SPACE_MAX); + if (!skb) { + wl1271_error("failed to allocate buffer for arp rsp template"); + return -ENOMEM; + } - /* mac80211 header */ - hdr = &tmpl.hdr; - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_DATA | - IEEE80211_FCTL_TODS); - memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN); - memcpy(hdr->addr2, vif->addr, ETH_ALEN); - memset(hdr->addr3, 0xff, ETH_ALEN); + skb_reserve(skb, sizeof(*hdr) + WL1271_EXTRA_SPACE_MAX); + + tmpl = (struct wl12xx_arp_rsp_template *)skb_put(skb, sizeof(*tmpl)); + memset(tmpl, 0, sizeof(tmpl)); /* llc layer */ - memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header)); - tmpl.llc_type = cpu_to_be16(ETH_P_ARP); + memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header)); + tmpl->llc_type = cpu_to_be16(ETH_P_ARP); /* arp header */ - arp_hdr = &tmpl.arp_hdr; + arp_hdr = &tmpl->arp_hdr; arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER); arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP); arp_hdr->ar_hln = ETH_ALEN; @@ -1246,13 +1248,59 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY); /* arp payload */ - memcpy(tmpl.sender_hw, vif->addr, ETH_ALEN); - tmpl.sender_ip = ip_addr; + memcpy(tmpl->sender_hw, vif->addr, ETH_ALEN); + tmpl->sender_ip = wlvif->ip_addr; + + /* encryption space */ + switch (wlvif->encryption_type) { + case KEY_TKIP: + extra = WL1271_EXTRA_SPACE_TKIP; + break; + case KEY_AES: + extra = WL1271_EXTRA_SPACE_AES; + break; + case KEY_NONE: + case KEY_WEP: + case KEY_GEM: + extra = 0; + break; + default: + wl1271_warning("Unknown encryption type: %d", + wlvif->encryption_type); + ret = -EINVAL; + goto out; + } + + if (extra) { + u8 *space = skb_push(skb, extra); + memset(space, 0, extra); + } + + /* QoS header - BE */ + if (wlvif->sta.qos) + memset(skb_push(skb, sizeof(__le16)), 0, sizeof(__le16)); + + /* mac80211 header */ + hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(hdr)); + fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS; + if (wlvif->sta.qos) + fc |= IEEE80211_STYPE_QOS_DATA; + else + fc |= IEEE80211_STYPE_DATA; + if (wlvif->encryption_type != KEY_NONE) + fc |= IEEE80211_FCTL_PROTECTED; + + hdr->frame_control = cpu_to_le16(fc); + memcpy(hdr->addr1, vif->bss_conf.bssid, ETH_ALEN); + memcpy(hdr->addr2, vif->addr, ETH_ALEN); + memset(hdr->addr3, 0xff, ETH_ALEN); ret = wl1271_cmd_template_set(wl, wlvif->role_id, CMD_TEMPL_ARP_RSP, - &tmpl, sizeof(tmpl), 0, + skb->data, skb->len, 0, wlvif->basic_rate); - +out: + dev_kfree_skb(skb); return ret; } diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 83a2a2edb261..de217d92516b 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -67,8 +67,7 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct sk_buff *skb); -int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif, - __be32 ip_addr); +int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_build_qos_null_data(struct wl1271 *wl, struct ieee80211_vif *vif); int wl12xx_cmd_build_klv_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif); diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 1c2623850eae..f063c704ebcf 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -579,6 +579,7 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf, VIF_STATE_PRINT_INT(sta.basic_rate_idx); VIF_STATE_PRINT_INT(sta.ap_rate_idx); VIF_STATE_PRINT_INT(sta.p2p_rate_idx); + VIF_STATE_PRINT_INT(sta.qos); } else { VIF_STATE_PRINT_INT(ap.global_hlid); VIF_STATE_PRINT_INT(ap.bcast_hlid); diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index fcf2b128f9a4..acc03799fc66 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -37,6 +37,7 @@ int wl1271_init_templates_config(struct wl1271 *wl) { int ret, i; + size_t max_size; /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, @@ -89,10 +90,11 @@ int wl1271_init_templates_config(struct wl1271 *wl) if (ret < 0) return ret; + max_size = sizeof(struct wl12xx_arp_rsp_template) + + WL1271_EXTRA_SPACE_MAX; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_ARP_RSP, NULL, - sizeof - (struct wl12xx_arp_rsp_template), + max_size, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b828149a1655..a84fe28873ea 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2324,6 +2324,9 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) wl1271_info("JOIN while associated."); + /* clear encryption type */ + wlvif->encryption_type = KEY_NONE; + if (set_assoc) set_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags); @@ -2981,6 +2984,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, wl1271_error("Could not add or replace key"); goto out_sleep; } + + /* + * reconfiguring arp response if the unicast (or common) + * encryption key type was changed + */ + if (wlvif->bss_type == BSS_TYPE_STA_BSS && + (sta || key_type == KEY_WEP) && + wlvif->encryption_type != key_type) { + wlvif->encryption_type = key_type; + ret = wl1271_cmd_build_arp_rsp(wl, wlvif); + if (ret < 0) { + wl1271_warning("build arp rsp failed: %d", ret); + goto out_sleep; + } + } break; case DISABLE_KEY: @@ -3822,19 +3840,22 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) goto out; - if (changed & BSS_CHANGED_ARP_FILTER) { + if ((changed & BSS_CHANGED_ARP_FILTER) || + (!is_ibss && (changed & BSS_CHANGED_QOS))) { __be32 addr = bss_conf->arp_addr_list[0]; + wlvif->sta.qos = bss_conf->qos; WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled) { + wlvif->ip_addr = addr; /* * The template should have been configured only upon * association. however, it seems that the correct ip * isn't being set (when sending), so we have to * reconfigure the template upon every ip change. */ - ret = wl1271_cmd_build_arp_rsp(wl, wlvif, addr); + ret = wl1271_cmd_build_arp_rsp(wl, wlvif); if (ret < 0) { wl1271_warning("build arp rsp failed: %d", ret); goto out; @@ -3843,8 +3864,10 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, ret = wl1271_acx_arp_ip_filter(wl, wlvif, ACX_ARP_FILTER_ARP_FILTERING, addr); - } else + } else { + wlvif->ip_addr = 0; ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr); + } if (ret < 0) goto out; @@ -5007,7 +5030,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) }; /* The tx descriptor buffer and the TKIP space. */ - wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE + + wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP + sizeof(struct wl1271_tx_hw_descr); /* unit us */ diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 1f5cc2af5bc6..6446e4d3e8f9 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -386,7 +386,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) - extra = WL1271_TKIP_IV_SPACE; + extra = WL1271_EXTRA_SPACE_TKIP; if (info->control.hw_key) { bool is_wep; @@ -861,8 +861,9 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl, if (info->control.hw_key && info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); - memmove(skb->data + WL1271_TKIP_IV_SPACE, skb->data, hdrlen); - skb_pull(skb, WL1271_TKIP_IV_SPACE); + memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, + hdrlen); + skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); } wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" @@ -1004,9 +1005,9 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues) info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { int hdrlen = ieee80211_get_hdrlen_from_skb(skb); - memmove(skb->data + WL1271_TKIP_IV_SPACE, + memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen); - skb_pull(skb, WL1271_TKIP_IV_SPACE); + skb_pull(skb, WL1271_EXTRA_SPACE_TKIP); } info->status.rates[0].idx = -1; diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 7ceb3ceaa648..e3977b55a710 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -52,7 +52,9 @@ #define TX_HW_RESULT_QUEUE_LEN_MASK 0xf #define WL1271_TX_ALIGN_TO 4 -#define WL1271_TKIP_IV_SPACE 4 +#define WL1271_EXTRA_SPACE_TKIP 4 +#define WL1271_EXTRA_SPACE_AES 8 +#define WL1271_EXTRA_SPACE_MAX 8 /* Used for management frames and dummy packets */ #define WL1271_TID_MGMT 7 diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 61c58c13fa90..81af416d17da 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -507,6 +507,8 @@ struct wl12xx_vif { u8 basic_rate_idx; u8 ap_rate_idx; u8 p2p_rate_idx; + + bool qos; } sta; struct { u8 global_hlid; @@ -573,6 +575,10 @@ struct wl12xx_vif { int rssi_thold; int last_rssi_event; + /* save the current encryption type for auto-arp config */ + u8 encryption_type; + __be32 ip_addr; + /* RX BA constraint value */ bool ba_support; bool ba_allowed; diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 8f0ffaf62309..22b0bc98d7b5 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -117,7 +117,7 @@ struct wl12xx_ps_poll_template { } __packed; struct wl12xx_arp_rsp_template { - struct ieee80211_hdr_3addr hdr; + /* not including ieee80211 header */ u8 llc_hdr[sizeof(rfc1042_header)]; __be16 llc_type; From b87f20c018cead99b76c431b9289c0f17168d21c Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 2 Feb 2012 12:22:10 +0200 Subject: [PATCH 39/50] wl12xx: Revert "wl12xx: disable auto-arp" This reverts commit e5e2f24b3eec67a7a35d43654a997f98ca21aff2. The encryption consideration on auto-arp configuration, along with a fw fix, seem to resolve the crashes that occured when auto-arp was enabled, so we can re-enable it now. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a84fe28873ea..a264cb5bee5a 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3862,7 +3862,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } ret = wl1271_acx_arp_ip_filter(wl, wlvif, - ACX_ARP_FILTER_ARP_FILTERING, + (ACX_ARP_FILTER_ARP_FILTERING | + ACX_ARP_FILTER_AUTO_ARP), addr); } else { wlvif->ip_addr = 0; From 76a74c8a65c6d2587e48355d3a122b4274f5abbf Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 2 Feb 2012 12:22:11 +0200 Subject: [PATCH 40/50] wl12xx: configure arp filtering only after association We have to configure arp filtering only after the role was started, so move the BSS_CHANGED_ARP_FILTER handling after the join. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 69 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index a264cb5bee5a..b45a16c1f717 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3840,40 +3840,6 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) goto out; - if ((changed & BSS_CHANGED_ARP_FILTER) || - (!is_ibss && (changed & BSS_CHANGED_QOS))) { - __be32 addr = bss_conf->arp_addr_list[0]; - wlvif->sta.qos = bss_conf->qos; - WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); - - if (bss_conf->arp_addr_cnt == 1 && - bss_conf->arp_filter_enabled) { - wlvif->ip_addr = addr; - /* - * The template should have been configured only upon - * association. however, it seems that the correct ip - * isn't being set (when sending), so we have to - * reconfigure the template upon every ip change. - */ - ret = wl1271_cmd_build_arp_rsp(wl, wlvif); - if (ret < 0) { - wl1271_warning("build arp rsp failed: %d", ret); - goto out; - } - - ret = wl1271_acx_arp_ip_filter(wl, wlvif, - (ACX_ARP_FILTER_ARP_FILTERING | - ACX_ARP_FILTER_AUTO_ARP), - addr); - } else { - wlvif->ip_addr = 0; - ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr); - } - - if (ret < 0) - goto out; - } - if (do_join) { ret = wl1271_join(wl, wlvif, set_assoc); if (ret < 0) { @@ -3940,6 +3906,41 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } + /* Handle arp filtering. Done after join. */ + if ((changed & BSS_CHANGED_ARP_FILTER) || + (!is_ibss && (changed & BSS_CHANGED_QOS))) { + __be32 addr = bss_conf->arp_addr_list[0]; + wlvif->sta.qos = bss_conf->qos; + WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS); + + if (bss_conf->arp_addr_cnt == 1 && + bss_conf->arp_filter_enabled) { + wlvif->ip_addr = addr; + /* + * The template should have been configured only upon + * association. however, it seems that the correct ip + * isn't being set (when sending), so we have to + * reconfigure the template upon every ip change. + */ + ret = wl1271_cmd_build_arp_rsp(wl, wlvif); + if (ret < 0) { + wl1271_warning("build arp rsp failed: %d", ret); + goto out; + } + + ret = wl1271_acx_arp_ip_filter(wl, wlvif, + (ACX_ARP_FILTER_ARP_FILTERING | + ACX_ARP_FILTER_AUTO_ARP), + addr); + } else { + wlvif->ip_addr = 0; + ret = wl1271_acx_arp_ip_filter(wl, wlvif, 0, addr); + } + + if (ret < 0) + goto out; + } + out: return; } From 79aba1baf251e22fbc97a42c711c4b683807ac43 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 2 Feb 2012 13:15:35 +0200 Subject: [PATCH 41/50] wl12xx: declare support for hw scan while idle By allowing hw scan while idle, we no longer need the redundant ROC/CROC that are done on idle off/on, which helps simplifying the state machine of the driver. This way, we can also allow scanning while there is an ongoing sched scan (otherwise, we won't be able to ROC on idle-off) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 7 ++----- drivers/net/wireless/wl12xx/scan.c | 13 ------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b45a16c1f717..17dcd63bfc48 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -3068,10 +3068,6 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, goto out_sleep; } - /* cancel ROC before scanning */ - if (wl12xx_dev_role_started(wlvif)) - wl12xx_croc(wl, wlvif->dev_role_id); - ret = wl1271_scan(hw->priv, vif, ssid, len, req); out_sleep: wl1271_ps_elp_sleep(wl); @@ -5050,7 +5046,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_TX_AMPDU_SETUP_IN_HW; + IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | + IEEE80211_HW_SCAN_WHILE_IDLE; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 6adc7333b7e6..848a165fa55c 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -38,7 +38,6 @@ void wl1271_scan_complete_work(struct work_struct *work) struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; int ret; - bool is_sta, is_ibss; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, scan_complete_work); @@ -70,18 +69,6 @@ void wl1271_scan_complete_work(struct work_struct *work) wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq); } - /* return to ROC if needed */ - is_sta = (wlvif->bss_type == BSS_TYPE_STA_BSS); - is_ibss = (wlvif->bss_type == BSS_TYPE_IBSS); - if (((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) || - (is_ibss && !test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags))) && - !test_bit(wlvif->dev_role_id, wl->roc_map)) { - /* restore remain on channel */ - if (wlvif->dev_hlid == WL12XX_INVALID_LINK_ID) - wl12xx_start_dev(wl, wlvif); - else - wl12xx_roc(wl, wlvif, wlvif->dev_role_id); - } wl1271_ps_elp_sleep(wl); if (wl->scan.failed) { From 35d7742ff31609d655f8f8b3f869647006a2ac26 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 2 Feb 2012 13:54:26 +0200 Subject: [PATCH 42/50] wl12xx: don't fail on AP scan AP role uses its own role_id for scans, so there's no reason to fail the scan if dev_role_id is invalid. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/scan.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 848a165fa55c..e3566ca90f0d 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -175,16 +175,17 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, if (passive) scan_options |= WL1271_SCAN_OPT_PASSIVE; - if (WARN_ON(wlvif->role_id == WL12XX_INVALID_ROLE_ID || - wlvif->dev_role_id == WL12XX_INVALID_ROLE_ID)) { - ret = -EINVAL; - goto out; - } - if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + if (wlvif->bss_type == BSS_TYPE_AP_BSS || + test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) cmd->params.role_id = wlvif->role_id; else cmd->params.role_id = wlvif->dev_role_id; + if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { + ret = -EINVAL; + goto out; + } + cmd->params.scan_options = cpu_to_le16(scan_options); cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, From c08e371a683021cfb99632db799bea9b69d176e5 Mon Sep 17 00:00:00 2001 From: Ido Reis Date: Thu, 2 Feb 2012 13:54:27 +0200 Subject: [PATCH 43/50] wl12xx: increase max probe-req template size to WL1271_CMD_TEMPL_MAX_SIZE Increase max scan IEs to allow big probe-req frames Report a correct max-length for the scan IEs we can support, according to the now larger size of the probe-req template. Signed-off-by: Ido Reis Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/init.c | 4 ++-- drivers/net/wireless/wl12xx/main.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index acc03799fc66..203fbebf09eb 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -42,14 +42,14 @@ int wl1271_init_templates_config(struct wl1271 *wl) /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, - WL1271_CMD_TEMPL_DFLT_SIZE, + WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; ret = wl1271_cmd_template_set(wl, WL12XX_INVALID_ROLE_ID, CMD_TEMPL_CFG_PROBE_REQ_5, - NULL, WL1271_CMD_TEMPL_DFLT_SIZE, 0, + NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 17dcd63bfc48..253847ff344b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -5063,10 +5063,10 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) * should be the maximum length possible for a template, without * the IEEE80211 header of the template */ - wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE - + wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); - wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE - + wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; From d647f2dd1a445f5a683372f1b47919fe92c19896 Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 13:54:28 +0200 Subject: [PATCH 44/50] wl12xx: use split scan for normal scan Split scan allows the FW to schedule other activities during a scan which may be a long operation. This is achieved by setting a trigger TID to ANY_TID and a scan trigger timeout other than 0. The default one is set to 50ms. Signed-off-by: Eyal Shapira Signed-off-by: Igal Chernobelsky Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/conf.h | 8 ++++++++ drivers/net/wireless/wl12xx/main.c | 1 + drivers/net/wireless/wl12xx/scan.c | 8 +++++--- drivers/net/wireless/wl12xx/scan.h | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 823535cdf150..cc50faaf03d1 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1082,6 +1082,14 @@ struct conf_scan_settings { */ u16 num_probe_reqs; + /* + * Scan trigger (split scan) timeout. The FW will split the scan + * operation into slices of the given time and allow the FW to schedule + * other tasks in between. + * + * Range: u32 Microsecs + */ + u32 split_scan_timeout; }; struct conf_sched_scan_settings { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 253847ff344b..723df48ed447 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -272,6 +272,7 @@ static struct conf_drv_settings default_conf = { .min_dwell_time_passive = 100000, .max_dwell_time_passive = 100000, .num_probe_reqs = 2, + .split_scan_timeout = 50000, }, .sched_scan = { /* sched_scan requires dwell times in TU instead of TU/1000 */ diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index e3566ca90f0d..e43a6b2c1d91 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -172,6 +172,9 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, goto out; } + if (wl->conf.scan.split_scan_timeout) + scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; + if (passive) scan_options |= WL1271_SCAN_OPT_PASSIVE; @@ -198,7 +201,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, cmd->params.tx_rate = cpu_to_le32(basic_rate); cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; - cmd->params.tid_trigger = 0; + cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; if (band == IEEE80211_BAND_2GHZ) @@ -223,8 +226,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct ieee80211_vif *vif, goto out; } - /* disable the timeout */ - trigger->timeout = 0; + trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, sizeof(*trigger), 0); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h index a7ed43dc08c9..96ff457a3a0b 100644 --- a/drivers/net/wireless/wl12xx/scan.h +++ b/drivers/net/wireless/wl12xx/scan.h @@ -48,7 +48,7 @@ void wl1271_scan_sched_scan_results(struct wl1271 *wl); #define WL1271_SCAN_CURRENT_TX_PWR 0 #define WL1271_SCAN_OPT_ACTIVE 0 #define WL1271_SCAN_OPT_PASSIVE 1 -#define WL1271_SCAN_OPT_TRIGGERED_SCAN 2 +#define WL1271_SCAN_OPT_SPLIT_SCAN 2 #define WL1271_SCAN_OPT_PRIORITY_HIGH 4 /* scan even if we fail to enter psm */ #define WL1271_SCAN_OPT_FORCE 8 From f95f9aad150238236b2338efa60a14891081026e Mon Sep 17 00:00:00 2001 From: Eyal Shapira Date: Thu, 2 Feb 2012 13:54:29 +0200 Subject: [PATCH 45/50] wl12xx: add split_scan_timeout debugfs file Add control over split_scan_timeout through debugfs. Values are in ms while 0 will disable split scan. Signed-off-by: Eyal Shapira Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index f063c704ebcf..00dbe1595a7f 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -445,6 +445,48 @@ static const struct file_operations forced_ps_ops = { .llseek = default_llseek, }; +static ssize_t split_scan_timeout_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + + return wl1271_format_buffer(user_buf, count, + ppos, "%d\n", + wl->conf.scan.split_scan_timeout / 1000); +} + +static ssize_t split_scan_timeout_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + unsigned long value; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 10, &value); + if (ret < 0) { + wl1271_warning("illegal value in split_scan_timeout"); + return -EINVAL; + } + + if (value == 0) + wl1271_info("split scan will be disabled"); + + mutex_lock(&wl->mutex); + + wl->conf.scan.split_scan_timeout = value * 1000; + + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations split_scan_timeout_ops = { + .read = split_scan_timeout_read, + .write = split_scan_timeout_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static ssize_t driver_state_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1082,6 +1124,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(beacon_filtering, rootdir); DEBUGFS_ADD(dynamic_ps_timeout, rootdir); DEBUGFS_ADD(forced_ps, rootdir); + DEBUGFS_ADD(split_scan_timeout, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) From 3fcdab7066a31ae90ac2beba7d38e8e606374998 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 6 Feb 2012 12:47:54 +0200 Subject: [PATCH 46/50] wl12xx: Use a dedicated fw for PLT A special PLT firmware is used for calibration. Add multiple fw support by introducing a new fw_type member, representing the currently saved fw (the actual fw state can be determined by wl->state). Signed-off-by: Gery Kahn Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 2 +- drivers/net/wireless/wl12xx/main.c | 53 +++++++++++++++++--------- drivers/net/wireless/wl12xx/rx.c | 2 +- drivers/net/wireless/wl12xx/sdio.c | 2 + drivers/net/wireless/wl12xx/spi.c | 2 + drivers/net/wireless/wl12xx/testmode.c | 2 +- drivers/net/wireless/wl12xx/wl12xx.h | 11 +++++- 7 files changed, 52 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 00dbe1595a7f..e1cf72765965 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -113,7 +113,7 @@ static void wl1271_debugfs_update_stats(struct wl1271 *wl) if (ret < 0) goto out; - if (wl->state == WL1271_STATE_ON && + if (wl->state == WL1271_STATE_ON && !wl->plt && time_after(jiffies, wl->stats.fw_stats_update + msecs_to_jiffies(WL1271_DEBUGFS_STATS_LIFETIME))) { wl1271_acx_statistics(wl, wl->stats.fw_stats); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 723df48ed447..c10940703e8c 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -993,16 +993,29 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) return IRQ_HANDLED; } -static int wl1271_fetch_firmware(struct wl1271 *wl) +static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt) { const struct firmware *fw; const char *fw_name; + enum wl12xx_fw_type fw_type; int ret; - if (wl->chip.id == CHIP_ID_1283_PG20) - fw_name = WL128X_FW_NAME; - else - fw_name = WL127X_FW_NAME; + if (plt) { + fw_type = WL12XX_FW_TYPE_PLT; + if (wl->chip.id == CHIP_ID_1283_PG20) + fw_name = WL128X_PLT_FW_NAME; + else + fw_name = WL127X_PLT_FW_NAME; + } else { + fw_type = WL12XX_FW_TYPE_NORMAL; + if (wl->chip.id == CHIP_ID_1283_PG20) + fw_name = WL128X_FW_NAME; + else + fw_name = WL127X_FW_NAME; + } + + if (wl->fw_type == fw_type) + return 0; wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name); @@ -1021,6 +1034,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) } vfree(wl->fw); + wl->fw_type = WL12XX_FW_TYPE_NONE; wl->fw_len = fw->size; wl->fw = vmalloc(wl->fw_len); @@ -1032,7 +1046,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl) memcpy(wl->fw, fw->data, wl->fw_len); ret = 0; - + wl->fw_type = fw_type; out: release_firmware(fw); @@ -1160,7 +1174,7 @@ static void wl1271_recovery_work(struct work_struct *work) mutex_lock(&wl->mutex); - if (wl->state != WL1271_STATE_ON) + if (wl->state != WL1271_STATE_ON || wl->plt) goto out_unlock; /* Avoid a recursive recovery */ @@ -1261,7 +1275,7 @@ static int wl12xx_set_power_on(struct wl1271 *wl) return ret; } -static int wl1271_chip_wakeup(struct wl1271 *wl) +static int wl12xx_chip_wakeup(struct wl1271 *wl, bool plt) { int ret = 0; @@ -1316,11 +1330,9 @@ static int wl1271_chip_wakeup(struct wl1271 *wl) goto out; } - if (wl->fw == NULL) { - ret = wl1271_fetch_firmware(wl); - if (ret < 0) - goto out; - } + ret = wl12xx_fetch_firmware(wl, plt); + if (ret < 0) + goto out; /* No NVS from netlink, try to get it from the filesystem */ if (wl->nvs == NULL) { @@ -1352,7 +1364,7 @@ int wl1271_plt_start(struct wl1271 *wl) while (retries) { retries--; - ret = wl1271_chip_wakeup(wl); + ret = wl12xx_chip_wakeup(wl, true); if (ret < 0) goto power_off; @@ -1364,7 +1376,8 @@ int wl1271_plt_start(struct wl1271 *wl) if (ret < 0) goto irq_disable; - wl->state = WL1271_STATE_PLT; + wl->plt = true; + wl->state = WL1271_STATE_ON; wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver_str); @@ -1413,7 +1426,7 @@ int wl1271_plt_stop(struct wl1271 *wl) */ wl1271_disable_interrupts(wl); mutex_lock(&wl->mutex); - if (wl->state != WL1271_STATE_PLT) { + if (!wl->plt) { mutex_unlock(&wl->mutex); /* @@ -1440,6 +1453,7 @@ int wl1271_plt_stop(struct wl1271 *wl) wl1271_power_off(wl); wl->flags = 0; wl->state = WL1271_STATE_OFF; + wl->plt = false; wl->rx_counter = 0; mutex_unlock(&wl->mutex); @@ -1995,7 +2009,7 @@ static bool wl12xx_init_fw(struct wl1271 *wl) while (retries) { retries--; - ret = wl1271_chip_wakeup(wl); + ret = wl12xx_chip_wakeup(wl, false); if (ret < 0) goto power_off; @@ -2098,6 +2112,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } + ret = wl12xx_init_vif_data(wl, vif); if (ret < 0) goto out; @@ -5009,7 +5024,7 @@ static int wl1271_register_hw(struct wl1271 *wl) static void wl1271_unregister_hw(struct wl1271 *wl) { - if (wl->state == WL1271_STATE_PLT) + if (wl->plt) wl1271_plt_stop(wl); unregister_netdevice_notifier(&wl1271_dev_notifier); @@ -5186,6 +5201,7 @@ static struct ieee80211_hw *wl1271_alloc_hw(void) spin_lock_init(&wl->wl_lock); wl->state = WL1271_STATE_OFF; + wl->fw_type = WL12XX_FW_TYPE_NONE; mutex_init(&wl->mutex); /* Apply default driver configuration. */ @@ -5253,6 +5269,7 @@ static int wl1271_free_hw(struct wl1271 *wl) vfree(wl->fw); wl->fw = NULL; + wl->fw_type = WL12XX_FW_TYPE_NONE; kfree(wl->nvs); wl->nvs = NULL; diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 4fbd2a722ffa..cfa6071704c5 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -113,7 +113,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, * In PLT mode we seem to get frames and mac80211 warns about them, * workaround this by not retrieving them at all. */ - if (unlikely(wl->state == WL1271_STATE_PLT)) + if (unlikely(wl->plt)) return -EINVAL; /* the data read starts with the descriptor */ diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index ce3b9a99463e..1c0264ca021f 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -372,3 +372,5 @@ MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL127X_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME); +MODULE_FIRMWARE(WL127X_PLT_FW_NAME); +MODULE_FIRMWARE(WL128X_PLT_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 92caa7ce6053..5c2d4a0ef000 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -435,4 +435,6 @@ MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); MODULE_FIRMWARE(WL127X_FW_NAME); MODULE_FIRMWARE(WL128X_FW_NAME); +MODULE_FIRMWARE(WL127X_PLT_FW_NAME); +MODULE_FIRMWARE(WL128X_PLT_FW_NAME); MODULE_ALIAS("spi:wl1271"); diff --git a/drivers/net/wireless/wl12xx/testmode.c b/drivers/net/wireless/wl12xx/testmode.c index b6b433e5562f..1e93bb9c0246 100644 --- a/drivers/net/wireless/wl12xx/testmode.c +++ b/drivers/net/wireless/wl12xx/testmode.c @@ -274,7 +274,7 @@ static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[]) mutex_lock(&wl->mutex); - if (wl->state != WL1271_STATE_PLT) { + if (!wl->plt) { ret = -EINVAL; goto out; } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 81af416d17da..9b48aa48cdca 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -37,6 +37,8 @@ #define WL127X_FW_NAME "ti-connectivity/wl127x-fw-4-sr.bin" #define WL128X_FW_NAME "ti-connectivity/wl128x-fw-4-sr.bin" +#define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" +#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" /* * wl127x and wl128x are using the same NVS file name. However, the @@ -90,7 +92,12 @@ enum wl1271_state { WL1271_STATE_OFF, WL1271_STATE_ON, - WL1271_STATE_PLT, +}; + +enum wl12xx_fw_type { + WL12XX_FW_TYPE_NONE, + WL12XX_FW_TYPE_NORMAL, + WL12XX_FW_TYPE_PLT, }; enum wl1271_partition_type { @@ -294,6 +301,8 @@ struct wl1271 { spinlock_t wl_lock; enum wl1271_state state; + enum wl12xx_fw_type fw_type; + bool plt; struct mutex mutex; unsigned long flags; From 4549d09c57cf44ae9ab6095c375bad5c100658c7 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 6 Feb 2012 13:07:52 +0200 Subject: [PATCH 47/50] wl12xx: dynamically change fw according to number of active roles wl12xx uses different fw for single-role and multi-role scenarios (due to lack of space, some of the fw advanced features are disabled in the multi-role fw). Add checks on add_interfae and remove_interface in order to determine whether a fw switch is needed (and initiate recovery in this case). Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 114 +++++++++++++++++++++++++-- drivers/net/wireless/wl12xx/sdio.c | 6 +- drivers/net/wireless/wl12xx/spi.c | 6 +- drivers/net/wireless/wl12xx/wl12xx.h | 11 ++- 4 files changed, 124 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index c10940703e8c..b3b4c4019787 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -993,6 +993,35 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) return IRQ_HANDLED; } +struct vif_counter_data { + u8 counter; + + struct ieee80211_vif *cur_vif; + bool cur_vif_running; +}; + +static void wl12xx_vif_count_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct vif_counter_data *counter = data; + + counter->counter++; + if (counter->cur_vif == vif) + counter->cur_vif_running = true; +} + +/* caller must not hold wl->mutex, as it might deadlock */ +static void wl12xx_get_vif_count(struct ieee80211_hw *hw, + struct ieee80211_vif *cur_vif, + struct vif_counter_data *data) +{ + memset(data, 0, sizeof(*data)); + data->cur_vif = cur_vif; + + ieee80211_iterate_active_interfaces(hw, + wl12xx_vif_count_iter, data); +} + static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt) { const struct firmware *fw; @@ -1007,11 +1036,23 @@ static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt) else fw_name = WL127X_PLT_FW_NAME; } else { - fw_type = WL12XX_FW_TYPE_NORMAL; - if (wl->chip.id == CHIP_ID_1283_PG20) - fw_name = WL128X_FW_NAME; - else - fw_name = WL127X_FW_NAME; + /* + * we can't call wl12xx_get_vif_count() here because + * wl->mutex is taken, so use the cached last_vif_count value + */ + if (wl->last_vif_count > 1) { + fw_type = WL12XX_FW_TYPE_MULTI; + if (wl->chip.id == CHIP_ID_1283_PG20) + fw_name = WL128X_FW_NAME_MULTI; + else + fw_name = WL127X_FW_NAME_MULTI; + } else { + fw_type = WL12XX_FW_TYPE_NORMAL; + if (wl->chip.id == CHIP_ID_1283_PG20) + fw_name = WL128X_FW_NAME_SINGLE; + else + fw_name = WL127X_FW_NAME_SINGLE; + } } if (wl->fw_type == fw_type) @@ -2074,11 +2115,47 @@ static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif) return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID; } +/* + * Check whether a fw switch (i.e. moving from one loaded + * fw to another) is needed. This function is also responsible + * for updating wl->last_vif_count, so it must be called before + * loading a non-plt fw (so the correct fw (single-role/multi-role) + * will be used). + */ +static bool wl12xx_need_fw_change(struct wl1271 *wl, + struct vif_counter_data vif_counter_data, + bool add) +{ + enum wl12xx_fw_type current_fw = wl->fw_type; + u8 vif_count = vif_counter_data.counter; + + if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags)) + return false; + + /* increase the vif count if this is a new vif */ + if (add && !vif_counter_data.cur_vif_running) + vif_count++; + + wl->last_vif_count = vif_count; + + /* no need for fw change if the device is OFF */ + if (wl->state == WL1271_STATE_OFF) + return false; + + if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL) + return true; + if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI) + return true; + + return false; +} + static int wl1271_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct vif_counter_data vif_count; int ret = 0; u8 role_type; bool booted = false; @@ -2089,6 +2166,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", ieee80211_vif_type_p2p(vif), vif->addr); + wl12xx_get_vif_count(hw, vif, &vif_count); + mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) @@ -2124,6 +2203,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } + if (wl12xx_need_fw_change(wl, vif_count, true)) { + mutex_unlock(&wl->mutex); + wl1271_recovery_work(&wl->recovery_work); + return 0; + } + /* * TODO: after the nvs issue will be solved, move this block * to start(), and make sure here the driver is ON. @@ -2287,7 +2372,10 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl12xx_vif *iter; + struct vif_counter_data vif_count; + bool cancel_recovery = true; + wl12xx_get_vif_count(hw, vif, &vif_count); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF || @@ -2306,20 +2394,32 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, break; } WARN_ON(iter != wlvif); + if (wl12xx_need_fw_change(wl, vif_count, false)) { + wl12xx_queue_recovery_work(wl); + cancel_recovery = false; + } out: mutex_unlock(&wl->mutex); - cancel_work_sync(&wl->recovery_work); + if (cancel_recovery) + cancel_work_sync(&wl->recovery_work); } static int wl12xx_op_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype new_type, bool p2p) { + struct wl1271 *wl = hw->priv; + int ret; + + set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags); wl1271_op_remove_interface(hw, vif); vif->type = ieee80211_iftype_p2p(new_type, p2p); vif->p2p = p2p; - return wl1271_op_add_interface(hw, vif); + ret = wl1271_op_add_interface(hw, vif); + + clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags); + return ret; } static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 1c0264ca021f..4b3c32774bae 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -370,7 +370,9 @@ module_exit(wl1271_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); -MODULE_FIRMWARE(WL127X_FW_NAME); -MODULE_FIRMWARE(WL128X_FW_NAME); +MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); +MODULE_FIRMWARE(WL127X_FW_NAME_MULTI); MODULE_FIRMWARE(WL127X_PLT_FW_NAME); +MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE); +MODULE_FIRMWARE(WL128X_FW_NAME_MULTI); MODULE_FIRMWARE(WL128X_PLT_FW_NAME); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 5c2d4a0ef000..2fc18a8dcce8 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -433,8 +433,10 @@ module_exit(wl1271_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); -MODULE_FIRMWARE(WL127X_FW_NAME); -MODULE_FIRMWARE(WL128X_FW_NAME); +MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); +MODULE_FIRMWARE(WL127X_FW_NAME_MULTI); MODULE_FIRMWARE(WL127X_PLT_FW_NAME); +MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE); +MODULE_FIRMWARE(WL128X_FW_NAME_MULTI); MODULE_FIRMWARE(WL128X_PLT_FW_NAME); MODULE_ALIAS("spi:wl1271"); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9b48aa48cdca..04cddaaf9498 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -35,8 +35,12 @@ #include "conf.h" #include "ini.h" -#define WL127X_FW_NAME "ti-connectivity/wl127x-fw-4-sr.bin" -#define WL128X_FW_NAME "ti-connectivity/wl128x-fw-4-sr.bin" +#define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin" +#define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin" + +#define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin" +#define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin" + #define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" #define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" @@ -97,6 +101,7 @@ enum wl1271_state { enum wl12xx_fw_type { WL12XX_FW_TYPE_NONE, WL12XX_FW_TYPE_NORMAL, + WL12XX_FW_TYPE_MULTI, WL12XX_FW_TYPE_PLT, }; @@ -254,6 +259,7 @@ enum wl12xx_flags { WL1271_FLAG_PENDING_WORK, WL1271_FLAG_SOFT_GEMINI, WL1271_FLAG_RECOVERY_IN_PROGRESS, + WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, }; enum wl12xx_vif_flags { @@ -303,6 +309,7 @@ struct wl1271 { enum wl1271_state state; enum wl12xx_fw_type fw_type; bool plt; + u8 last_vif_count; struct mutex mutex; unsigned long flags; From 3dee43930fb48a0746621c99d46318aad8169980 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 6 Feb 2012 12:47:56 +0200 Subject: [PATCH 48/50] wl12xx: enter forced-psm on fw change Enter forced psm when changing fw, in order to make the sta a bit more disconnection-persistent. (DPM doesn't know about the incoming recovery, so it won't enter psm by itself) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b3b4c4019787..10c1ecdf9bab 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2150,6 +2150,19 @@ static bool wl12xx_need_fw_change(struct wl1271 *wl, return false; } +/* + * Enter "forced psm". Make sure the sta is in psm against the ap, + * to make the fw switch a bit more disconnection-persistent. + */ +static void wl12xx_force_active_psm(struct wl1271 *wl) +{ + struct wl12xx_vif *wlvif; + + wl12xx_for_each_wlvif_sta(wl, wlvif) { + wl1271_ps_set_mode(wl, wlvif, STATION_POWER_SAVE_MODE); + } +} + static int wl1271_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -2204,6 +2217,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, } if (wl12xx_need_fw_change(wl, vif_count, true)) { + wl12xx_force_active_psm(wl); mutex_unlock(&wl->mutex); wl1271_recovery_work(&wl->recovery_work); return 0; @@ -2395,6 +2409,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, } WARN_ON(iter != wlvif); if (wl12xx_need_fw_change(wl, vif_count, false)) { + wl12xx_force_active_psm(wl); wl12xx_queue_recovery_work(wl); cancel_recovery = false; } From 9b44350b495a6ec94f59073c5a4e09e80a818304 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Mon, 6 Feb 2012 12:47:57 +0200 Subject: [PATCH 49/50] wl12xx: delete wl->vif (and allow multiple vifs) Delete the global wl->vif (and the checks on it), so multiple vifs could be added. Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 11 ----------- drivers/net/wireless/wl12xx/wl12xx.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 10c1ecdf9bab..fc8dfbfe1d89 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2186,13 +2186,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, if (ret < 0) goto out_unlock; - if (wl->vif) { - wl1271_debug(DEBUG_MAC80211, - "multiple vifs are not supported yet"); - ret = -EBUSY; - goto out; - } - /* * in some very corner case HW recovery scenarios its possible to * get here before __wl1271_op_remove_interface is complete, so @@ -2265,7 +2258,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, if (ret < 0) goto out; - wl->vif = vif; list_add(&wlvif->list, &wl->wlvif_list); set_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags); @@ -2298,8 +2290,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, if (!test_and_clear_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)) return; - wl->vif = NULL; - /* because of hardware recovery, we may get here twice */ if (wl->state != WL1271_STATE_ON) return; @@ -5291,7 +5281,6 @@ static struct ieee80211_hw *wl1271_alloc_hw(void) wl->rx_counter = 0; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->band = IEEE80211_BAND_2GHZ; - wl->vif = NULL; wl->flags = 0; wl->sg_enabled = true; wl->hw_pg_ver = -1; diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 04cddaaf9498..90352415e2aa 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -445,8 +445,6 @@ struct wl1271 { struct wl12xx_fw_status *fw_status; struct wl1271_tx_hw_res_if *tx_res_if; - struct ieee80211_vif *vif; - /* Current chipset configuration */ struct conf_drv_settings conf; From 51c4ed95a857ded6017537fd53fa00db64908954 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 8 Feb 2012 12:20:04 +0300 Subject: [PATCH 50/50] wl12xx: remove some dead code in wl1271_plt_init() "ret" has already been checked at this point, and we don't need to check it again. This was left around from a previous patch 49d750ca14 "wl12xx: 1281/1283 support - New radio structs and functions". Signed-off-by: Dan Carpenter Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index fc8dfbfe1d89..adf9bbcf88fd 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -680,8 +680,6 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) return ret; } - if (ret < 0) - return ret; /* Chip-specific initializations */ ret = wl1271_chip_specific_init(wl);