2009-10-08 18:58:59

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 00/20] First batch of new patches for wl1271

Hi John et al,

We have finally upgraded our internal kernel to 2.6.32, so I now had the time
to continue working on forward porting the patches we have been working on
internally, on top of 2.6.28. There are 61 patches in total, so I decided to
break them down into separate patch sets, so that it's a bit easier to review.

Here's the first batch. There are multiple fixes, improvements and updates for
a new firmware release (which has some API changes).

I'll wait until these patches cook a little bit before I send the next bunch.

Cheers,
Luca.

Juuso Oikarinen (17):
wl1271: Correction to TX block allocation calculation
wl1271: Security sequence number handling for TX (for WPA)
wl1271: Correct TKIP header space handling in TX path
wl1271: Implement delayed entry into ELP
wl1271: mask aid bits 14 and 15 in ps-poll template
wl1271: Implementation for SPI busy word checking
wl1271: Configure rate policies based on AP rates
wl1271: Update join usage
wl1271: Corrections to TX path
wl1271: use workqueue provided by mac80211 instead of the default
wl1271: Clear probe-request template after scan
wl1271: Multicast filtering configuration
wl1271: Use vmalloc to allocate memory for firmware
wl1271: Add connection monitoring configuration
wl1271: Enable beacon filtering with the stack
wl1271: Configure beacon filtering on if PSM used
wl1271: Mask unneeded events from firmware to conserve power

Luciano Coelho (3):
wl1271: remove unecessary qual parameter from rx status
wl1271: added Juuso Oikarinen as module author
wl1271: hack to disable filters

drivers/net/wireless/wl12xx/wl1271.h | 42 +++++-
drivers/net/wireless/wl12xx/wl1271_acx.c | 54 +++++--
drivers/net/wireless/wl12xx/wl1271_acx.h | 29 +++-
drivers/net/wireless/wl12xx/wl1271_boot.c | 21 ++-
drivers/net/wireless/wl12xx/wl1271_cmd.c | 40 +++--
drivers/net/wireless/wl12xx/wl1271_cmd.h | 6 +-
drivers/net/wireless/wl12xx/wl1271_event.c | 18 ++-
drivers/net/wireless/wl12xx/wl1271_init.c | 13 ++-
drivers/net/wireless/wl12xx/wl1271_main.c | 244 +++++++++++++++++++++++-----
drivers/net/wireless/wl12xx/wl1271_ps.c | 57 +++++--
drivers/net/wireless/wl12xx/wl1271_ps.h | 2 +-
drivers/net/wireless/wl12xx/wl1271_rx.c | 6 -
drivers/net/wireless/wl12xx/wl1271_spi.c | 69 ++++++++-
drivers/net/wireless/wl12xx/wl1271_tx.c | 48 ++++--
14 files changed, 517 insertions(+), 132 deletions(-)



2009-10-08 18:58:56

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 16/20] wl1271: Use vmalloc to allocate memory for firmware

From: Juuso Oikarinen <[email protected]>

Use vmalloc to allocate memory for the firmware image, and use a smaller
linear buffer for the actual transfer of the firmware to the chipset.

This patch is an adaptation of a similar patch for wl1251 by Kalle Valo.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_boot.c | 16 ++++++++++++----
drivers/net/wireless/wl12xx/wl1271_main.c | 5 +++--
2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 8228ef4..7640313 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -94,7 +94,7 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
size_t fw_data_len, u32 dest)
{
int addr, chunk_num, partition_limit;
- u8 *p;
+ u8 *p, *chunk;

/* whal_FwCtrl_LoadFwImageSm() */

@@ -103,12 +103,17 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d",
fw_data_len, CHUNK_SIZE);

-
if ((fw_data_len % 4) != 0) {
wl1271_error("firmware length not multiple of four");
return -EIO;
}

+ chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ wl1271_error("allocation for firmware upload chunk failed");
+ return -ENOMEM;
+ }
+
wl1271_set_partition(wl, dest,
part_table[PART_DOWN].mem.size,
part_table[PART_DOWN].reg.start,
@@ -137,9 +142,10 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
/* 10.3 upload the chunk */
addr = dest + chunk_num * CHUNK_SIZE;
p = buf + chunk_num * CHUNK_SIZE;
+ memcpy(chunk, p, CHUNK_SIZE);
wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x",
p, addr);
- wl1271_spi_mem_write(wl, addr, p, CHUNK_SIZE);
+ wl1271_spi_mem_write(wl, addr, chunk, CHUNK_SIZE);

chunk_num++;
}
@@ -147,10 +153,12 @@ static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf,
/* 10.4 upload the last chunk */
addr = dest + chunk_num * CHUNK_SIZE;
p = buf + chunk_num * CHUNK_SIZE;
+ memcpy(chunk, p, fw_data_len % CHUNK_SIZE);
wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x",
fw_data_len % CHUNK_SIZE, p, addr);
- wl1271_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE);
+ wl1271_spi_mem_write(wl, addr, chunk, fw_data_len % CHUNK_SIZE);

+ kfree(chunk);
return 0;
}

diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 09fe968..d22de23 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -30,6 +30,7 @@
#include <linux/spi/spi.h>
#include <linux/crc32.h>
#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
#include <linux/spi/wl12xx.h>

#include "wl1271.h"
@@ -231,7 +232,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
}

wl->fw_len = fw->size;
- wl->fw = kmalloc(wl->fw_len, GFP_KERNEL);
+ wl->fw = vmalloc(wl->fw_len);

if (!wl->fw) {
wl1271_error("could not allocate memory for the firmware");
@@ -1484,7 +1485,7 @@ static int __devexit wl1271_remove(struct spi_device *spi)
platform_device_unregister(&wl1271_device);
free_irq(wl->irq, wl);
kfree(wl->target_mem_map);
- kfree(wl->fw);
+ vfree(wl->fw);
wl->fw = NULL;
kfree(wl->nvs);
wl->nvs = NULL;
--
1.5.6.5


2009-10-08 18:59:40

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 06/20] wl1271: mask aid bits 14 and 15 in ps-poll template

From: Juuso Oikarinen <[email protected]>

In ps-poll template aid bits 14 and 15 were not masked as required by
the standard. Mask them so that aid is sent in correct format.

This patch is a direct port of the respective patch for the wl1251
driver by Kalle Valo.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Vidhya Govindan <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_cmd.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 1ee1b2b..bd65b38 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -682,7 +682,10 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)

memcpy(template.bssid, wl->bssid, ETH_ALEN);
memcpy(template.ta, wl->mac_addr, ETH_ALEN);
- template.aid = aid;
+
+ /* aid in PS-Poll has its two MSBs each set to 1 */
+ template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid);
+
template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);

return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template,
--
1.5.6.5


2009-10-08 18:58:33

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 08/20] wl1271: Configure rate policies based on AP rates

From: Juuso Oikarinen <[email protected]>

Configure the rate policies to the firmware based on the rates given by
the AP.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 3 ++
drivers/net/wireless/wl12xx/wl1271_acx.c | 4 +-
drivers/net/wireless/wl12xx/wl1271_acx.h | 2 +-
drivers/net/wireless/wl12xx/wl1271_init.c | 2 +-
drivers/net/wireless/wl12xx/wl1271_main.c | 31 +++++++++++++++++++++++++++++
5 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 957da8c..671dc5a 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -369,6 +369,9 @@ struct wl1271 {
/* Our association ID */
u16 aid;

+ /* The current band */
+ enum ieee80211_band band;
+
/* Default key (for WEP) */
u32 default_key;

diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index f622a40..2ae1081 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -703,7 +703,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
return 0;
}

-int wl1271_acx_rate_policies(struct wl1271 *wl)
+int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates)
{
struct acx_rate_policy *acx;
int ret = 0;
@@ -719,7 +719,7 @@ int wl1271_acx_rate_policies(struct wl1271 *wl)

/* configure one default (one-size-fits-all) rate class */
acx->rate_class_cnt = 1;
- acx->rate_class[0].enabled_rates = ACX_RATE_MASK_ALL;
+ acx->rate_class[0].enabled_rates = enabled_rates;
acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT;
acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT;
acx->rate_class[0].aflags = 0;
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index 9068daa..55850eb 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -1209,7 +1209,7 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl,
enum acx_ctsprotect_type ctsprotect);
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
-int wl1271_acx_rate_policies(struct wl1271 *wl);
+int wl1271_acx_rate_policies(struct wl1271 *wl, u32 enabled_rates);
int wl1271_acx_ac_cfg(struct wl1271 *wl);
int wl1271_acx_tid_cfg(struct wl1271 *wl);
int wl1271_acx_frag_threshold(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index 490df21..eb6b91a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -369,7 +369,7 @@ int wl1271_hw_init(struct wl1271 *wl)
goto out_free_memmap;

/* Configure TX rate classes */
- ret = wl1271_acx_rate_policies(wl);
+ ret = wl1271_acx_rate_policies(wl, ACX_RATE_MASK_ALL);
if (ret < 0)
goto out_free_memmap;

diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index cc5c328..b9b1c20 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -583,6 +583,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl->ssid_len = 0;
wl->listen_int = 1;
wl->bss_type = MAX_BSS_TYPE;
+ wl->band = IEEE80211_BAND_2GHZ;

wl->rx_counter = 0;
wl->elp = false;
@@ -727,6 +728,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)

mutex_lock(&wl->mutex);

+ wl->band = conf->channel->band;
+
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
@@ -978,6 +981,22 @@ out:
return ret;
}

+static u32 wl1271_enabled_rates_get(struct wl1271 *wl, u64 basic_rate_set)
+{
+ struct ieee80211_supported_band *band;
+ u32 enabled_rates = 0;
+ int bit;
+
+ band = wl->hw->wiphy->bands[wl->band];
+ for (bit = 0; bit < band->n_bitrates; bit++) {
+ if (basic_rate_set & 0x1)
+ enabled_rates |= band->bitrates[bit].hw_value;
+ basic_rate_set >>= 1;
+ }
+
+ return enabled_rates;
+}
+
static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *bss_conf,
@@ -1016,6 +1035,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
}
}
}
+
if (changed & BSS_CHANGED_ERP_SLOT) {
if (bss_conf->use_short_slot)
ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT);
@@ -1045,6 +1065,16 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
}
}

+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ u32 enabled_rates = wl1271_enabled_rates_get(
+ wl, bss_conf->basic_rates);
+ ret = wl1271_acx_rate_policies(wl, enabled_rates);
+ if (ret < 0) {
+ wl1271_warning("Set rate policies failed %d", ret);
+ goto out_sleep;
+ }
+ }
+
out_sleep:
wl1271_ps_elp_sleep(wl);

@@ -1239,6 +1269,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
wl->psm_requested = false;
wl->tx_queue_stopped = false;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
+ wl->band = IEEE80211_BAND_2GHZ;

/* We use the default power on sleep time until we know which chip
* we're using */
--
1.5.6.5


2009-10-08 18:59:36

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 03/20] wl1271: Security sequence number handling for TX (for WPA)

From: Juuso Oikarinen <[email protected]>

Add security sequence number handling to the driver TX data path needed
by WPA.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Kalle Valo <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 5 +++++
drivers/net/wireless/wl12xx/wl1271_cmd.c | 11 +++++++++--
drivers/net/wireless/wl12xx/wl1271_cmd.h | 3 ++-
drivers/net/wireless/wl12xx/wl1271_main.c | 13 +++++++++++--
drivers/net/wireless/wl12xx/wl1271_tx.c | 11 +++++++++++
5 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 55818f9..e575dcc 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -337,6 +337,11 @@ struct wl1271 {
/* Pending TX frames */
struct sk_buff *tx_frames[16];

+ /* Security sequence number counters */
+ u8 tx_security_last_seq;
+ u16 tx_security_seq_16;
+ u32 tx_security_seq_32;
+
/* FW Rx counter */
u32 rx_counter;

diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 2a4351f..1ee1b2b 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -228,6 +228,10 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,

join->ctrl |= wl->session_counter << WL1271_JOIN_CMD_TX_SESSION_OFFSET;

+ /* reset TX security counters */
+ wl->tx_security_last_seq = 0;
+ wl->tx_security_seq_16 = 0;
+ wl->tx_security_seq_32 = 0;

ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join));
if (ret < 0) {
@@ -759,7 +763,8 @@ out:
}

int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
- u8 key_size, const u8 *key, const u8 *addr)
+ u8 key_size, const u8 *key, const u8 *addr,
+ u32 tx_seq_32, u16 tx_seq_16)
{
struct wl1271_cmd_set_keys *cmd;
int ret = 0;
@@ -777,12 +782,14 @@ int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
cmd->key_size = key_size;
cmd->key_type = key_type;

+ cmd->ac_seq_num16[0] = tx_seq_16;
+ cmd->ac_seq_num32[0] = tx_seq_32;
+
/* we have only one SSID profile */
cmd->ssid_profile = 0;

cmd->id = id;

- /* FIXME: this is from wl1251, needs to be checked */
if (key_type == KEY_TKIP) {
/*
* We get the key in the following form:
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h
index 951a844..7c4d3aa 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.h
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h
@@ -49,7 +49,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len);
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
- u8 key_size, const u8 *key, const u8 *addr);
+ u8 key_size, const u8 *key, const u8 *addr,
+ u32 tx_seq_32, u16 tx_seq_16);

enum wl1271_commands {
CMD_INTERROGATE = 1, /*use this to read information elements*/
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 27298b1..bedd19b 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -592,6 +592,9 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
wl->tx_blocks_available = 0;
wl->tx_results_count = 0;
wl->tx_packets_count = 0;
+ wl->tx_security_last_seq = 0;
+ wl->tx_security_seq_16 = 0;
+ wl->tx_security_seq_32 = 0;
wl->time_offset = 0;
wl->session_counter = 0;
for (i = 0; i < NUM_TX_QUEUES; i++)
@@ -823,6 +826,8 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct wl1271 *wl = hw->priv;
const u8 *addr;
int ret;
+ u32 tx_seq_32 = 0;
+ u16 tx_seq_16 = 0;
u8 key_type;

static const u8 bcast_addr[ETH_ALEN] =
@@ -861,11 +866,15 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
key_type = KEY_TKIP;

key_conf->hw_key_idx = key_conf->keyidx;
+ tx_seq_32 = wl->tx_security_seq_32;
+ tx_seq_16 = wl->tx_security_seq_16;
break;
case ALG_CCMP:
key_type = KEY_AES;

key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ tx_seq_32 = wl->tx_security_seq_32;
+ tx_seq_16 = wl->tx_security_seq_16;
break;
default:
wl1271_error("Unknown key algo 0x%x", key_conf->alg);
@@ -879,7 +888,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
- addr);
+ addr, tx_seq_32, tx_seq_16);
if (ret < 0) {
wl1271_error("Could not add or replace key");
goto out_sleep;
@@ -890,7 +899,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
- addr);
+ addr, 0, 0);
if (ret < 0) {
wl1271_error("Could not remove key");
goto out_sleep;
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index 0c19688..162f026 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -258,6 +258,7 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
struct ieee80211_tx_info *info;
struct sk_buff *skb;
u32 header_len;
+ u16 seq;
int id = result->id;

/* check for id legality */
@@ -284,6 +285,16 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
/* info->status.retry_count = result->ack_failures; */
wl->stats.retry_count += result->ack_failures;

+ /* update security sequence number */
+ seq = wl->tx_security_seq_16 +
+ (result->lsb_security_sequence_number -
+ wl->tx_security_last_seq);
+ wl->tx_security_last_seq = result->lsb_security_sequence_number;
+
+ if (seq < wl->tx_security_seq_16)
+ wl->tx_security_seq_32++;
+ wl->tx_security_seq_16 = seq;
+
/* get header len */
if (info->control.hw_key &&
info->control.hw_key->alg == ALG_TKIP)
--
1.5.6.5


2009-10-08 18:58:48

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 20/20] wl1271: Mask unneeded events from firmware to conserve power

From: Juuso Oikarinen <[email protected]>

Currently several events are enabled from the firmware for which there
is no handling. This wakes up the host unnecessarily. Mask those
unneeded events.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_boot.c | 5 +++--
1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c
index 7640313..140e943 100644
--- a/drivers/net/wireless/wl12xx/wl1271_boot.c
+++ b/drivers/net/wireless/wl12xx/wl1271_boot.c
@@ -390,8 +390,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
/* enable gpio interrupts */
wl1271_boot_enable_interrupts(wl);

- /* unmask all mbox events */
- wl->event_mask = 0xffffffff;
+ /* unmask required mbox events */
+ wl->event_mask = BSS_LOSE_EVENT_ID |
+ SCAN_COMPLETE_EVENT_ID;

ret = wl1271_event_unmask(wl);
if (ret < 0) {
--
1.5.6.5


2009-10-08 18:59:02

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 19/20] wl1271: Configure beacon filtering on if PSM used

From: Juuso Oikarinen <[email protected]>

Enable beacon filtering when PSM is enabled

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_acx.c | 11 +++++++----
drivers/net/wireless/wl12xx/wl1271_acx.h | 7 ++++++-
drivers/net/wireless/wl12xx/wl1271_init.c | 3 ++-
drivers/net/wireless/wl12xx/wl1271_main.c | 3 ++-
drivers/net/wireless/wl12xx/wl1271_ps.c | 11 +++++++++++
5 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index 4b5fd94..b9dfa09 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -386,7 +386,7 @@ out:
return ret;
}

-int wl1271_acx_beacon_filter_opt(struct wl1271 *wl)
+int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter)
{
struct acx_beacon_filter_option *beacon_filter;
int ret;
@@ -399,7 +399,7 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl)
goto out;
}

- beacon_filter->enable = 0;
+ beacon_filter->enable = enable_filter;
beacon_filter->max_num_beacons = 0;

ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_OPT,
@@ -417,6 +417,7 @@ out:
int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
{
struct acx_beacon_filter_ie_table *ie_table;
+ int idx = 0;
int ret;

wl1271_debug(DEBUG_ACX, "acx beacon filter table");
@@ -427,8 +428,10 @@ int wl1271_acx_beacon_filter_table(struct wl1271 *wl)
goto out;
}

- ie_table->num_ie = 0;
- memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE);
+ /* configure default beacon pass-through rules */
+ ie_table->num_ie = 1;
+ ie_table->table[idx++] = BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN;
+ ie_table->table[idx++] = BEACON_RULE_PASS_ON_APPEARANCE;

ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_TABLE,
ie_table, sizeof(*ie_table));
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index bb21bcb..0c2a107 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -398,6 +398,11 @@ struct acx_beacon_filter_option {
(BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \
BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE))

+#define BEACON_RULE_PASS_ON_CHANGE BIT(0)
+#define BEACON_RULE_PASS_ON_APPEARANCE BIT(1)
+
+#define BEACON_FILTER_IE_ID_CHANNEL_SWITCH_ANN (37)
+
struct acx_beacon_filter_ie_table {
struct acx_header header;

@@ -1206,7 +1211,7 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
void *mc_list, u32 mc_list_len);
int wl1271_acx_service_period_timeout(struct wl1271 *wl);
int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold);
-int wl1271_acx_beacon_filter_opt(struct wl1271 *wl);
+int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
int wl1271_acx_conn_monit_params(struct wl1271 *wl);
int wl1271_acx_sg_enable(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index d3db3fb..bf4d0e1 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -136,7 +136,8 @@ static int wl1271_init_beacon_filter(struct wl1271 *wl)
{
int ret;

- ret = wl1271_acx_beacon_filter_opt(wl);
+ /* disable beacon filtering at this stage */
+ ret = wl1271_acx_beacon_filter_opt(wl, false);
if (ret < 0)
return ret;

diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index e6f9e9b..3662715 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1319,7 +1319,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->channel_change_time = 10000;

wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
- IEEE80211_HW_NOISE_DBM;
+ IEEE80211_HW_NOISE_DBM |
+ IEEE80211_HW_BEACON_FILTER;

wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
wl->hw->wiphy->max_scan_ssids = 1;
diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c
index 0f6ea16..5580e53 100644
--- a/drivers/net/wireless/wl12xx/wl1271_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1271_ps.c
@@ -129,6 +129,12 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
switch (mode) {
case STATION_POWER_SAVE_MODE:
wl1271_debug(DEBUG_PSM, "entering psm");
+
+ /* enable beacon filtering */
+ ret = wl1271_acx_beacon_filter_opt(wl, true);
+ if (ret < 0)
+ return ret;
+
ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE);
if (ret < 0)
return ret;
@@ -146,6 +152,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode)
if (ret < 0)
return ret;

+ /* disable beacon filtering */
+ ret = wl1271_acx_beacon_filter_opt(wl, false);
+ if (ret < 0)
+ return ret;
+
ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE);
if (ret < 0)
return ret;
--
1.5.6.5


2009-10-08 18:58:38

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 14/20] wl1271: Clear probe-request template after scan

From: Juuso Oikarinen <[email protected]>

Clear the probe-request template on the firmware after scan. Unless
cleared, the firmware can independently send probe requests to the AP
and interfere with the mac80211 logic.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_event.c | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c
index f3afd4a..87055f7 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.c
+++ b/drivers/net/wireless/wl12xx/wl1271_event.c
@@ -26,6 +26,7 @@
#include "wl1271_spi.h"
#include "wl1271_event.h"
#include "wl1271_ps.h"
+#include "wl12xx_80211.h"

static int wl1271_event_scan_complete(struct wl1271 *wl,
struct event_mailbox *mbox)
@@ -34,6 +35,9 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
mbox->scheduled_scan_status);

if (wl->scanning) {
+ int size = sizeof(struct wl12xx_probe_req_template);
+ wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL,
+ size);
mutex_unlock(&wl->mutex);
ieee80211_scan_completed(wl->hw, false);
mutex_lock(&wl->mutex);
--
1.5.6.5


2009-10-08 18:58:32

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 10/20] wl1271: Corrections to TX path

From: Juuso Oikarinen <[email protected]>

Corrections to the TX path - use correct number of maximum descriptors
(32 instead of 16) and correct checking and setting of excessive retries
on completion.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 4 +++-
drivers/net/wireless/wl12xx/wl1271_acx.h | 1 -
drivers/net/wireless/wl12xx/wl1271_main.c | 4 +---
drivers/net/wireless/wl12xx/wl1271_tx.c | 8 +++-----
4 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 05eb29c..0b4744d 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -123,6 +123,8 @@ enum {
#define WL1271_DEFAULT_BEACON_INT 100
#define WL1271_DEFAULT_DTIM_PERIOD 1

+#define ACX_TX_DESCRIPTORS 32
+
enum wl1271_state {
WL1271_STATE_OFF,
WL1271_STATE_ON,
@@ -346,7 +348,7 @@ struct wl1271 {
struct work_struct filter_work;

/* Pending TX frames */
- struct sk_buff *tx_frames[16];
+ struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];

/* Security sequence number counters */
u8 tx_security_last_seq;
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index 55850eb..c177345 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -170,7 +170,6 @@ enum {
#define DP_RX_PACKET_RING_CHUNK_NUM 2
#define DP_TX_PACKET_RING_CHUNK_NUM 2
#define DP_TX_COMPLETE_TIME_OUT 20
-#define FW_TX_CMPLT_BLOCK_SIZE 16

#define TX_MSDU_LIFETIME_MIN 0
#define TX_MSDU_LIFETIME_MAX 3000
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 6e35fcf..0a0f2ea 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1293,9 +1293,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
wl->band = IEEE80211_BAND_2GHZ;

- /* We use the default power on sleep time until we know which chip
- * we're using */
- for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
+ for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
wl->tx_frames[i] = NULL;

spin_lock_init(&wl->wl_lock);
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index 1ad1bc3..5d3aa4b 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -33,8 +33,7 @@
static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb)
{
int i;
-
- for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
+ for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
if (wl->tx_frames[i] == NULL) {
wl->tx_frames[i] = skb;
return i;
@@ -262,14 +261,13 @@ out:
static void wl1271_tx_complete_packet(struct wl1271 *wl,
struct wl1271_tx_hw_res_descr *result)
{
-
struct ieee80211_tx_info *info;
struct sk_buff *skb;
u16 seq;
int id = result->id;

/* check for id legality */
- if (id >= TX_HW_RESULT_QUEUE_LEN || wl->tx_frames[id] == NULL) {
+ if (id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL) {
wl1271_warning("TX result illegal id: %d", id);
return;
}
@@ -382,7 +380,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
ieee80211_tx_status(wl->hw, skb);
}

- for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
+ for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
if (wl->tx_frames[i] != NULL) {
skb = wl->tx_frames[i];
info = IEEE80211_SKB_CB(skb);
--
1.5.6.5


2009-10-08 20:17:37

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 15/20] wl1271: Multicast filtering configuration

Hi Johannes,

ext Johannes Berg wrote:
> On Thu, 2009-10-08 at 21:56 +0300, Luciano Coelho wrote:
>
>> + /* first, get the filter parameters */
>> + spin_lock_irqsave(&wl->wl_lock, flags);
>> + fp = wl->filter_params;
>> + wl->filter_params = NULL;
>> + spin_unlock_irqrestore(&wl->wl_lock, flags);
>> +
>> + if (!fp)
>> + return;
>
>> + kfree(fp);
>
>> +static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
>> + struct dev_addr_list *mc_list)
>> +{
>> + struct wl1271 *wl = hw->priv;
>> + struct wl1271_filter_params *fp;
>> + unsigned long flags;
>> + int i;
>> +
>> + /*
>> + * FIXME: we should return a hash that will be passed to
>> + * configure_filter() instead of saving everything in the context.
>> + */
>> +
>> + fp = kzalloc(sizeof(*fp), GFP_KERNEL);
>> + if (!fp) {
>> + wl1271_error("Out of memory setting filters.");
>> + return 0;
>> + }
>
> It's a u64 on purpose, you can return the parameters struct to pass it
> through to configure_filter.

Yes, you're right. It's just that I have forwarded-ported loads of patches at
once, so I just made a quick hack to get this to compile and work before the
real thing is done. Juuso Oikarinen (who is also working on the wl1271) has
already implemented the final solution for this and his patch will be included
in one of my next batches ;)

Thanks a lot for your timely comment (as usual).

--
Cheers,
Luca.

2009-10-08 18:59:49

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 18/20] wl1271: Enable beacon filtering with the stack

From: Juuso Oikarinen <[email protected]>

Enable beacon filtering with the mac80211 stack.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 2 ++
drivers/net/wireless/wl12xx/wl1271_event.c | 14 ++++++++------
drivers/net/wireless/wl12xx/wl1271_main.c | 12 ++++++++++++
3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 34a52b3..96a5813 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -420,6 +420,8 @@ struct wl1271 {

struct wl1271_fw_status *fw_status;
struct wl1271_tx_hw_res_if *tx_res_if;
+
+ struct ieee80211_vif *vif;
};

int wl1271_plt_start(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c
index 87055f7..f329276 100644
--- a/drivers/net/wireless/wl12xx/wl1271_event.c
+++ b/drivers/net/wireless/wl12xx/wl1271_event.c
@@ -70,14 +70,16 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
return ret;
}

- if (vector & BSS_LOSE_EVENT_ID) {
+ /*
+ * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon
+ * filtering) is enabled. Without PSM, the stack will receive all
+ * beacons and can detect beacon loss by itself.
+ */
+ if (vector & BSS_LOSE_EVENT_ID && wl->psm) {
wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT");

- if (wl->psm_requested && wl->psm) {
- ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE);
- if (ret < 0)
- return ret;
- }
+ /* indicate to the stack, that beacons have been lost */
+ ieee80211_beacon_loss(wl->vif);
}

return 0;
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index d22de23..e6f9e9b 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -665,6 +665,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
conf->type, conf->mac_addr);

mutex_lock(&wl->mutex);
+ if (wl->vif) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ wl->vif = conf->vif;

switch (conf->type) {
case NL80211_IFTYPE_STATION:
@@ -688,7 +694,12 @@ out:
static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
struct ieee80211_if_init_conf *conf)
{
+ struct wl1271 *wl = hw->priv;
+
+ mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
+ wl->vif = NULL;
+ mutex_unlock(&wl->mutex);
}

#if 0
@@ -1382,6 +1393,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
wl->dtim_period = WL1271_DEFAULT_DTIM_PERIOD;
wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
wl->band = IEEE80211_BAND_2GHZ;
+ wl->vif = NULL;

for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
wl->tx_frames[i] = NULL;
--
1.5.6.5


2009-10-08 18:58:21

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 01/20] wl1271: remove unecessary qual parameter from rx status

The qual element in ieee80211_rx_status is not used anymore, so we don't need
to set it in the wl1271_rx_status() function. This saves a bit of time in
the RX path.

Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_rx.c | 6 ------
1 files changed, 0 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c
index ad8b690..5d8d401 100644
--- a/drivers/net/wireless/wl12xx/wl1271_rx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_rx.c
@@ -91,12 +91,6 @@ static void wl1271_rx_status(struct wl1271 *wl,
*/
status->signal = desc->rssi;

- /* FIXME: Should this be optimized? */
- status->qual = (desc->rssi - WL1271_RX_MIN_RSSI) * 100 /
- (WL1271_RX_MAX_RSSI - WL1271_RX_MIN_RSSI);
- status->qual = min(status->qual, 100);
- status->qual = max(status->qual, 0);
-
/*
* FIXME: In wl1251, the SNR should be divided by two. In wl1271 we
* need to divide by two for now, but TI has been discussing about
--
1.5.6.5


2009-10-08 18:58:29

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 07/20] wl1271: Implementation for SPI busy word checking

From: Juuso Oikarinen <[email protected]>

This patch adds implementation for checking for SPI busy words - i.e.
honoring a delay request from the WLAN chipset upon reading
registers/memory.

To optimized the average SPI ready by 32 bits, also configure the number
of busywords to one to disable the "fixed-busy-word" functionality.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Vidhya Govindan <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 10 +++-
drivers/net/wireless/wl12xx/wl1271_spi.c | 69 +++++++++++++++++++++++++++++-
2 files changed, 75 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index c455dcb..957da8c 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -107,7 +107,13 @@ enum {
#define WL1271_FW_NAME "wl1271-fw.bin"
#define WL1271_NVS_NAME "wl1271-nvs.bin"

-#define WL1271_BUSY_WORD_LEN 8
+/*
+ * FIXME: for the wl1271, a busy word count of 1 here will result in a more
+ * optimal SPI interface. There is some SPI bug however, causing RXS time outs
+ * with this mode occasionally on boot, so lets have two for now.
+ */
+#define WL1271_BUSY_WORD_CNT 2
+#define WL1271_BUSY_WORD_LEN (WL1271_BUSY_WORD_CNT * sizeof(u32))

#define WL1271_ELP_HW_STATE_ASLEEP 0
#define WL1271_ELP_HW_STATE_IRQ 1
@@ -389,7 +395,7 @@ struct wl1271 {

u32 buffer_32;
u32 buffer_cmd;
- u8 buffer_busyword[WL1271_BUSY_WORD_LEN];
+ u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
struct wl1271_rx_descriptor *rx_descriptor;

struct wl1271_fw_status *fw_status;
diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c
index 4a12880..504991a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_spi.c
+++ b/drivers/net/wireless/wl12xx/wl1271_spi.c
@@ -244,12 +244,75 @@ int wl1271_set_partition(struct wl1271 *wl,
return 0;
}

+#define WL1271_BUSY_WORD_TIMEOUT 1000
+
+void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len)
+{
+ struct spi_transfer t[1];
+ struct spi_message m;
+ u32 *busy_buf;
+ int num_busy_bytes = 0;
+
+ wl1271_info("spi read BUSY!");
+
+ /*
+ * Look for the non-busy word in the read buffer, and if found,
+ * read in the remaining data into the buffer.
+ */
+ busy_buf = (u32 *)buf;
+ for (; (u32)busy_buf < (u32)buf + len; busy_buf++) {
+ num_busy_bytes += sizeof(u32);
+ if (*busy_buf & 0x1) {
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+ memmove(buf, busy_buf, len - num_busy_bytes);
+ t[0].rx_buf = buf + (len - num_busy_bytes);
+ t[0].len = num_busy_bytes;
+ spi_message_add_tail(&t[0], &m);
+ spi_sync(wl->spi, &m);
+ return;
+ }
+ }
+
+ /*
+ * Read further busy words from SPI until a non-busy word is
+ * encountered, then read the data itself into the buffer.
+ */
+ wl1271_info("spi read BUSY-polling needed!");
+
+ num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT;
+ busy_buf = wl->buffer_busyword;
+ while (num_busy_bytes) {
+ num_busy_bytes--;
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+ t[0].rx_buf = busy_buf;
+ t[0].len = sizeof(u32);
+ spi_message_add_tail(&t[0], &m);
+ spi_sync(wl->spi, &m);
+
+ if (*busy_buf & 0x1) {
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+ t[0].rx_buf = buf;
+ t[0].len = len;
+ spi_message_add_tail(&t[0], &m);
+ spi_sync(wl->spi, &m);
+ return;
+ }
+ }
+
+ /* The SPI bus is unresponsive, the read failed. */
+ memset(buf, 0, len);
+ wl1271_error("SPI read busy-word timeout!\n");
+}
+
void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf,
size_t len, bool fixed)
{
struct spi_transfer t[3];
struct spi_message m;
- u8 *busy_buf;
+ u32 *busy_buf;
u32 *cmd;

cmd = &wl->buffer_cmd;
@@ -281,7 +344,9 @@ void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf,

spi_sync(wl->spi, &m);

- /* FIXME: check busy words */
+ /* Check busy words */
+ if (!(busy_buf[WL1271_BUSY_WORD_CNT - 1] & 0x1))
+ wl1271_spi_read_busy(wl, buf, len);

wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd));
wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len);
--
1.5.6.5


2009-10-08 18:58:24

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 04/20] wl1271: Correct TKIP header space handling in TX path

From: Juuso Oikarinen <[email protected]>

Correct the position to which TKIP header space is appended for TX
packets.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Vidhya Govindan <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_main.c | 9 +++------
drivers/net/wireless/wl12xx/wl1271_tx.c | 27 +++++++++++++++++----------
2 files changed, 20 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index bedd19b..3d629da 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1160,12 +1160,9 @@ static int wl1271_register_hw(struct wl1271 *wl)

static int wl1271_init_ieee80211(struct wl1271 *wl)
{
- /*
- * The tx descriptor buffer and the TKIP space.
- *
- * FIXME: add correct 1271 descriptor size
- */
- wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE;
+ /* The tx descriptor buffer and the TKIP space. */
+ wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE +
+ sizeof(struct wl1271_tx_hw_descr);

/* unit us */
/* FIXME: find a proper value */
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index 162f026..1ad1bc3 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -92,6 +92,14 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,

desc = (struct wl1271_tx_hw_descr *) skb->data;

+ /* relocate space for security header */
+ if (extra) {
+ void *framestart = skb->data + sizeof(*desc);
+ u16 fc = *(u16 *)(framestart + extra);
+ int hdrlen = ieee80211_hdrlen(fc);
+ memmove(framestart, framestart + extra, hdrlen);
+ }
+
/* configure packet life time */
desc->start_time = jiffies_to_usecs(jiffies) - wl->time_offset;
desc->life_time = TX_HW_MGMT_PKT_LIFETIME_TU;
@@ -257,7 +265,6 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,

struct ieee80211_tx_info *info;
struct sk_buff *skb;
- u32 header_len;
u16 seq;
int id = result->id;

@@ -295,22 +302,22 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
wl->tx_security_seq_32++;
wl->tx_security_seq_16 = seq;

- /* get header len */
+ /* remove private header from packet */
+ skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
+
+ /* remove TKIP header space if present */
if (info->control.hw_key &&
- info->control.hw_key->alg == ALG_TKIP)
- header_len = WL1271_TKIP_IV_SPACE +
- sizeof(struct wl1271_tx_hw_descr);
- else
- header_len = sizeof(struct wl1271_tx_hw_descr);
+ info->control.hw_key->alg == ALG_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);
+ }

wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x"
" status 0x%x",
result->id, skb, result->ack_failures,
result->rate_class_index, result->status);

- /* remove private header from packet */
- skb_pull(skb, header_len);
-
/* return the packet to the stack */
ieee80211_tx_status(wl->hw, skb);
wl->tx_frames[result->id] = NULL;
--
1.5.6.5


2009-10-08 18:58:34

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 11/20] wl1271: added Juuso Oikarinen as module author

Add Juuso as one of the module authors, since he's working heavily on this
module as well.

Cc: Juuso Oikarinen <[email protected]>
Reviewed-by: Juuso Oikarinen <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_main.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 0a0f2ea..1a0491a 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1449,3 +1449,4 @@ module_exit(wl1271_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <[email protected]>");
+MODULE_AUTHOR("Juuso Oikarinen <[email protected]>");
--
1.5.6.5


2009-10-08 18:58:43

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 12/20] wl1271: hack to disable filters

This is a hack to disable all filters in the join command. This is based on
Kalle Valo's patch for wl1251.

Cc: Kalle Valo <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_cmd.c | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index 35a6e67..ac93efd 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -208,6 +208,15 @@ int wl1271_cmd_join(struct wl1271 *wl)
join->rx_config_options = wl->rx_config;
join->rx_filter_options = wl->rx_filter;

+ /*
+ * FIXME: disable temporarily all filters because after commit
+ * 9cef8737 "mac80211: fix managed mode BSSID handling" broke
+ * association. The filter logic needs to be implemented properly
+ * and once that is done, this hack can be removed.
+ */
+ join->rx_config_options = 0;
+ join->rx_filter_options = WL1271_DEFAULT_RX_FILTER;
+
join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS |
RATE_MASK_5_5MBPS | RATE_MASK_11MBPS;

--
1.5.6.5


2009-10-08 18:58:45

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 17/20] wl1271: Add connection monitoring configuration

From: Juuso Oikarinen <[email protected]>

Add configuration for connection monitor (number of allowed beacons, and
timeout after last received beacon.)

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_acx.c | 30 +++++++++++++++++++++++++++++
drivers/net/wireless/wl12xx/wl1271_acx.h | 11 ++++++++++
drivers/net/wireless/wl12xx/wl1271_init.c | 6 +++++
3 files changed, 47 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index a457123..4b5fd94 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -442,6 +442,36 @@ out:
return ret;
}

+int wl1271_acx_conn_monit_params(struct wl1271 *wl)
+{
+ struct acx_conn_monit_params *acx;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx connection monitor parameters");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD;
+ acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT;
+
+ ret = wl1271_cmd_configure(wl, ACX_CONN_MONIT_PARAMS,
+ acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("failed to set connection monitor "
+ "parameters: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
+
int wl1271_acx_sg_enable(struct wl1271 *wl)
{
struct acx_bt_wlan_coex *pta;
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index dae1fed..bb21bcb 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -406,6 +406,16 @@ struct acx_beacon_filter_ie_table {
u8 pad[3];
} __attribute__ ((packed));

+#define SYNCH_FAIL_DEFAULT_THRESHOLD 5 /* number of beacons */
+#define NO_BEACON_DEFAULT_TIMEOUT (100) /* TU */
+
+struct acx_conn_monit_params {
+ struct acx_header header;
+
+ u32 synch_fail_thold; /* number of beacons missed */
+ u32 bss_lose_timeout; /* number of TU's from synch fail */
+};
+
enum {
SG_ENABLE = 0,
SG_DISABLE,
@@ -1198,6 +1208,7 @@ int wl1271_acx_service_period_timeout(struct wl1271 *wl);
int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold);
int wl1271_acx_beacon_filter_opt(struct wl1271 *wl);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
+int wl1271_acx_conn_monit_params(struct wl1271 *wl);
int wl1271_acx_sg_enable(struct wl1271 *wl);
int wl1271_acx_sg_cfg(struct wl1271 *wl);
int wl1271_acx_cca_threshold(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index 49ff407..d3db3fb 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -323,6 +323,11 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;

+ /* Initialize connection monitoring thresholds */
+ ret = wl1271_acx_conn_monit_params(wl);
+ if (ret < 0)
+ goto out_free_memmap;
+
/* Beacon filtering */
ret = wl1271_init_beacon_filter(wl);
if (ret < 0)
@@ -392,6 +397,7 @@ int wl1271_hw_init(struct wl1271 *wl)

out_free_memmap:
kfree(wl->target_mem_map);
+ wl->target_mem_map = NULL;

return ret;
}
--
1.5.6.5


2009-10-08 18:58:24

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 05/20] wl1271: Implement delayed entry into ELP

From: Juuso Oikarinen <[email protected]>

Implement delayed entry into ELP. This will promote the following:
- Less redundant sleep/wake cycles (better perf)
- Avoids known firmware issues with going to ELP too fast after an
operation

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Vidhya Govindan <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 1 +
drivers/net/wireless/wl12xx/wl1271_main.c | 1 +
drivers/net/wireless/wl12xx/wl1271_ps.c | 46 ++++++++++++++++++++---------
drivers/net/wireless/wl12xx/wl1271_ps.h | 2 +-
4 files changed, 35 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index e575dcc..c455dcb 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -373,6 +373,7 @@ struct wl1271 {
bool elp;

struct completion *elp_compl;
+ struct delayed_work elp_work;

/* we can be in psm, but not in elp, we have to differentiate */
bool psm;
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 3d629da..cc5c328 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -1226,6 +1226,7 @@ static int __devinit wl1271_probe(struct spi_device *spi)
skb_queue_head_init(&wl->tx_queue);

INIT_WORK(&wl->filter_work, wl1271_filter_work);
+ INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
wl->channel = WL1271_DEFAULT_CHANNEL;
wl->scanning = false;
wl->default_key = 0;
diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c
index 1dc74b0..0f6ea16 100644
--- a/drivers/net/wireless/wl12xx/wl1271_ps.c
+++ b/drivers/net/wireless/wl12xx/wl1271_ps.c
@@ -27,25 +27,43 @@

#define WL1271_WAKEUP_TIMEOUT 500

-/* Routines to toggle sleep mode while in ELP */
-void wl1271_ps_elp_sleep(struct wl1271 *wl)
+void wl1271_elp_work(struct work_struct *work)
{
+ struct delayed_work *dwork;
+ struct wl1271 *wl;
+
+ dwork = container_of(work, struct delayed_work, work);
+ wl = container_of(dwork, struct wl1271, elp_work);
+
+ wl1271_debug(DEBUG_PSM, "elp work");
+
+ mutex_lock(&wl->mutex);
+
/*
- * FIXME: due to a problem in the firmware (causing a firmware
- * crash), ELP entry is prevented below. Remove the "true" to
- * re-enable ELP entry.
+ * FIXME: below, by means of the "true", ELP has been disabled for now
+ * to work around a firmware bug. To be enabled upon receiving a new
+ * firmware version.
*/
if (true || wl->elp || !wl->psm)
- return;
+ goto out;

- /*
- * Go to ELP unless there is work already pending - pending work
- * will immediately wakeup the chipset anyway.
- */
- if (!work_pending(&wl->irq_work) && !work_pending(&wl->tx_work)) {
- wl1271_debug(DEBUG_PSM, "chip to elp");
- wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
- wl->elp = true;
+ wl1271_debug(DEBUG_PSM, "chip to elp");
+ wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP);
+ wl->elp = true;
+
+out:
+ mutex_unlock(&wl->mutex);
+}
+
+#define ELP_ENTRY_DELAY 5
+
+/* Routines to toggle sleep mode while in ELP */
+void wl1271_ps_elp_sleep(struct wl1271 *wl)
+{
+ if (wl->psm) {
+ cancel_delayed_work(&wl->elp_work);
+ ieee80211_queue_delayed_work(wl->hw, &wl->elp_work,
+ msecs_to_jiffies(ELP_ENTRY_DELAY));
}
}

diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.h b/drivers/net/wireless/wl12xx/wl1271_ps.h
index de2bd3c..779653d 100644
--- a/drivers/net/wireless/wl12xx/wl1271_ps.h
+++ b/drivers/net/wireless/wl12xx/wl1271_ps.h
@@ -30,6 +30,6 @@
int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode);
void wl1271_ps_elp_sleep(struct wl1271 *wl);
int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake);
-
+void wl1271_elp_work(struct work_struct *work);

#endif /* __WL1271_PS_H__ */
--
1.5.6.5


2009-10-08 20:07:39

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH 15/20] wl1271: Multicast filtering configuration

On Thu, 2009-10-08 at 21:56 +0300, Luciano Coelho wrote:

> + /* first, get the filter parameters */
> + spin_lock_irqsave(&wl->wl_lock, flags);
> + fp = wl->filter_params;
> + wl->filter_params = NULL;
> + spin_unlock_irqrestore(&wl->wl_lock, flags);
> +
> + if (!fp)
> + return;

> + kfree(fp);

> +static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
> + struct dev_addr_list *mc_list)
> +{
> + struct wl1271 *wl = hw->priv;
> + struct wl1271_filter_params *fp;
> + unsigned long flags;
> + int i;
> +
> + /*
> + * FIXME: we should return a hash that will be passed to
> + * configure_filter() instead of saving everything in the context.
> + */
> +
> + fp = kzalloc(sizeof(*fp), GFP_KERNEL);
> + if (!fp) {
> + wl1271_error("Out of memory setting filters.");
> + return 0;
> + }

It's a u64 on purpose, you can return the parameters struct to pass it
through to configure_filter.

johannes


Attachments:
signature.asc (801.00 B)
This is a digitally signed message part

2009-10-08 18:58:41

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 13/20] wl1271: use workqueue provided by mac80211 instead of the default

From: Juuso Oikarinen <[email protected]>

Use the workqueue provided by the mac80211 stack instead of the system
default queue.

Modified to use new ieee_queue_work() as required by changes in the
stack.

Signed-off-by: Juuso Oikarinen <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_main.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index 1a0491a..d104230 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -107,7 +107,7 @@ static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status)

/* if more blocks are available now, schedule some tx work */
if (total && !skb_queue_empty(&wl->tx_queue))
- schedule_work(&wl->tx_work);
+ ieee80211_queue_work(wl->hw, &wl->tx_work);

/* update the host-chipset time offset */
wl->time_offset = jiffies_to_usecs(jiffies) - status->fw_localtime;
@@ -205,7 +205,7 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
wl->elp_compl = NULL;
}

- schedule_work(&wl->irq_work);
+ ieee80211_queue_work(wl->hw, &wl->irq_work);
spin_unlock_irqrestore(&wl->wl_lock, flags);

return IRQ_HANDLED;
@@ -480,7 +480,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
* before that, the tx_work will not be initialized!
*/

- schedule_work(&wl->tx_work);
+ ieee80211_queue_work(wl->hw, &wl->tx_work);

/*
* The workqueue is slow to process the tx_queue and we need stop
--
1.5.6.5


2009-10-08 18:58:46

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 15/20] wl1271: Multicast filtering configuration

From: Juuso Oikarinen <[email protected]>

Enable multicast filtering. This way by default no multicast frames will
reach the host, and when needed, only required multicast frames can be
passed from the WLAN chipset to the host.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 7 +-
drivers/net/wireless/wl12xx/wl1271_acx.c | 9 +-
drivers/net/wireless/wl12xx/wl1271_acx.h | 8 +-
drivers/net/wireless/wl12xx/wl1271_init.c | 2 +-
drivers/net/wireless/wl12xx/wl1271_main.c | 121 +++++++++++++++++++++++++----
5 files changed, 120 insertions(+), 27 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 0b4744d..34a52b3 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -97,7 +97,8 @@ enum {
} while (0)

#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \
- CFG_BSSID_FILTER_EN)
+ CFG_BSSID_FILTER_EN | \
+ CFG_MC_FILTER_EN)

#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \
CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
@@ -123,7 +124,7 @@ enum {
#define WL1271_DEFAULT_BEACON_INT 100
#define WL1271_DEFAULT_DTIM_PERIOD 1

-#define ACX_TX_DESCRIPTORS 32
+#define ACX_TX_DESCRIPTORS 32

enum wl1271_state {
WL1271_STATE_OFF,
@@ -345,7 +346,9 @@ struct wl1271 {
bool tx_queue_stopped;

struct work_struct tx_work;
+
struct work_struct filter_work;
+ struct wl1271_filter_params *filter_params;

/* Pending TX frames */
struct sk_buff *tx_frames[ACX_TX_DESCRIPTORS];
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c
index 2ae1081..a457123 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.c
@@ -300,7 +300,8 @@ out:
return ret;
}

-int wl1271_acx_group_address_tbl(struct wl1271 *wl)
+int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
+ void *mc_list, u32 mc_list_len)
{
struct acx_dot11_grp_addr_tbl *acx;
int ret;
@@ -314,9 +315,9 @@ int wl1271_acx_group_address_tbl(struct wl1271 *wl)
}

/* MAC filtering */
- acx->enabled = 0;
- acx->num_groups = 0;
- memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN);
+ acx->enabled = enable;
+ acx->num_groups = mc_list_len;
+ memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);

ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL,
acx, sizeof(*acx));
diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h
index c177345..dae1fed 100644
--- a/drivers/net/wireless/wl12xx/wl1271_acx.h
+++ b/drivers/net/wireless/wl12xx/wl1271_acx.h
@@ -301,8 +301,8 @@ struct acx_slot {
} __attribute__ ((packed));


-#define ADDRESS_GROUP_MAX (8)
-#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX)
+#define ACX_MC_ADDRESS_GROUP_MAX (8)
+#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX)

struct acx_dot11_grp_addr_tbl {
struct acx_header header;
@@ -313,7 +313,6 @@ struct acx_dot11_grp_addr_tbl {
u8 mac_table[ADDRESS_GROUP_MAX_LEN];
} __attribute__ ((packed));

-
#define RX_TIMEOUT_PS_POLL_MIN 0
#define RX_TIMEOUT_PS_POLL_MAX (200000)
#define RX_TIMEOUT_PS_POLL_DEF (15)
@@ -1193,7 +1192,8 @@ int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time);
int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter);
int wl1271_acx_pd_threshold(struct wl1271 *wl);
int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time);
-int wl1271_acx_group_address_tbl(struct wl1271 *wl);
+int wl1271_acx_group_address_tbl(struct wl1271 *wl, bool enable,
+ void *mc_list, u32 mc_list_len);
int wl1271_acx_service_period_timeout(struct wl1271 *wl);
int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold);
int wl1271_acx_beacon_filter_opt(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c
index eb6b91a..49ff407 100644
--- a/drivers/net/wireless/wl12xx/wl1271_init.c
+++ b/drivers/net/wireless/wl12xx/wl1271_init.c
@@ -117,7 +117,7 @@ static int wl1271_init_phy_config(struct wl1271 *wl)
if (ret < 0)
return ret;

- ret = wl1271_acx_group_address_tbl(wl);
+ ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
if (ret < 0)
return ret;

diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index d104230..09fe968 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -379,12 +379,39 @@ out:
return ret;
}

+struct wl1271_filter_params {
+ unsigned int filters;
+ unsigned int changed;
+ int mc_list_length;
+ u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
+};
+
+#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
+ FIF_ALLMULTI | \
+ FIF_FCSFAIL | \
+ FIF_BCN_PRBRESP_PROMISC | \
+ FIF_CONTROL | \
+ FIF_OTHER_BSS)
+
static void wl1271_filter_work(struct work_struct *work)
{
struct wl1271 *wl =
container_of(work, struct wl1271, filter_work);
+ struct wl1271_filter_params *fp;
+ unsigned long flags;
+ bool enabled = true;
int ret;

+ /* first, get the filter parameters */
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ fp = wl->filter_params;
+ wl->filter_params = NULL;
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+ if (!fp)
+ return;
+
+ /* then, lock the mutex without risk of lock-up */
mutex_lock(&wl->mutex);

if (wl->state == WL1271_STATE_OFF)
@@ -394,6 +421,20 @@ static void wl1271_filter_work(struct work_struct *work)
if (ret < 0)
goto out;

+ /* configure the mc filter regardless of the changed flags */
+ if (fp->filters & FIF_ALLMULTI)
+ enabled = false;
+
+ ret = wl1271_acx_group_address_tbl(wl, enabled,
+ fp->mc_list, fp->mc_list_length);
+ if (ret < 0)
+ goto out_sleep;
+
+ /* determine, whether supported filter values have changed */
+ if (fp->changed == 0)
+ goto out;
+
+ /* apply configured filters */
ret = wl1271_cmd_join(wl);
if (ret < 0)
goto out_sleep;
@@ -403,6 +444,7 @@ out_sleep:

out:
mutex_unlock(&wl->mutex);
+ kfree(fp);
}

int wl1271_plt_start(struct wl1271 *wl)
@@ -544,12 +586,20 @@ out:
static void wl1271_op_stop(struct ieee80211_hw *hw)
{
struct wl1271 *wl = hw->priv;
+ unsigned long flags;
int i;

wl1271_info("down");

wl1271_debug(DEBUG_MAC80211, "mac80211 stop");

+ /* complete/cancel ongoing work */
+ cancel_work_sync(&wl->filter_work);
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ kfree(wl->filter_params);
+ wl->filter_params = NULL;
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+
mutex_lock(&wl->mutex);

WARN_ON(wl->state != WL1271_STATE_ON);
@@ -784,16 +834,52 @@ out:
return ret;
}

-#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
- FIF_ALLMULTI | \
- FIF_FCSFAIL | \
- FIF_BCN_PRBRESP_PROMISC | \
- FIF_CONTROL | \
- FIF_OTHER_BSS)
+static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count,
+ struct dev_addr_list *mc_list)
+{
+ struct wl1271 *wl = hw->priv;
+ struct wl1271_filter_params *fp;
+ unsigned long flags;
+ int i;
+
+ /*
+ * FIXME: we should return a hash that will be passed to
+ * configure_filter() instead of saving everything in the context.
+ */
+
+ fp = kzalloc(sizeof(*fp), GFP_KERNEL);
+ if (!fp) {
+ wl1271_error("Out of memory setting filters.");
+ return 0;
+ }
+
+ /* update multicast filtering parameters */
+ if (mc_count > ACX_MC_ADDRESS_GROUP_MAX) {
+ mc_count = 0;
+ fp->filters |= FIF_ALLMULTI;
+ }
+
+ fp->mc_list_length = 0;
+ for (i = 0; i < mc_count; i++) {
+ if (mc_list->da_addrlen == ETH_ALEN) {
+ memcpy(fp->mc_list[fp->mc_list_length],
+ mc_list->da_addr, ETH_ALEN);
+ fp->mc_list_length++;
+ } else
+ wl1271_warning("Unknown mc address length.");
+ }
+
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ kfree(wl->filter_params);
+ wl->filter_params = fp;
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+ return 1;
+}

static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
unsigned int changed,
- unsigned int *total,u64 multicast)
+ unsigned int *total, u64 multicast)
{
struct wl1271 *wl = hw->priv;

@@ -802,19 +888,21 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
*total &= WL1271_SUPPORTED_FILTERS;
changed &= WL1271_SUPPORTED_FILTERS;

- if (changed == 0)
+ if (!multicast)
return;

- /* FIXME: wl->rx_config and wl->rx_filter are not protected */
- wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
- wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
-
/*
- * FIXME: workqueues need to be properly cancelled on stop(), for
- * now let's just disable changing the filter settings. They will
- * be updated any on config().
+ * FIXME: for now we are still using a workqueue for filter
+ * configuration, but with the new mac80211, this is not needed,
+ * since configure_filter can now sleep. We now have
+ * prepare_multicast, which needs to be atomic instead.
*/
- /* schedule_work(&wl->filter_work); */
+
+ /* store current filter config */
+ wl->filter_params->filters = *total;
+ wl->filter_params->changed = changed;
+
+ ieee80211_queue_work(wl->hw, &wl->filter_work);
}

static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
@@ -1177,6 +1265,7 @@ static const struct ieee80211_ops wl1271_ops = {
.remove_interface = wl1271_op_remove_interface,
.config = wl1271_op_config,
/* .config_interface = wl1271_op_config_interface, */
+ .prepare_multicast = wl1271_op_prepare_multicast,
.configure_filter = wl1271_op_configure_filter,
.tx = wl1271_op_tx,
.set_key = wl1271_op_set_key,
--
1.5.6.5


2009-10-08 18:58:38

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 09/20] wl1271: Update join usage

From: Juuso Oikarinen <[email protected]>

Update the usage of join's, including using actual beacon interval and
dtim from AP, and configuring a basic rate set from AP.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Luciano Coelho <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271.h | 12 ++++++++
drivers/net/wireless/wl12xx/wl1271_cmd.c | 15 +++-------
drivers/net/wireless/wl12xx/wl1271_cmd.h | 3 +-
drivers/net/wireless/wl12xx/wl1271_main.c | 42 ++++++++++++++++++++++-------
4 files changed, 50 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h
index 671dc5a..05eb29c 100644
--- a/drivers/net/wireless/wl12xx/wl1271.h
+++ b/drivers/net/wireless/wl12xx/wl1271.h
@@ -104,6 +104,8 @@ enum {
CFG_RX_CTL_EN | CFG_RX_BCN_EN | \
CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN)

+#define WL1271_DEFAULT_BASIC_RATE_SET (ACX_RATE_MASK_ALL)
+
#define WL1271_FW_NAME "wl1271-fw.bin"
#define WL1271_NVS_NAME "wl1271-nvs.bin"

@@ -118,6 +120,9 @@ enum {
#define WL1271_ELP_HW_STATE_ASLEEP 0
#define WL1271_ELP_HW_STATE_IRQ 1

+#define WL1271_DEFAULT_BEACON_INT 100
+#define WL1271_DEFAULT_DTIM_PERIOD 1
+
enum wl1271_state {
WL1271_STATE_OFF,
WL1271_STATE_ON,
@@ -369,6 +374,13 @@ struct wl1271 {
/* Our association ID */
u16 aid;

+ /* Beacon parameters */
+ u16 beacon_int;
+ u8 dtim_period;
+
+ /* currently configured rate set */
+ u32 basic_rate_set;
+
/* The current band */
enum ieee80211_band band;

diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c
index bd65b38..35a6e67 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.c
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c
@@ -175,11 +175,9 @@ int wl1271_cmd_cal(struct wl1271 *wl)
return ret;
}

-int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
- u16 beacon_interval, u8 wait)
+int wl1271_cmd_join(struct wl1271 *wl)
{
static bool do_cal = true;
- unsigned long timeout;
struct wl1271_cmd_join *join;
int ret, i;
u8 *bssid;
@@ -213,9 +211,9 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS |
RATE_MASK_5_5MBPS | RATE_MASK_11MBPS;

- join->beacon_interval = beacon_interval;
- join->dtim_interval = dtim_interval;
- join->bss_type = bss_type;
+ join->beacon_interval = wl->beacon_int;
+ join->dtim_interval = wl->dtim_period;
+ join->bss_type = wl->bss_type;
join->channel = wl->channel;
join->ssid_len = wl->ssid_len;
memcpy(join->ssid, wl->ssid, wl->ssid_len);
@@ -239,14 +237,11 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
goto out_free;
}

- timeout = msecs_to_jiffies(JOIN_TIMEOUT);
-
/*
* ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to
* simplify locking we just sleep instead, for now
*/
- if (wait)
- msleep(10);
+ msleep(10);

out_free:
kfree(join);
diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h
index 7c4d3aa..63bc441 100644
--- a/drivers/net/wireless/wl12xx/wl1271_cmd.h
+++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h
@@ -30,8 +30,7 @@
struct acx_header;

int wl1271_cmd_send(struct wl1271 *wl, u16 type, void *buf, size_t buf_len);
-int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval,
- u16 beacon_interval, u8 wait);
+int wl1271_cmd_join(struct wl1271 *wl);
int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer);
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);
diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c
index b9b1c20..6e35fcf 100644
--- a/drivers/net/wireless/wl12xx/wl1271_main.c
+++ b/drivers/net/wireless/wl12xx/wl1271_main.c
@@ -394,8 +394,7 @@ static void wl1271_filter_work(struct work_struct *work)
if (ret < 0)
goto out;

- /* FIXME: replace the magic numbers with proper definitions */
- ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0);
+ ret = wl1271_cmd_join(wl);
if (ret < 0)
goto out_sleep;

@@ -672,8 +671,7 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw,
memcpy(wl->ssid, conf->ssid, wl->ssid_len);

if (wl->bss_type != BSS_TYPE_IBSS) {
- /* FIXME: replace the magic numbers with proper definitions */
- ret = wl1271_cmd_join(wl, wl->bss_type, 5, 100, 1);
+ ret = wl1271_cmd_join(wl);
if (ret < 0)
goto out_sleep;
}
@@ -696,8 +694,7 @@ static int wl1271_op_config_interface(struct ieee80211_hw *hw,
if (ret < 0)
goto out_sleep;

- /* FIXME: replace the magic numbers with proper definitions */
- ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0);
+ ret = wl1271_cmd_join(wl);

if (ret < 0)
goto out_sleep;
@@ -738,8 +735,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
u8 old_channel = wl->channel;
wl->channel = channel;

- /* FIXME: use beacon interval provided by mac80211 */
- ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0);
+ ret = wl1271_cmd_join(wl);
if (ret < 0) {
wl->channel = old_channel;
goto out_sleep;
@@ -1016,8 +1012,17 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,

if (changed & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
+ wl->beacon_int = bss_conf->beacon_int;
+ wl->dtim_period = bss_conf->dtim_period;
wl->aid = bss_conf->aid;

+ ret = wl1271_cmd_join(wl);
+ if (ret < 0) {
+ wl1271_warning("Association configuration "
+ "failed %d", ret);
+ goto out_sleep;
+ }
+
ret = wl1271_cmd_build_ps_poll(wl, wl->aid);
if (ret < 0)
goto out_sleep;
@@ -1033,7 +1038,14 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
if (ret < 0)
goto out_sleep;
}
+ } else {
+ /* use defaults when not associated */
+ wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
+ wl->dtim_period = WL1271_DEFAULT_DTIM_PERIOD;
+ wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
+ wl->aid = 0;
}
+
}

if (changed & BSS_CHANGED_ERP_SLOT) {
@@ -1066,13 +1078,20 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
}

if (changed & BSS_CHANGED_BASIC_RATES) {
- u32 enabled_rates = wl1271_enabled_rates_get(
+ wl->basic_rate_set = wl1271_enabled_rates_get(
wl, bss_conf->basic_rates);
- ret = wl1271_acx_rate_policies(wl, enabled_rates);
+ ret = wl1271_acx_rate_policies(wl, wl->basic_rate_set);
+
if (ret < 0) {
wl1271_warning("Set rate policies failed %d", ret);
goto out_sleep;
}
+ ret = wl1271_cmd_join(wl);
+ if (ret < 0) {
+ wl1271_warning("Join with new basic rate "
+ "set failed %d", ret);
+ goto out_sleep;
+ }
}

out_sleep:
@@ -1269,6 +1288,9 @@ static int __devinit wl1271_probe(struct spi_device *spi)
wl->psm_requested = false;
wl->tx_queue_stopped = false;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
+ wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
+ wl->dtim_period = WL1271_DEFAULT_DTIM_PERIOD;
+ wl->basic_rate_set = WL1271_DEFAULT_BASIC_RATE_SET;
wl->band = IEEE80211_BAND_2GHZ;

/* We use the default power on sleep time until we know which chip
--
1.5.6.5


2009-10-08 18:58:26

by Luciano Coelho

[permalink] [raw]
Subject: [PATCH 02/20] wl1271: Correction to TX block allocation calculation

From: Juuso Oikarinen <[email protected]>

Correct the TX path implementation to allocate sufficient blocks in the
firmware for TX packets.

Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Kalle Valo <[email protected]>
Signed-off-by: Luciano Coelho <[email protected]>
---
drivers/net/wireless/wl12xx/wl1271_tx.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index ff22125..0c19688 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -58,7 +58,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
/* approximate the number of blocks required for this packet
in the firmware */
/* FIXME: try to figure out what is done here and make it cleaner */
- total_blocks = (skb->len) >> TX_HW_BLOCK_SHIFT_DIV;
+ total_blocks = (total_len) >> TX_HW_BLOCK_SHIFT_DIV;
excluded = (total_blocks << 2) + (skb->len & 0xff) + 34;
total_blocks += (excluded > 252) ? 2 : 1;
total_blocks += TX_HW_BLOCK_SPARE;
--
1.5.6.5