2012-11-27 06:45:07

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 00/20] wlcore fixes for 3.8 (part2)

This series applies on top of part 1 sent yesterday.

The main addition is a new Tx scheduling algorithm for
better multi-channel operation.

Arik Nemtsov (9):
wlcore: clear roc_vif on iface removal
wlcore: take the mutex before resetting Tx queues
wlcore: consolidate free_link and always call it
wlcore: initialize per-link FW freed blocks correctly
wlcore: track wlvif inside per-link structure
wlcore: count packets held per AC in each vif
wlcore: track FW-allocated packets per link
wlcore: improved Tx scheduling algorithm
wl18xx: set last Tx rate from FW status

Eliad Peller (4):
wlcore: allow fw commands to fail
wlcore: allow ACX_BA_SESSION_RX_SETUP to fail
wlcore: remove WLCORE_QUIRK_NO_ELP
wl18xx: declare support for greenfield ht_cap

Ido Reis (3):
wl18xx: update default mac/phy parameters
wlcore: fwlog dynamic mem_block control
wl18xx: FDSP Code RAM Corruption fix

Ido Yariv (1):
wlcore: Allow memory access when the FW crashes

Victor Goldenshtein (2):
wlcore: remove unnecessary WARN_ON in wl12xx_tx_reset
wlcore: restore default channel configuration

Yair Shapira (1):
wl18xx: support 2nd set of mac/phy tx-power params

drivers/net/wireless/ti/wl12xx/main.c | 2 +
drivers/net/wireless/ti/wl18xx/conf.h | 21 +++-
drivers/net/wireless/ti/wl18xx/main.c | 87 +++++++++++---
drivers/net/wireless/ti/wl18xx/reg.h | 20 ++++
drivers/net/wireless/ti/wl18xx/tx.c | 56 ++++++++-
drivers/net/wireless/ti/wlcore/acx.c | 13 ++-
drivers/net/wireless/ti/wlcore/cmd.c | 96 +++++++++++++---
drivers/net/wireless/ti/wlcore/cmd.h | 7 +-
drivers/net/wireless/ti/wlcore/conf.h | 76 ++++++++++---
drivers/net/wireless/ti/wlcore/debugfs.c | 8 +-
drivers/net/wireless/ti/wlcore/init.c | 3 -
drivers/net/wireless/ti/wlcore/main.c | 176 ++++++++++++++++++-----------
drivers/net/wireless/ti/wlcore/ps.c | 8 +-
drivers/net/wireless/ti/wlcore/tx.c | 157 +++++++++++++++++--------
drivers/net/wireless/ti/wlcore/wlcore.h | 9 +-
drivers/net/wireless/ti/wlcore/wlcore_i.h | 13 ++-
16 files changed, 566 insertions(+), 186 deletions(-)

--
1.7.9.5



2012-11-27 06:45:12

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 03/20] wlcore: Allow memory access when the FW crashes

From: Ido Yariv <[email protected]>

When the no_recovery flag is used, the recovery work will not restart
the FW and the state will not be set to 'on'. To enable post-mortem
analysis, allow memory access in the 'restarting' state.

Also, since the FW might not be operational, don't fail the read/write
operations if elp_wakeup fails.

Reported-by: Arkady Miasnikov <[email protected]>
Signed-off-by: Ido Yariv <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/debugfs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index 78d2eb3..e61fc2b 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -1063,7 +1063,7 @@ static ssize_t dev_mem_read(struct file *file,

mutex_lock(&wl->mutex);

- if (unlikely(wl->state != WLCORE_STATE_ON)) {
+ if (unlikely(wl->state == WLCORE_STATE_OFF)) {
ret = -EFAULT;
goto skip_read;
}
@@ -1150,7 +1150,7 @@ static ssize_t dev_mem_write(struct file *file, const char __user *user_buf,

mutex_lock(&wl->mutex);

- if (unlikely(wl->state != WLCORE_STATE_ON)) {
+ if (unlikely(wl->state == WLCORE_STATE_OFF)) {
ret = -EFAULT;
goto skip_write;
}
--
1.7.9.5


2012-11-27 06:45:23

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 09/20] wlcore: fwlog dynamic mem_block control

From: Ido Reis <[email protected]>

number of fwlog mem_blocks can be configured using module param.
this is a fw debug feature: in case a large fw log data is busrted during
a short period of time, the memory get filled and data is lost.
this allows us to dynamicly set the fw log mem_block usage, although
configuring more mem_block for logger comes at the expense of TP.

Signed-off-by: Yair Shapira <[email protected]>
Signed-off-by: Ido Reis <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/conf.h | 5 ++++-
drivers/net/wireless/ti/wlcore/main.c | 16 ++++++++++++++++
2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index b64d6b5..b3894ef 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -1208,6 +1208,9 @@ struct conf_rx_streaming_settings {
u8 always;
} __packed;

+#define CONF_FWLOG_MIN_MEM_BLOCKS 2
+#define CONF_FWLOG_MAX_MEM_BLOCKS 16
+
struct conf_fwlog {
/* Continuous or on-demand */
u8 mode;
@@ -1215,7 +1218,7 @@ struct conf_fwlog {
/*
* Number of memory blocks dedicated for the FW logger
*
- * Range: 1-3, or 0 to disable the FW logger
+ * Range: 2-16, or 0 to disable the FW logger
*/
u8 mem_blocks;

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index b659efd..8932cca 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -56,6 +56,7 @@
#define WL1271_BOOT_RETRIES 3

static char *fwlog_param;
+static int fwlog_mem_blocks = -1;
static int bug_on_recovery = -1;
static int no_recovery = -1;

@@ -307,6 +308,18 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
{
/* Adjust settings according to optional module parameters */

+ /* Firmware Logger params */
+ if (fwlog_mem_blocks != -1) {
+ if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS &&
+ fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) {
+ wl->conf.fwlog.mem_blocks = fwlog_mem_blocks;
+ } else {
+ wl1271_error(
+ "Illegal fwlog_mem_blocks=%d using default %d",
+ fwlog_mem_blocks, wl->conf.fwlog.mem_blocks);
+ }
+ }
+
/* Firmware Log Settings */
if (fwlog_param) {
if (!strcmp(fwlog_param, "continuous")) {
@@ -6024,6 +6037,9 @@ module_param_named(fwlog, fwlog_param, charp, 0);
MODULE_PARM_DESC(fwlog,
"FW logger options: continuous, ondemand, dbgpins or disable");

+module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
+
module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");

--
1.7.9.5


2012-11-27 06:45:20

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 07/20] wlcore: clear roc_vif on iface removal

When removing an interface currently performing a ROC operation, clear
the current ROC state. This is useful especially during recovery and
keeps mac80211 in sync to our state.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 05ba72a..b659efd 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2416,6 +2416,11 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
wl->sched_vif = NULL;
}

+ if (wl->roc_vif == vif) {
+ wl->roc_vif = NULL;
+ ieee80211_remain_on_channel_expired(wl->hw);
+ }
+
if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
/* disable active roles */
ret = wl1271_ps_elp_wakeup(wl);
--
1.7.9.5


2012-11-27 06:45:09

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 01/20] wl18xx: update default mac/phy parameters

From: Ido Reis <[email protected]>

Update mac/phy paramters according to the default HP SISO boards.

Signed-off-by: Ido Reis <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl18xx/conf.h | 17 +++++++++++------
drivers/net/wireless/ti/wl18xx/main.c | 7 ++++---
2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
index 4d426cc..f020c1c 100644
--- a/drivers/net/wireless/ti/wl18xx/conf.h
+++ b/drivers/net/wireless/ti/wl18xx/conf.h
@@ -23,20 +23,21 @@
#define __WL18XX_CONF_H__

#define WL18XX_CONF_MAGIC 0x10e100ca
-#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0003)
+#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0004)
#define WL18XX_CONF_MASK 0x0000ffff
#define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \
sizeof(struct wl18xx_priv_conf))

#define NUM_OF_CHANNELS_11_ABG 150
#define NUM_OF_CHANNELS_11_P 7
-#define WL18XX_NUM_OF_SUB_BANDS 9
#define SRF_TABLE_LEN 16
#define PIN_MUXING_SIZE 2
+#define NUM_OF_TRACE_LOSS_GAPS_TX 10
+#define NUM_OF_TRACE_LOSS_GAPS_RX 18

struct wl18xx_mac_and_phy_params {
u8 phy_standalone;
- u8 rdl;
+ u8 spare0;
u8 enable_clpc;
u8 enable_tx_low_pwr_on_siso_rdl;
u8 auto_detect;
@@ -69,8 +70,8 @@ struct wl18xx_mac_and_phy_params {
u8 pwr_limit_reference_11_abg;
u8 per_chan_pwr_limit_arr_11p[NUM_OF_CHANNELS_11_P];
u8 pwr_limit_reference_11p;
- u8 per_sub_band_tx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
- u8 per_sub_band_rx_trace_loss[WL18XX_NUM_OF_SUB_BANDS];
+ u8 spare1[9];
+ u8 spare2[9];
u8 primary_clock_setting_time;
u8 clock_valid_on_wake_up;
u8 secondary_clock_setting_time;
@@ -81,7 +82,11 @@ struct wl18xx_mac_and_phy_params {
s8 low_power_val;
s8 med_power_val;
s8 high_power_val;
- u8 padding[1];
+ s8 per_sub_band_tx_trace_loss[NUM_OF_TRACE_LOSS_GAPS_TX];
+ s8 per_sub_band_rx_trace_loss[NUM_OF_TRACE_LOSS_GAPS_RX];
+ u8 tx_rf_margin;
+
+ u8 padding[4];
} __packed;

enum wl18xx_ht_mode {
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index e71e2cf..30222cb 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -508,7 +508,6 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
.clock_valid_on_wake_up = 0x00,
.secondary_clock_setting_time = 0x05,
.board_type = BOARD_TYPE_HDK_18XX,
- .rdl = 0x01,
.auto_detect = 0x00,
.dedicated_fem = FEM_NONE,
.low_band_component = COMPONENT_3_WAY_SWITCH,
@@ -525,13 +524,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
.enable_tx_low_pwr_on_siso_rdl = 0x00,
.rx_profile = 0x00,
.pwr_limit_reference_11_abg = 0xc8,
+ .pwr_limit_reference_11p = 0xc8,
.psat = 0,
.low_power_val = 0x00,
- .med_power_val = 0x0a,
- .high_power_val = 0x1e,
+ .med_power_val = 0x0A,
+ .high_power_val = 0x11,
.external_pa_dc2dc = 0,
.number_of_assembled_ant2_4 = 1,
.number_of_assembled_ant5 = 1,
+ .tx_rf_margin = 1,
},
};

--
1.7.9.5


2012-11-27 06:45:33

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 14/20] wlcore: restore default channel configuration

From: Victor Goldenshtein <[email protected]>

wlcore allocates two static structs wl1271_band_2ghz & wl1271_band_5ghz
which are used/modified by Reg-Domain e.g. some channel might be marked
as passive at some point. Make sure we don't keep stale settings around
if the HW is unregistered/registered during operation.

[Arik - use Tx-power constant and tweak commit message]

Signed-off-by: Victor Goldenshtein <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 113 ++++++++++++++++++-------------
drivers/net/wireless/ti/wlcore/wlcore.h | 3 +
2 files changed, 68 insertions(+), 48 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index e68cd3f..3ced59b 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5086,20 +5086,20 @@ static struct ieee80211_rate wl1271_rates[] = {

/* can't be const, mac80211 writes to this */
static struct ieee80211_channel wl1271_channels[] = {
- { .hw_value = 1, .center_freq = 2412, .max_power = 25 },
- { .hw_value = 2, .center_freq = 2417, .max_power = 25 },
- { .hw_value = 3, .center_freq = 2422, .max_power = 25 },
- { .hw_value = 4, .center_freq = 2427, .max_power = 25 },
- { .hw_value = 5, .center_freq = 2432, .max_power = 25 },
- { .hw_value = 6, .center_freq = 2437, .max_power = 25 },
- { .hw_value = 7, .center_freq = 2442, .max_power = 25 },
- { .hw_value = 8, .center_freq = 2447, .max_power = 25 },
- { .hw_value = 9, .center_freq = 2452, .max_power = 25 },
- { .hw_value = 10, .center_freq = 2457, .max_power = 25 },
- { .hw_value = 11, .center_freq = 2462, .max_power = 25 },
- { .hw_value = 12, .center_freq = 2467, .max_power = 25 },
- { .hw_value = 13, .center_freq = 2472, .max_power = 25 },
- { .hw_value = 14, .center_freq = 2484, .max_power = 25 },
+ { .hw_value = 1, .center_freq = 2412, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 2, .center_freq = 2417, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 3, .center_freq = 2422, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 4, .center_freq = 2427, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 5, .center_freq = 2432, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 6, .center_freq = 2437, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 7, .center_freq = 2442, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 8, .center_freq = 2447, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 9, .center_freq = 2452, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 10, .center_freq = 2457, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 11, .center_freq = 2462, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 12, .center_freq = 2467, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 13, .center_freq = 2472, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 14, .center_freq = 2484, .max_power = WLCORE_MAX_TXPWR },
};

/* can't be const, mac80211 writes to this */
@@ -5140,40 +5140,40 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = {

/* 5 GHz band channels for WL1273 */
static struct ieee80211_channel wl1271_channels_5ghz[] = {
- { .hw_value = 7, .center_freq = 5035, .max_power = 25 },
- { .hw_value = 8, .center_freq = 5040, .max_power = 25 },
- { .hw_value = 9, .center_freq = 5045, .max_power = 25 },
- { .hw_value = 11, .center_freq = 5055, .max_power = 25 },
- { .hw_value = 12, .center_freq = 5060, .max_power = 25 },
- { .hw_value = 16, .center_freq = 5080, .max_power = 25 },
- { .hw_value = 34, .center_freq = 5170, .max_power = 25 },
- { .hw_value = 36, .center_freq = 5180, .max_power = 25 },
- { .hw_value = 38, .center_freq = 5190, .max_power = 25 },
- { .hw_value = 40, .center_freq = 5200, .max_power = 25 },
- { .hw_value = 42, .center_freq = 5210, .max_power = 25 },
- { .hw_value = 44, .center_freq = 5220, .max_power = 25 },
- { .hw_value = 46, .center_freq = 5230, .max_power = 25 },
- { .hw_value = 48, .center_freq = 5240, .max_power = 25 },
- { .hw_value = 52, .center_freq = 5260, .max_power = 25 },
- { .hw_value = 56, .center_freq = 5280, .max_power = 25 },
- { .hw_value = 60, .center_freq = 5300, .max_power = 25 },
- { .hw_value = 64, .center_freq = 5320, .max_power = 25 },
- { .hw_value = 100, .center_freq = 5500, .max_power = 25 },
- { .hw_value = 104, .center_freq = 5520, .max_power = 25 },
- { .hw_value = 108, .center_freq = 5540, .max_power = 25 },
- { .hw_value = 112, .center_freq = 5560, .max_power = 25 },
- { .hw_value = 116, .center_freq = 5580, .max_power = 25 },
- { .hw_value = 120, .center_freq = 5600, .max_power = 25 },
- { .hw_value = 124, .center_freq = 5620, .max_power = 25 },
- { .hw_value = 128, .center_freq = 5640, .max_power = 25 },
- { .hw_value = 132, .center_freq = 5660, .max_power = 25 },
- { .hw_value = 136, .center_freq = 5680, .max_power = 25 },
- { .hw_value = 140, .center_freq = 5700, .max_power = 25 },
- { .hw_value = 149, .center_freq = 5745, .max_power = 25 },
- { .hw_value = 153, .center_freq = 5765, .max_power = 25 },
- { .hw_value = 157, .center_freq = 5785, .max_power = 25 },
- { .hw_value = 161, .center_freq = 5805, .max_power = 25 },
- { .hw_value = 165, .center_freq = 5825, .max_power = 25 },
+ { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 36, .center_freq = 5180, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 38, .center_freq = 5190, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 40, .center_freq = 5200, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 42, .center_freq = 5210, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 44, .center_freq = 5220, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 46, .center_freq = 5230, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 48, .center_freq = 5240, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 52, .center_freq = 5260, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 56, .center_freq = 5280, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 60, .center_freq = 5300, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 64, .center_freq = 5320, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 100, .center_freq = 5500, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 104, .center_freq = 5520, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 108, .center_freq = 5540, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 112, .center_freq = 5560, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 116, .center_freq = 5580, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 120, .center_freq = 5600, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 124, .center_freq = 5620, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 128, .center_freq = 5640, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 132, .center_freq = 5660, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 136, .center_freq = 5680, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 140, .center_freq = 5700, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 149, .center_freq = 5745, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 153, .center_freq = 5765, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 157, .center_freq = 5785, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 161, .center_freq = 5805, .max_power = WLCORE_MAX_TXPWR },
+ { .hw_value = 165, .center_freq = 5825, .max_power = WLCORE_MAX_TXPWR },
};

static struct ieee80211_supported_band wl1271_band_5ghz = {
@@ -5537,6 +5537,7 @@ wlcore_iface_combinations[] = {

static int wl1271_init_ieee80211(struct wl1271 *wl)
{
+ int i;
static const u32 cipher_suites[] = {
WLAN_CIPHER_SUITE_WEP40,
WLAN_CIPHER_SUITE_WEP104,
@@ -5599,6 +5600,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
ARRAY_SIZE(wl1271_channels_5ghz) >
WL1271_MAX_CHANNELS);
/*
+ * clear channel flags from the previous usage
+ * and restore max_power & max_antenna_gain values.
+ */
+ for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
+ wl1271_band_2ghz.channels[i].flags = 0;
+ wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+ wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
+ wl1271_band_5ghz.channels[i].flags = 0;
+ wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
+ wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
+ }
+
+ /*
* We keep local copies of the band structs because we need to
* modify them on a per-device basis.
*/
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 07898de..8417ee2 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -37,6 +37,9 @@
*/
#define WLCORE_NUM_MAC_ADDRESSES 3

+/* wl12xx/wl18xx maximum transmission power (in dBm) */
+#define WLCORE_MAX_TXPWR 25
+
/* forward declaration */
struct wl1271_tx_hw_descr;
enum wl_rx_buf_align;
--
1.7.9.5


2012-11-27 06:45:41

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 19/20] wlcore: improved Tx scheduling algorithm

Prioritize EDCA by choosing the AC before anything else. Use the
fast/slow link bitmap in FW to improve the scheduling algorithm for
the multi-link scenario.

Set packet thresholds to determine if a given link is high or low
priority according to its speed. A slow link will be given high priority
if the amount of packets queued for it in the FW is lower than the
slow-threshold. Similarly, a fast link will be given high priority if
the number of its packets queued in FW is smaller than the high-threshold.

The improved algorithm:
1. Choose optimal AC according to FW utilization
2. Traversing the VIFs in a round-robin fashion, try to choose a high
priority link. Links are traversed in a round-robin fashion inside a
VIF.
3. If no high priority links are found, choose the first non-empty
(low priority) link found in the round robin.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl12xx/main.c | 2 +
drivers/net/wireless/ti/wl18xx/main.c | 2 +
drivers/net/wireless/ti/wlcore/conf.h | 14 +++-
drivers/net/wireless/ti/wlcore/main.c | 4 +-
drivers/net/wireless/ti/wlcore/tx.c | 124 ++++++++++++++++++++++---------
drivers/net/wireless/ti/wlcore/wlcore.h | 3 +
6 files changed, 112 insertions(+), 37 deletions(-)

diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index d997cdc..9ac042b 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -210,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = {
.tmpl_short_retry_limit = 10,
.tmpl_long_retry_limit = 10,
.tx_watchdog_timeout = 5000,
+ .slow_link_thold = 3,
+ .fast_link_thold = 10,
},
.conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 8fd13f3..53f4ba8 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -337,6 +337,8 @@ static struct wlcore_conf wl18xx_conf = {
.tmpl_short_retry_limit = 10,
.tmpl_long_retry_limit = 10,
.tx_watchdog_timeout = 5000,
+ .slow_link_thold = 3,
+ .fast_link_thold = 30,
},
.conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index b3894ef..e07d74d 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -674,6 +674,18 @@ struct conf_tx_settings {

/* Time in ms for Tx watchdog timer to expire */
u32 tx_watchdog_timeout;
+
+ /*
+ * when a slow link has this much packets pending, it becomes a low
+ * priority link, scheduling-wise
+ */
+ u8 slow_link_thold;
+
+ /*
+ * when a fast link has this much packets pending, it becomes a low
+ * priority link, scheduling-wise
+ */
+ u8 fast_link_thold;
} __packed;

enum {
@@ -1281,7 +1293,7 @@ struct conf_recovery_settings {
* version, the two LSB are the lower driver's private conf
* version.
*/
-#define WLCORE_CONF_VERSION (0x0004 << 16)
+#define WLCORE_CONF_VERSION (0x0005 << 16)
#define WLCORE_CONF_MASK 0xffff0000
#define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \
sizeof(struct wlcore_conf))
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index c557678..3834e9d 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -377,8 +377,6 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
u32 cur_fw_ps_map;
u8 hlid;

- /* TODO: also use link_fast_bitmap here */
-
cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
if (wl->ap_fw_ps_map != cur_fw_ps_map) {
wl1271_debug(DEBUG_PSM,
@@ -494,6 +492,8 @@ static int wlcore_fw_status(struct wl1271 *wl,
wl->time_offset = (timespec_to_ns(&ts) >> 10) -
(s64)le32_to_cpu(status_2->fw_localtime);

+ wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+
return 0;
}

diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 5ce6248..dd05fab 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -462,8 +462,7 @@ void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
}
}

-static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
- struct sk_buff_head *queues)
+static int wlcore_select_ac(struct wl1271 *wl)
{
int i, q = -1, ac;
u32 min_pkts = 0xffffffff;
@@ -477,33 +476,24 @@ static struct sk_buff_head *wl1271_select_queue(struct wl1271 *wl,
*/
for (i = 0; i < NUM_TX_QUEUES; i++) {
ac = wl1271_tx_get_queue(i);
- if (!skb_queue_empty(&queues[ac]) &&
- (wl->tx_allocated_pkts[ac] < min_pkts)) {
+ if (wl->tx_queue_count[ac] &&
+ wl->tx_allocated_pkts[ac] < min_pkts) {
q = ac;
min_pkts = wl->tx_allocated_pkts[q];
}
}

- if (q == -1)
- return NULL;
-
- return &queues[q];
+ return q;
}

-static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
- struct wl1271_link *lnk)
+static struct sk_buff *wlcore_lnk_dequeue(struct wl1271 *wl,
+ struct wl1271_link *lnk, u8 q)
{
struct sk_buff *skb;
unsigned long flags;
- struct sk_buff_head *queue;

- queue = wl1271_select_queue(wl, lnk->tx_queue);
- if (!queue)
- return NULL;
-
- skb = skb_dequeue(queue);
+ skb = skb_dequeue(&lnk->tx_queue[q]);
if (skb) {
- int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
spin_lock_irqsave(&wl->wl_lock, flags);
WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
wl->tx_queue_count[q]--;
@@ -517,9 +507,41 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
return skb;
}

-static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
- struct wl12xx_vif *wlvif,
- u8 *hlid)
+static bool wlcore_lnk_high_prio(struct wl1271 *wl, u8 hlid,
+ struct wl1271_link *lnk)
+{
+ u8 thold;
+
+ if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map))
+ thold = wl->conf.tx.fast_link_thold;
+ else
+ thold = wl->conf.tx.slow_link_thold;
+
+ return lnk->allocated_pkts < thold;
+}
+
+static struct sk_buff *wlcore_lnk_dequeue_high_prio(struct wl1271 *wl,
+ u8 hlid, u8 ac,
+ u8 *low_prio_hlid)
+{
+ struct wl1271_link *lnk = &wl->links[hlid];
+
+ if (!wlcore_lnk_high_prio(wl, hlid, lnk)) {
+ if (*low_prio_hlid == WL12XX_INVALID_LINK_ID &&
+ !skb_queue_empty(&lnk->tx_queue[ac]))
+ /* we found the first non-empty low priority queue */
+ *low_prio_hlid = hlid;
+
+ return NULL;
+ }
+
+ return wlcore_lnk_dequeue(wl, lnk, ac);
+}
+
+static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
+ struct wl12xx_vif *wlvif,
+ u8 ac, u8 *hlid,
+ u8 *low_prio_hlid)
{
struct sk_buff *skb = NULL;
int i, h, start_hlid;
@@ -535,7 +557,8 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
if (!test_bit(h, wlvif->links_map))
continue;

- skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[h]);
+ skb = wlcore_lnk_dequeue_high_prio(wl, h, ac,
+ low_prio_hlid);
if (!skb)
continue;

@@ -555,42 +578,74 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
unsigned long flags;
struct wl12xx_vif *wlvif = wl->last_wlvif;
struct sk_buff *skb = NULL;
+ int ac;
+ u8 low_prio_hlid = WL12XX_INVALID_LINK_ID;
+
+ ac = wlcore_select_ac(wl);
+ if (ac < 0)
+ goto out;

/* continue from last wlvif (round robin) */
if (wlvif) {
wl12xx_for_each_wlvif_continue(wl, wlvif) {
- skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
- if (skb) {
- wl->last_wlvif = wlvif;
- break;
- }
+ if (!wlvif->tx_queue_count[ac])
+ continue;
+
+ skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+ &low_prio_hlid);
+ if (!skb)
+ continue;
+
+ wl->last_wlvif = wlvif;
+ break;
}
}

/* dequeue from the system HLID before the restarting wlvif list */
if (!skb) {
- skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
- *hlid = wl->system_hlid;
+ skb = wlcore_lnk_dequeue_high_prio(wl, wl->system_hlid,
+ ac, &low_prio_hlid);
+ if (skb) {
+ *hlid = wl->system_hlid;
+ wl->last_wlvif = NULL;
+ }
}

- /* do a new pass over the wlvif list */
+ /* Do a new pass over the wlvif list. But no need to continue
+ * after last_wlvif. The previous pass should have found it. */
if (!skb) {
wl12xx_for_each_wlvif(wl, wlvif) {
- skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
+ if (!wlvif->tx_queue_count[ac])
+ goto next;
+
+ skb = wlcore_vif_dequeue_high_prio(wl, wlvif, ac, hlid,
+ &low_prio_hlid);
if (skb) {
wl->last_wlvif = wlvif;
break;
}

- /*
- * No need to continue after last_wlvif. The previous
- * pass should have found it.
- */
+next:
if (wlvif == wl->last_wlvif)
break;
}
}

+ /* no high priority skbs found - but maybe a low priority one? */
+ if (!skb && low_prio_hlid != WL12XX_INVALID_LINK_ID) {
+ struct wl1271_link *lnk = &wl->links[low_prio_hlid];
+ skb = wlcore_lnk_dequeue(wl, lnk, ac);
+
+ WARN_ON(!skb); /* we checked this before */
+ *hlid = low_prio_hlid;
+
+ /* ensure proper round robin in the vif/link levels */
+ wl->last_wlvif = lnk->wlvif;
+ if (lnk->wlvif)
+ lnk->wlvif->last_tx_hlid = low_prio_hlid;
+
+ }
+
if (!skb &&
test_and_clear_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) {
int q;
@@ -604,6 +659,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
spin_unlock_irqrestore(&wl->wl_lock, flags);
}

+out:
return skb;
}

diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 8417ee2..71137aa 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -361,6 +361,9 @@ struct wl1271 {
*/
struct wl1271_link links[WL12XX_MAX_LINKS];

+ /* Fast/slow links bitmap according to FW */
+ u32 fw_fast_lnk_map;
+
/* AP-mode - a bitmap of links currently in PS mode according to FW */
u32 ap_fw_ps_map;

--
1.7.9.5


2012-11-27 12:54:40

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 09/20] wlcore: fwlog dynamic mem_block control

On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> From: Ido Reis <[email protected]>
>
> number of fwlog mem_blocks can be configured using module param.
> this is a fw debug feature: in case a large fw log data is busrted during
> a short period of time, the memory get filled and data is lost.
> this allows us to dynamicly set the fw log mem_block usage, although
> configuring more mem_block for logger comes at the expense of TP.
>
> Signed-off-by: Yair Shapira <[email protected]>
> Signed-off-by: Ido Reis <[email protected]>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

Who's the original author of this patch? Yair's sob is above Ido's, so
apparently he was the one who created it. But the From says the author
is Ido...

--
Luca.


2012-11-28 05:54:08

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 14/20] wlcore: restore default channel configuration

On Wed, 2012-11-28 at 07:35 +0200, Luciano Coelho wrote:
> On Tue, 2012-11-27 at 23:32 +0200, Arik Nemtsov wrote:
> > I'm also not sure that having cfg80211 change a structure allocated
> > and owned by the driver is the cleanest solution for the regulatory
> > stuff.
>
> Yeah, but it has always been like that. I think the idea is probably
> the same: to avoid duplicate arrays. But if cfg80211 modifies it, it
> should be able to reset it back to the original as well. Or the driver
> must alloc a new one each time.
>
> I wonder what the other drivers do...

I had a quick look and it seems that iwlwifi has the channels struct in
eeprom, so it needs a copy anyway; ath9k seems to make a copy of the
struct before passing it to cfg80211; and ath5k seems to auto-generate
the list using different sources as input.

--
Luca.


2012-11-27 13:26:05

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 10/20] wl18xx: FDSP Code RAM Corruption fix

On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> From: Ido Reis <[email protected]>
>
> In PG2.0 there is an issue where PHY's FDSP Code RAM sometimes gets
> corrupted when exiting from ELP mode. This issue is related to FDSP
> Code RAM clock implementation.
>
> PG2.1 introduces a HW fix for this issue that requires the driver to
> change the FDSP Code Ram clock settings (mux it to ATGP clock instead
> of its own clock).
>
> This workaround uses PHY_FPGA_SPARE_1 register and is relevant to WL8
> PG2.1 devices.
>
> The fix is also backward compatible with older PG2.0 devices where the
> register PHY_FPGA_SPARE_1 is not used and not connected.
>
> The fix is done in the wl18xx_pre_upload function (must be performed
> before uploading the FW code) and includes the following steps:
>
> 1. Disable FDSP clock
> 2. Set ATPG clock toward FDSP Code RAM rather than its own clock.
> 3. Re-enable FDSP clock
>
> Signed-off-by: Yair Shapira <[email protected]>
> Signed-off-by: Ido Reis <[email protected]>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

Same question as before... Who's the actual author?


> diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
> index 4f3c142..8fd13f3 100644
> --- a/drivers/net/wireless/ti/wl18xx/main.c
> +++ b/drivers/net/wireless/ti/wl18xx/main.c
> @@ -559,6 +559,9 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
> },
> };
>
> +#define WL18XX_PHY_INIT_MEM_SIZE \
> + (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR + 4)
> +

This is ugly. Where does the 4 come from?


> @@ -585,8 +588,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
> .mem3 = { .start = 0x00000000, .size = 0x00000000 },
> },
> [PART_PHY_INIT] = {
> - .mem = { .start = 0x80926000,
> - .size = sizeof(struct wl18xx_mac_and_phy_params) },
> + .mem = { .start = WL18XX_PHY_INIT_MEM_ADDR,
> + .size = WL18XX_PHY_INIT_MEM_SIZE },
> .reg = { .start = 0x00000000, .size = 0x00000000 },
> .mem2 = { .start = 0x00000000, .size = 0x00000000 },
> .mem3 = { .start = 0x00000000, .size = 0x00000000 },
> @@ -787,6 +790,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
> u32 tmp;
> int ret;
>
> + BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) >
> + WL18XX_PHY_INIT_MEM_SIZE);

Why can't we just add the spare to the end of the mac_and_phy structure
and avoid all this hassle?

Is it because something will break if we write the same register again
with the mac_and_phy block during the wl18xx_set_mac_and_phy() call?


> @@ -803,6 +809,30 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
> wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);
>
> ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp);
> + if (ret < 0)
> + goto out;
> +
> + /* Set ATPG clock toward FDSP Code RAM rather than its own clock */

This seems to be out of place.


> + /* Disable FDSP clock */
> +
> + ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
> + if (ret < 0)
> + goto out;
> +
> + ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
> + MEM_FDSP_CLK_120_DISABLE);
> + if (ret < 0)
> + goto out;
> +
> + /* Set ATPG clock toward FDSP Code RAM rather than its own clock */
> + ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
> + MEM_FDSP_CODERAM_FUNC_CLK_SEL);

This looks strange. We're writing this value to the same register where
we disable and enable the clock. Is this really how it's supposed to
be?



> + if (ret < 0)
> + goto out;
> +
> + /* Re-enable FDSP clock */
> + ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
> + MEM_FDSP_CLK_120_ENABLE);
>
> out:
> return ret;
> diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h
> index 937b71d..ed35519 100644
> --- a/drivers/net/wireless/ti/wl18xx/reg.h
> +++ b/drivers/net/wireless/ti/wl18xx/reg.h
> @@ -188,4 +188,24 @@ enum {
> NUM_BOARD_TYPES,
> };
>
> +/*
> + * The following definitions are used to change the PHY ATPG clock towards
> + * FDSP code RAM. This is done during FW boot before we download the FW.
> + *
> + * This change is required by PG2.1 and has not impact on previous PGs.
> + */

This explanation is about the action not about the definitions here. It
would be better to have it in the pre_upload() function instead.

--
Luca.


2012-11-27 21:32:34

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH 14/20] wlcore: restore default channel configuration

On Tue, Nov 27, 2012 at 3:36 PM, Luciano Coelho <[email protected]> wrote:
> On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
>> From: Victor Goldenshtein <[email protected]>
>>
>> wlcore allocates two static structs wl1271_band_2ghz & wl1271_band_5ghz
>> which are used/modified by Reg-Domain e.g. some channel might be marked
>> as passive at some point. Make sure we don't keep stale settings around
>> if the HW is unregistered/registered during operation.
>>
>> [Arik - use Tx-power constant and tweak commit message]
>>
>> Signed-off-by: Victor Goldenshtein <[email protected]>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
>> index e68cd3f..3ced59b 100644
>> --- a/drivers/net/wireless/ti/wlcore/main.c
>> +++ b/drivers/net/wireless/ti/wlcore/main.c
>
> [...]
>
>> @@ -5537,6 +5537,7 @@ wlcore_iface_combinations[] = {
>>
>> static int wl1271_init_ieee80211(struct wl1271 *wl)
>> {
>> + int i;
>> static const u32 cipher_suites[] = {
>> WLAN_CIPHER_SUITE_WEP40,
>> WLAN_CIPHER_SUITE_WEP104,
>> @@ -5599,6 +5600,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
>> ARRAY_SIZE(wl1271_channels_5ghz) >
>> WL1271_MAX_CHANNELS);
>> /*
>> + * clear channel flags from the previous usage
>> + * and restore max_power & max_antenna_gain values.
>> + */
>> + for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
>> + wl1271_band_2ghz.channels[i].flags = 0;
>> + wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
>> + wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
>> + }
>> +
>> + for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
>> + wl1271_band_5ghz.channels[i].flags = 0;
>> + wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
>> + wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
>> + }
>> +
>> + /*
>
> This looks a bit hacky, because you must know what values are being
> modified by cfg80211. What if in the future there are more values in
> the channel structure that are being modified?
>
> The best would be to have a copy of the struct to pass to cfg80211 when
> the hardware is registered and realloc it when we unregister. That
> would be safer and more future-proof, but would use a bit more memory
> though.

Yea a new struct wasn't used because of the memory requirement. I
agree a copy is cleaner.
Let's start with this one to solve the actual bug and make a TODO for later?

I'm also not sure that having cfg80211 change a structure allocated
and owned by the driver is the cleanest solution for the regulatory
stuff.

2012-11-27 11:54:24

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH 03/20] wlcore: Allow memory access when the FW crashes

On Tue, Nov 27, 2012 at 11:34 AM, Luciano Coelho <[email protected]> wrote:
> On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
>> From: Ido Yariv <[email protected]>
>>
>> When the no_recovery flag is used, the recovery work will not restart
>> the FW and the state will not be set to 'on'. To enable post-mortem
>> analysis, allow memory access in the 'restarting' state.
>>
>> Also, since the FW might not be operational, don't fail the read/write
>> operations if elp_wakeup fails.
>>
>> Reported-by: Arkady Miasnikov <[email protected]>
>> Signed-off-by: Ido Yariv <[email protected]>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> This is weird. This patch is already in (d5560238) and the one here is
> actually just part of it and probably won't apply.

Heh. It seems Eliad and I are to blame (all the rebases can really
mess with the mind).

There's a patch by Eliad to reverse the patch you just mentioned:

commit fd8e73af670e6b57708eafe927f5d0364018b4e5
Author: Eliad Peller <[email protected]>
Date: Wed Nov 14 02:28:26 2012 +0200

wlcore: don't allow access to FW mem when chip is off

(which I submitted earlier in my part 1 series)

And the current patch simply cancels it out. So please ignore the
previous patch as well as this one. Good catch :)

Arik

2012-11-27 06:45:30

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 13/20] wlcore: consolidate free_link and always call it

Make sure free_link is always called when removing an interface. This
ensures all skbs belonging to this interface are returned to mac80211.
Otherwise these dangling skbs might crash the system on the next
call to wl1271_tx_reset_link_queues(). This happens on recovery/stop or
an unsuccessful Tx flush.

Signed-off-by: Ido Yariv <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/cmd.c | 5 +++++
drivers/net/wireless/ti/wlcore/main.c | 2 --
drivers/net/wireless/ti/wlcore/tx.c | 11 ++++++-----
3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index ead19c9..38243aa 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -339,6 +339,11 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
__clear_bit(*hlid, wlvif->links_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);

+ wl->links[*hlid].allocated_pkts = 0;
+ wl->links[*hlid].prev_freed_pkts = 0;
+ wl->links[*hlid].ba_bitmap = 0;
+ memset(wl->links[*hlid].addr, 0, ETH_ALEN);
+
/*
* At this point op_tx() will not add more packets to the queues. We
* can purge them.
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 24d5458..e68cd3f 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -4444,8 +4444,6 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid)
return;

clear_bit(hlid, wlvif->ap.sta_hlid_map);
- memset(wl->links[hlid].addr, 0, ETH_ALEN);
- wl->links[hlid].ba_bitmap = 0;
__clear_bit(hlid, &wl->ap_ps_map);
__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
wl12xx_free_link(wl, wlvif, &hlid);
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 404a516..2e91a37 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -1004,13 +1004,14 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)

/* TX failure */
for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
- if (wlvif->bss_type == BSS_TYPE_AP_BSS)
+ if (wlvif->bss_type == BSS_TYPE_AP_BSS) {
+ /* this calls wl12xx_free_link */
wl1271_free_sta(wl, wlvif, i);
- else
+ } else {
+ u8 hlid = i;
wlvif->sta.ba_rx_bitmap = 0;
-
- wl->links[i].allocated_pkts = 0;
- wl->links[i].prev_freed_pkts = 0;
+ wl12xx_free_link(wl, wlvif, &hlid);
+ }
}
wlvif->last_tx_hlid = 0;

--
1.7.9.5


2012-11-27 06:45:40

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 18/20] wlcore: track FW-allocated packets per link

Move FW-allocation tracking code to the fw_status function and track
allocations made by all links. These will be incorporated in the
improved Tx scheduling algorithm.

Manually zero the system link counters on op_stop, as this link is not
allocated the normal way.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 29 +++++++++++++++++------------
drivers/net/wireless/ti/wlcore/tx.c | 4 +---
2 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index d858deb..c557678 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -374,9 +374,8 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct wl_fw_status_2 *status)
{
- struct wl1271_link *lnk;
u32 cur_fw_ps_map;
- u8 hlid, cnt;
+ u8 hlid;

/* TODO: also use link_fast_bitmap here */

@@ -390,17 +389,9 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
wl->ap_fw_ps_map = cur_fw_ps_map;
}

- for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) {
- lnk = &wl->links[hlid];
- cnt = status->counters.tx_lnk_free_pkts[hlid] -
- lnk->prev_freed_pkts;
-
- lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[hlid];
- lnk->allocated_pkts -= cnt;
-
+ for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
- lnk->allocated_pkts);
- }
+ wl->links[hlid].allocated_pkts);
}

static int wlcore_fw_status(struct wl1271 *wl,
@@ -414,6 +405,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
int i;
size_t status_len;
int ret;
+ struct wl1271_link *lnk;

status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
sizeof(*status_2) + wl->fw_status_priv_len;
@@ -439,6 +431,17 @@ static int wlcore_fw_status(struct wl1271 *wl,
wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
}

+
+ for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+ lnk = &wl->links[i];
+ /* prevent wrap-around in freed-packets counter */
+ lnk->allocated_pkts -=
+ (status_2->counters.tx_lnk_free_pkts[i] -
+ lnk->prev_freed_pkts) & 0xff;
+
+ lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+ }
+
/* prevent wrap-around in total blocks counter */
if (likely(wl->tx_blocks_freed <=
le32_to_cpu(status_2->total_released_blks)))
@@ -1901,6 +1904,8 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
wl->active_sta_count = 0;

/* The system link is always allocated */
+ wl->links[WL12XX_SYSTEM_HLID].allocated_pkts = 0;
+ wl->links[WL12XX_SYSTEM_HLID].prev_freed_pkts = 0;
__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);

/*
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index a5fa54d..5ce6248 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -224,9 +224,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
wl->tx_allocated_pkts[ac]++;

- if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
- wlvif->bss_type == BSS_TYPE_AP_BSS &&
- test_bit(hlid, wlvif->ap.sta_hlid_map))
+ if (test_bit(hlid, wl->links_map))
wl->links[hlid].allocated_pkts++;

ret = 0;
--
1.7.9.5


2012-11-27 13:37:26

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 14/20] wlcore: restore default channel configuration

On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> From: Victor Goldenshtein <[email protected]>
>
> wlcore allocates two static structs wl1271_band_2ghz & wl1271_band_5ghz
> which are used/modified by Reg-Domain e.g. some channel might be marked
> as passive at some point. Make sure we don't keep stale settings around
> if the HW is unregistered/registered during operation.
>
> [Arik - use Tx-power constant and tweak commit message]
>
> Signed-off-by: Victor Goldenshtein <[email protected]>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
> index e68cd3f..3ced59b 100644
> --- a/drivers/net/wireless/ti/wlcore/main.c
> +++ b/drivers/net/wireless/ti/wlcore/main.c

[...]

> @@ -5537,6 +5537,7 @@ wlcore_iface_combinations[] = {
>
> static int wl1271_init_ieee80211(struct wl1271 *wl)
> {
> + int i;
> static const u32 cipher_suites[] = {
> WLAN_CIPHER_SUITE_WEP40,
> WLAN_CIPHER_SUITE_WEP104,
> @@ -5599,6 +5600,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
> ARRAY_SIZE(wl1271_channels_5ghz) >
> WL1271_MAX_CHANNELS);
> /*
> + * clear channel flags from the previous usage
> + * and restore max_power & max_antenna_gain values.
> + */
> + for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
> + wl1271_band_2ghz.channels[i].flags = 0;
> + wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
> + wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
> + }
> +
> + for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
> + wl1271_band_5ghz.channels[i].flags = 0;
> + wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
> + wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
> + }
> +
> + /*

This looks a bit hacky, because you must know what values are being
modified by cfg80211. What if in the future there are more values in
the channel structure that are being modified?

The best would be to have a copy of the struct to pass to cfg80211 when
the hardware is registered and realloc it when we unregister. That
would be safer and more future-proof, but would use a bit more memory
though.

--
Luca.


2012-11-27 06:45:15

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 04/20] wlcore: allow fw commands to fail

From: Eliad Peller <[email protected]>

Currently, each fw command/acx that return a status code
different than CMD_STATUS_SUCCESS will trigger a recovery
in the driver.

However, it is a valid for some fw commands to fail (e.g.
due to temporary lack of resources), so add new functions
that allow passing bitmap of valid error return values.

(make the current wl1271_cmd_send/wl1271_cmd_configure
wrappers around the new functions, in order to avoid
changing the whole driver)

Signed-off-by: Eliad Peller <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/cmd.c | 85 +++++++++++++++++++++++++++-------
drivers/net/wireless/ti/wlcore/cmd.h | 7 ++-
2 files changed, 73 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index fc508ee..ead19c9 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -48,14 +48,15 @@
* @id: command id
* @buf: buffer containing the command, must work with dma
* @len: length of the buffer
+ * return the cmd status code on success.
*/
-int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
- size_t res_len)
+static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
+ size_t len, size_t res_len)
{
struct wl1271_cmd_header *cmd;
unsigned long timeout;
u32 intr;
- int ret = 0;
+ int ret;
u16 status;
u16 poll_count = 0;

@@ -71,7 +72,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,

ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false);
if (ret < 0)
- goto fail;
+ return ret;

/*
* TODO: we just need this because one bit is in a different
@@ -79,19 +80,18 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
*/
ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len);
if (ret < 0)
- goto fail;
+ return ret;

timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT);

ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
if (ret < 0)
- goto fail;
+ return ret;

while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) {
if (time_after(jiffies, timeout)) {
wl1271_error("command complete timeout");
- ret = -ETIMEDOUT;
- goto fail;
+ return -ETIMEDOUT;
}

poll_count++;
@@ -102,7 +102,7 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,

ret = wlcore_read_reg(wl, REG_INTERRUPT_NO_CLEAR, &intr);
if (ret < 0)
- goto fail;
+ return ret;
}

/* read back the status code of the command */
@@ -111,22 +111,40 @@ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,

ret = wlcore_read(wl, wl->cmd_box_addr, cmd, res_len, false);
if (ret < 0)
- goto fail;
+ return ret;

status = le16_to_cpu(cmd->status);
- if (status != CMD_STATUS_SUCCESS) {
- wl1271_error("command execute failure %d", status);
- ret = -EIO;
- goto fail;
- }

ret = wlcore_write_reg(wl, REG_INTERRUPT_ACK,
WL1271_ACX_INTR_CMD_COMPLETE);
if (ret < 0)
+ return ret;
+
+ return status;
+}
+
+/*
+ * send command to fw and return cmd status on success
+ * valid_rets contains a bitmap of allowed error codes
+ */
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+ size_t res_len, unsigned long valid_rets)
+{
+ int ret = __wlcore_cmd_send(wl, id, buf, len, res_len);
+
+ if (ret < 0)
goto fail;

- return 0;
+ /* success is always a valid status */
+ valid_rets |= BIT(CMD_STATUS_SUCCESS);

+ if (ret >= MAX_COMMAND_STATUS ||
+ !test_bit(ret, &valid_rets)) {
+ wl1271_error("command execute failure %d", ret);
+ ret = -EIO;
+ goto fail;
+ }
+ return ret;
fail:
wl12xx_queue_recovery_work(wl);
return ret;
@@ -134,6 +152,20 @@ fail:
EXPORT_SYMBOL_GPL(wl1271_cmd_send);

/*
+ * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS
+ * return 0 on success.
+ */
+int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
+ size_t res_len)
+{
+ int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0);
+
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+/*
* Poll the mailbox event field until any of the bits in the mask is set or a
* timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
*/
@@ -791,8 +823,11 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len)
* @id: acx id
* @buf: buffer containing acx, including all headers, must work with dma
* @len: length of buf
+ * @valid_rets: bitmap of valid cmd status codes (i.e. return values).
+ * return the cmd status on success.
*/
-int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+ size_t len, unsigned long valid_rets)
{
struct acx_header *acx = buf;
int ret;
@@ -804,12 +839,26 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
/* payload length, does not include any headers */
acx->len = cpu_to_le16(len - sizeof(*acx));

- ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len, 0);
+ ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0,
+ valid_rets);
if (ret < 0) {
wl1271_warning("CONFIGURE command NOK");
return ret;
}

+ return ret;
+}
+
+/*
+ * wrapper for wlcore_cmd_configure that accepts only success status.
+ * return 0 on success
+ */
+int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
+{
+ int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0);
+
+ if (ret < 0)
+ return ret;
return 0;
}
EXPORT_SYMBOL_GPL(wl1271_cmd_configure);
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index 7f378b7..fd34123 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -31,6 +31,8 @@ struct acx_header;

int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len);
+int wlcore_cmd_send_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len,
+ size_t res_len, unsigned long valid_rets);
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
u8 *role_id);
int wl12xx_cmd_role_disable(struct wl1271 *wl, u8 *role_id);
@@ -45,6 +47,8 @@ int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif);
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);
+int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
+ size_t len, unsigned long valid_rets);
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, u16 auto_ps_timeout);
@@ -234,7 +238,8 @@ enum {
CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/
CMD_STATUS_TEMPLATE_OOM = 23,
CMD_STATUS_NO_RX_BA_SESSION = 24,
- MAX_COMMAND_STATUS = 0xff
+
+ MAX_COMMAND_STATUS
};

#define CMDMBOX_HEADER_LEN 4
--
1.7.9.5


2012-11-28 05:36:08

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 14/20] wlcore: restore default channel configuration

On Tue, 2012-11-27 at 23:32 +0200, Arik Nemtsov wrote:
> On Tue, Nov 27, 2012 at 3:36 PM, Luciano Coelho <[email protected]> wrote:
> > On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> >> From: Victor Goldenshtein <[email protected]>
> >>
> >> wlcore allocates two static structs wl1271_band_2ghz & wl1271_band_5ghz
> >> which are used/modified by Reg-Domain e.g. some channel might be marked
> >> as passive at some point. Make sure we don't keep stale settings around
> >> if the HW is unregistered/registered during operation.
> >>
> >> [Arik - use Tx-power constant and tweak commit message]
> >>
> >> Signed-off-by: Victor Goldenshtein <[email protected]>
> >> Signed-off-by: Arik Nemtsov <[email protected]>
> >> ---
> >
> > [...]
> >
> >> diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
> >> index e68cd3f..3ced59b 100644
> >> --- a/drivers/net/wireless/ti/wlcore/main.c
> >> +++ b/drivers/net/wireless/ti/wlcore/main.c
> >
> > [...]
> >
> >> @@ -5537,6 +5537,7 @@ wlcore_iface_combinations[] = {
> >>
> >> static int wl1271_init_ieee80211(struct wl1271 *wl)
> >> {
> >> + int i;
> >> static const u32 cipher_suites[] = {
> >> WLAN_CIPHER_SUITE_WEP40,
> >> WLAN_CIPHER_SUITE_WEP104,
> >> @@ -5599,6 +5600,22 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
> >> ARRAY_SIZE(wl1271_channels_5ghz) >
> >> WL1271_MAX_CHANNELS);
> >> /*
> >> + * clear channel flags from the previous usage
> >> + * and restore max_power & max_antenna_gain values.
> >> + */
> >> + for (i = 0; i < ARRAY_SIZE(wl1271_channels); i++) {
> >> + wl1271_band_2ghz.channels[i].flags = 0;
> >> + wl1271_band_2ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
> >> + wl1271_band_2ghz.channels[i].max_antenna_gain = 0;
> >> + }
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(wl1271_channels_5ghz); i++) {
> >> + wl1271_band_5ghz.channels[i].flags = 0;
> >> + wl1271_band_5ghz.channels[i].max_power = WLCORE_MAX_TXPWR;
> >> + wl1271_band_5ghz.channels[i].max_antenna_gain = 0;
> >> + }
> >> +
> >> + /*
> >
> > This looks a bit hacky, because you must know what values are being
> > modified by cfg80211. What if in the future there are more values in
> > the channel structure that are being modified?
> >
> > The best would be to have a copy of the struct to pass to cfg80211 when
> > the hardware is registered and realloc it when we unregister. That
> > would be safer and more future-proof, but would use a bit more memory
> > though.
>
> Yea a new struct wasn't used because of the memory requirement. I
> agree a copy is cleaner.
> Let's start with this one to solve the actual bug and make a TODO for later?

Sure, let's add it to the TOneverbeDOne list. :P


> I'm also not sure that having cfg80211 change a structure allocated
> and owned by the driver is the cleanest solution for the regulatory
> stuff.

Yeah, but it has always been like that. I think the idea is probably
the same: to avoid duplicate arrays. But if cfg80211 modifies it, it
should be able to reset it back to the original as well. Or the driver
must alloc a new one each time.

I wonder what the other drivers do...

--
Luca.


2012-11-29 12:51:53

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 20/20] wl18xx: set last Tx rate from FW status

On Tue, 2012-11-27 at 08:45 +0200, Arik Nemtsov wrote:
> Obtain the last Tx rate from the FW status and translate it to
> the mac80211 rate+flag format before sending it up via the Tx status.
>
> Bump up the min FW version to the first FW that supports the rate byte.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> +static
> +void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
> + u8 *tx_rate, u8 *tx_rate_flags)

[...]

> + /*
> + * first pass info->control.vif while it's valid, and then fill out
> + * the info->status structures
> + */
> + wl18xx_get_last_tx_rate(wl, info->control.vif,
> + &info->status.rates[0].idx,
> + &info->status.rates[0].flags);

This doesn't work anymore, because commit 8bc83c24 (mac80211: support
VHT rates in TX info) changed the struct like this:

@@ -560,10 +568,32 @@ enum mac80211_rate_control_flags {
*/
struct ieee80211_tx_rate {
s8 idx;
- u8 count;
- u8 flags;
+ u16 count:5,
+ flags:11;
} __packed;

So you can't get the address of flags anymore.

Do you mind respinning it?

--
Luca.


2012-11-27 06:45:21

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 08/20] wl18xx: declare support for greenfield ht_cap

From: Eliad Peller <[email protected]>

The 18xx fw supports greenfield so add the
IEEE80211_HT_CAP_GRN_FLD flag to the supported
ht capabilities flags.

Signed-off-by: Eliad Peller <[email protected]>
Signed-off-by: Ido Reis <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl18xx/main.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 635f8b7..4f3c142 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -1424,7 +1424,8 @@ static struct wlcore_ops wl18xx_ops = {
/* HT cap appropriate for wide channels in 2Ghz */
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40,
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_DSSSCCK40 |
+ IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1438,7 +1439,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_2ghz = {
/* HT cap appropriate for wide channels in 5Ghz */
static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {
.cap = IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 |
- IEEE80211_HT_CAP_SUP_WIDTH_20_40,
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1451,7 +1453,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso40_ht_cap_5ghz = {

/* HT cap appropriate for SISO 20 */
static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {
- .cap = IEEE80211_HT_CAP_SGI_20,
+ .cap = IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
@@ -1464,7 +1467,8 @@ static struct ieee80211_sta_ht_cap wl18xx_siso20_ht_cap = {

/* HT cap appropriate for MIMO rates in 20mhz channel */
static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
- .cap = IEEE80211_HT_CAP_SGI_20,
+ .cap = IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_GRN_FLD,
.ht_supported = true,
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K,
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
--
1.7.9.5


2012-11-27 06:45:34

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 15/20] wlcore: initialize per-link FW freed blocks correctly

When a link is allocated, sometimes the "freed packets" counter in FW
is non zero, but we always assumed it is. This caused us to incorrectly
account FW allocated blocks in some cases.

When operating in AP mode, this bug caused some stations to never
come back from PSM.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/cmd.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 38243aa..e00d64b 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -322,6 +322,10 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
__set_bit(link, wl->links_map);
__set_bit(link, wlvif->links_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);
+
+ /* take the last "freed packets" value from the current FW status */
+ wl->links[link].prev_freed_pkts =
+ wl->fw_status_2->counters.tx_lnk_free_pkts[link];
*hlid = link;
return 0;
}
--
1.7.9.5


2012-11-27 06:45:25

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 10/20] wl18xx: FDSP Code RAM Corruption fix

From: Ido Reis <[email protected]>

In PG2.0 there is an issue where PHY's FDSP Code RAM sometimes gets
corrupted when exiting from ELP mode. This issue is related to FDSP
Code RAM clock implementation.

PG2.1 introduces a HW fix for this issue that requires the driver to
change the FDSP Code Ram clock settings (mux it to ATGP clock instead
of its own clock).

This workaround uses PHY_FPGA_SPARE_1 register and is relevant to WL8
PG2.1 devices.

The fix is also backward compatible with older PG2.0 devices where the
register PHY_FPGA_SPARE_1 is not used and not connected.

The fix is done in the wl18xx_pre_upload function (must be performed
before uploading the FW code) and includes the following steps:

1. Disable FDSP clock
2. Set ATPG clock toward FDSP Code RAM rather than its own clock.
3. Re-enable FDSP clock

Signed-off-by: Yair Shapira <[email protected]>
Signed-off-by: Ido Reis <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl18xx/main.c | 34 +++++++++++++++++++++++++++++++--
drivers/net/wireless/ti/wl18xx/reg.h | 20 +++++++++++++++++++
2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 4f3c142..8fd13f3 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -559,6 +559,9 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
},
};

+#define WL18XX_PHY_INIT_MEM_SIZE \
+ (WL18XX_PHY_END_MEM_ADDR - WL18XX_PHY_INIT_MEM_ADDR + 4)
+
static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
[PART_TOP_PRCM_ELP_SOC] = {
.mem = { .start = 0x00A02000, .size = 0x00010000 },
@@ -585,8 +588,8 @@ static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = {
.mem3 = { .start = 0x00000000, .size = 0x00000000 },
},
[PART_PHY_INIT] = {
- .mem = { .start = 0x80926000,
- .size = sizeof(struct wl18xx_mac_and_phy_params) },
+ .mem = { .start = WL18XX_PHY_INIT_MEM_ADDR,
+ .size = WL18XX_PHY_INIT_MEM_SIZE },
.reg = { .start = 0x00000000, .size = 0x00000000 },
.mem2 = { .start = 0x00000000, .size = 0x00000000 },
.mem3 = { .start = 0x00000000, .size = 0x00000000 },
@@ -787,6 +790,9 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
u32 tmp;
int ret;

+ BUILD_BUG_ON(sizeof(struct wl18xx_mac_and_phy_params) >
+ WL18XX_PHY_INIT_MEM_SIZE);
+
ret = wlcore_set_partition(wl, &wl->ptable[PART_BOOT]);
if (ret < 0)
goto out;
@@ -803,6 +809,30 @@ static int wl18xx_pre_upload(struct wl1271 *wl)
wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp);

ret = wlcore_read32(wl, WL18XX_SCR_PAD2, &tmp);
+ if (ret < 0)
+ goto out;
+
+ /* Set ATPG clock toward FDSP Code RAM rather than its own clock */
+ /* Disable FDSP clock */
+
+ ret = wlcore_set_partition(wl, &wl->ptable[PART_PHY_INIT]);
+ if (ret < 0)
+ goto out;
+
+ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+ MEM_FDSP_CLK_120_DISABLE);
+ if (ret < 0)
+ goto out;
+
+ /* Set ATPG clock toward FDSP Code RAM rather than its own clock */
+ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+ MEM_FDSP_CODERAM_FUNC_CLK_SEL);
+ if (ret < 0)
+ goto out;
+
+ /* Re-enable FDSP clock */
+ ret = wlcore_write32(wl, WL18XX_PHY_FPGA_SPARE_1,
+ MEM_FDSP_CLK_120_ENABLE);

out:
return ret;
diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h
index 937b71d..ed35519 100644
--- a/drivers/net/wireless/ti/wl18xx/reg.h
+++ b/drivers/net/wireless/ti/wl18xx/reg.h
@@ -188,4 +188,24 @@ enum {
NUM_BOARD_TYPES,
};

+/*
+ * The following definitions are used to change the PHY ATPG clock towards
+ * FDSP code RAM. This is done during FW boot before we download the FW.
+ *
+ * This change is required by PG2.1 and has not impact on previous PGs.
+ */
+
+/* FPGA_SPARE_1 register - bits are set as described below */
+#define WL18XX_PHY_FPGA_SPARE_1 0x8093CA40
+#define WL18XX_PHY_END_MEM_ADDR WL18XX_PHY_FPGA_SPARE_1
+
+/* Bit to disable FDSP clock */
+#define MEM_FDSP_CLK_120_DISABLE 0x80000000
+
+/* Bit to set ATPG clock toward FDSP Code RAM rather than its own clock */
+#define MEM_FDSP_CODERAM_FUNC_CLK_SEL 0xC0000000
+
+/* Bit to re-enable FDSP clock */
+#define MEM_FDSP_CLK_120_ENABLE 0x40000000
+
#endif /* __REG_H__ */
--
1.7.9.5


2012-11-27 06:45:27

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 11/20] wlcore: remove unnecessary WARN_ON in wl12xx_tx_reset

From: Victor Goldenshtein <[email protected]>

Sometimes the driver can perform a recovery while Tx is
active, this will trigger unnecessary warning which might
delay the recovery for more than 100 mS.

Signed-off-by: Victor Goldenshtein <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/tx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 68f73f9..404a516 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -1023,7 +1023,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
struct ieee80211_tx_info *info;

/* only reset the queues if something bad happened */
- if (WARN_ON_ONCE(wl1271_tx_total_queue_count(wl) != 0)) {
+ if (wl1271_tx_total_queue_count(wl) != 0) {
for (i = 0; i < WL12XX_MAX_LINKS; i++)
wl1271_tx_reset_link_queues(wl, i);

--
1.7.9.5


2012-11-27 06:45:29

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 12/20] wlcore: take the mutex before resetting Tx queues

Otherwise we risk contention for private members of our global structure
while op_stop_locked is running.

Reported-by: Ido Yariv <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 8932cca..24d5458 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -1867,8 +1867,8 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
cancel_delayed_work_sync(&wl->tx_watchdog_work);

/* let's notify MAC80211 about the remaining pending TX frames */
- wl12xx_tx_reset(wl);
mutex_lock(&wl->mutex);
+ wl12xx_tx_reset(wl);

wl1271_power_off(wl);
/*
--
1.7.9.5


2012-11-29 15:50:13

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 20/20] wl18xx: set last Tx rate from FW status

On Thu, 2012-11-29 at 17:33 +0200, Arik Nemtsov wrote:
> On Thu, Nov 29, 2012 at 2:51 PM, Luciano Coelho <[email protected]> wrote:
> > On Tue, 2012-11-27 at 08:45 +0200, Arik Nemtsov wrote:
> >> Obtain the last Tx rate from the FW status and translate it to
> >> the mac80211 rate+flag format before sending it up via the Tx status.
> >>
> >> Bump up the min FW version to the first FW that supports the rate byte.
> >>
> >> Signed-off-by: Arik Nemtsov <[email protected]>
> >> ---
> >
> > [...]
> >
> >> +static
> >> +void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
> >> + u8 *tx_rate, u8 *tx_rate_flags)
> >
> > [...]
> >
> >> + /*
> >> + * first pass info->control.vif while it's valid, and then fill out
> >> + * the info->status structures
> >> + */
> >> + wl18xx_get_last_tx_rate(wl, info->control.vif,
> >> + &info->status.rates[0].idx,
> >> + &info->status.rates[0].flags);
> >
> > This doesn't work anymore, because commit 8bc83c24 (mac80211: support
> > VHT rates in TX info) changed the struct like this:
> >
> > @@ -560,10 +568,32 @@ enum mac80211_rate_control_flags {
> > */
> > struct ieee80211_tx_rate {
> > s8 idx;
> > - u8 count;
> > - u8 flags;
> > + u16 count:5,
> > + flags:11;
> > } __packed;
> >
> > So you can't get the address of flags anymore.
> >
> > Do you mind respinning it?
>
> Technically you should do it, since your master branch was pointing to
> an older revision at the time. But I'm feeling generous.

Well, yes I should do it, kind of.


> I'll also rebase the part 3 series on top of the latest code to fix
> any breakage. So I'll at least re-spin the patches containing
> ieee80211_iterate_active_interfaces() (as the prototype was changed)

Thanks! :)

--
Luca.


2012-11-29 15:33:20

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH 20/20] wl18xx: set last Tx rate from FW status

On Thu, Nov 29, 2012 at 2:51 PM, Luciano Coelho <[email protected]> wrote:
> On Tue, 2012-11-27 at 08:45 +0200, Arik Nemtsov wrote:
>> Obtain the last Tx rate from the FW status and translate it to
>> the mac80211 rate+flag format before sending it up via the Tx status.
>>
>> Bump up the min FW version to the first FW that supports the rate byte.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> +static
>> +void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
>> + u8 *tx_rate, u8 *tx_rate_flags)
>
> [...]
>
>> + /*
>> + * first pass info->control.vif while it's valid, and then fill out
>> + * the info->status structures
>> + */
>> + wl18xx_get_last_tx_rate(wl, info->control.vif,
>> + &info->status.rates[0].idx,
>> + &info->status.rates[0].flags);
>
> This doesn't work anymore, because commit 8bc83c24 (mac80211: support
> VHT rates in TX info) changed the struct like this:
>
> @@ -560,10 +568,32 @@ enum mac80211_rate_control_flags {
> */
> struct ieee80211_tx_rate {
> s8 idx;
> - u8 count;
> - u8 flags;
> + u16 count:5,
> + flags:11;
> } __packed;
>
> So you can't get the address of flags anymore.
>
> Do you mind respinning it?

Technically you should do it, since your master branch was pointing to
an older revision at the time. But I'm feeling generous.

I'll also rebase the part 3 series on top of the latest code to fix
any breakage. So I'll at least re-spin the patches containing
ieee80211_iterate_active_interfaces() (as the prototype was changed)

Arik

2012-11-27 09:23:33

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 01/20] wl18xx: update default mac/phy parameters

On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> From: Ido Reis <[email protected]>
>
> Update mac/phy paramters according to the default HP SISO boards.
>
> Signed-off-by: Ido Reis <[email protected]>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---
> drivers/net/wireless/ti/wl18xx/conf.h | 17 +++++++++++------
> drivers/net/wireless/ti/wl18xx/main.c | 7 ++++---
> 2 files changed, 15 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
> index 4d426cc..f020c1c 100644
> --- a/drivers/net/wireless/ti/wl18xx/conf.h
> +++ b/drivers/net/wireless/ti/wl18xx/conf.h
> @@ -23,20 +23,21 @@
> #define __WL18XX_CONF_H__
>
> #define WL18XX_CONF_MAGIC 0x10e100ca
> -#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0003)
> +#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0004)
> #define WL18XX_CONF_MASK 0x0000ffff
> #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \
> sizeof(struct wl18xx_priv_conf))
>
> #define NUM_OF_CHANNELS_11_ABG 150
> #define NUM_OF_CHANNELS_11_P 7
> -#define WL18XX_NUM_OF_SUB_BANDS 9
> #define SRF_TABLE_LEN 16
> #define PIN_MUXING_SIZE 2
> +#define NUM_OF_TRACE_LOSS_GAPS_TX 10
> +#define NUM_OF_TRACE_LOSS_GAPS_RX 18

I'll s/NUM_OF/WL18XX/ here when I apply.


> diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
> index e71e2cf..30222cb 100644
> --- a/drivers/net/wireless/ti/wl18xx/main.c
> +++ b/drivers/net/wireless/ti/wl18xx/main.c

[...]

> @@ -525,13 +524,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
> .enable_tx_low_pwr_on_siso_rdl = 0x00,
> .rx_profile = 0x00,
> .pwr_limit_reference_11_abg = 0xc8,
> + .pwr_limit_reference_11p = 0xc8,
> .psat = 0,
> .low_power_val = 0x00,
> - .med_power_val = 0x0a,
> - .high_power_val = 0x1e,
> + .med_power_val = 0x0A,
> + .high_power_val = 0x11,

The change from 0x0a to 0x0A shouldn't be here. I'll remove it.

--
Luca.


2012-11-27 06:45:11

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 02/20] wl18xx: support 2nd set of mac/phy tx-power params

From: Yair Shapira <[email protected]>

First set (low, medium and high TX power values) is used
for STA-HP background role. The 2nd set is used for other roles.

Update other mac/phy parameters according to new FW.

Signed-off-by: Yair Shapira <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl18xx/conf.h | 10 +++++++---
drivers/net/wireless/ti/wl18xx/main.c | 35 +++++++++++++++++++++++++++------
2 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h
index f020c1c..4e1313b 100644
--- a/drivers/net/wireless/ti/wl18xx/conf.h
+++ b/drivers/net/wireless/ti/wl18xx/conf.h
@@ -23,7 +23,7 @@
#define __WL18XX_CONF_H__

#define WL18XX_CONF_MAGIC 0x10e100ca
-#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0004)
+#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0005)
#define WL18XX_CONF_MASK 0x0000ffff
#define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \
sizeof(struct wl18xx_priv_conf))
@@ -78,15 +78,19 @@ struct wl18xx_mac_and_phy_params {
u8 board_type;
/* enable point saturation */
u8 psat;
- /* low/medium/high Tx power in dBm */
+ /* low/medium/high Tx power in dBm for STA-HP BG */
s8 low_power_val;
s8 med_power_val;
s8 high_power_val;
s8 per_sub_band_tx_trace_loss[NUM_OF_TRACE_LOSS_GAPS_TX];
s8 per_sub_band_rx_trace_loss[NUM_OF_TRACE_LOSS_GAPS_RX];
u8 tx_rf_margin;
+ /* low/medium/high Tx power in dBm for other role */
+ s8 low_power_val_2nd;
+ s8 med_power_val_2nd;
+ s8 high_power_val_2nd;

- u8 padding[4];
+ u8 padding[1];
} __packed;

enum wl18xx_ht_mode {
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 30222cb..617e8d1 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -523,14 +523,37 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = {
.enable_clpc = 0x00,
.enable_tx_low_pwr_on_siso_rdl = 0x00,
.rx_profile = 0x00,
- .pwr_limit_reference_11_abg = 0xc8,
- .pwr_limit_reference_11p = 0xc8,
+ .pwr_limit_reference_11_abg = 0x64,
+ .per_chan_pwr_limit_arr_11abg = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
+ .pwr_limit_reference_11p = 0x64,
+ .per_chan_pwr_limit_arr_11p = { 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff },
.psat = 0,
- .low_power_val = 0x00,
- .med_power_val = 0x0A,
- .high_power_val = 0x11,
+ .low_power_val = 0x08,
+ .med_power_val = 0x12,
+ .high_power_val = 0x18,
+ .low_power_val_2nd = 0x05,
+ .med_power_val_2nd = 0x0A,
+ .high_power_val_2nd = 0x14,
.external_pa_dc2dc = 0,
- .number_of_assembled_ant2_4 = 1,
+ .number_of_assembled_ant2_4 = 2,
.number_of_assembled_ant5 = 1,
.tx_rf_margin = 1,
},
--
1.7.9.5


2012-11-27 06:45:36

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 16/20] wlcore: track wlvif inside per-link structure

This allows us to pass only the link as a parameter to various functions
and deduce the wlvif. Note that this member will be NULL for global
links.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/cmd.c | 2 ++
drivers/net/wireless/ti/wlcore/wlcore_i.h | 5 +++++
2 files changed, 7 insertions(+)

diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index e00d64b..56432c8 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -326,6 +326,7 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
/* take the last "freed packets" value from the current FW status */
wl->links[link].prev_freed_pkts =
wl->fw_status_2->counters.tx_lnk_free_pkts[link];
+ wl->links[link].wlvif = wlvif;
*hlid = link;
return 0;
}
@@ -353,6 +354,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
* can purge them.
*/
wl1271_tx_reset_link_queues(wl, *hlid);
+ wl->links[*hlid].wlvif = NULL;

*hlid = WL12XX_INVALID_LINK_ID;
}
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index 5a92cb2..f86c716 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -260,6 +260,8 @@ enum wl12xx_vif_flags {
WLVIF_FLAG_IN_USE,
};

+struct wl12xx_vif;
+
struct wl1271_link {
/* AP-mode - TX queue per AC in link */
struct sk_buff_head tx_queue[NUM_TX_QUEUES];
@@ -272,6 +274,9 @@ struct wl1271_link {

/* bitmap of TIDs where RX BA sessions are active for this link */
u8 ba_bitmap;
+
+ /* The wlvif this link belongs to. Might be null for global links */
+ struct wl12xx_vif *wlvif;
};

#define WL1271_MAX_RX_FILTERS 5
--
1.7.9.5


2012-11-27 06:45:18

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 06/20] wlcore: remove WLCORE_QUIRK_NO_ELP

From: Eliad Peller <[email protected]>

all the current firmwares support elp, so
we can safely remove WLCORE_QUIRK_NO_ELP.

Signed-off-by: Eliad Peller <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl18xx/main.c | 3 +--
drivers/net/wireless/ti/wlcore/init.c | 3 ---
drivers/net/wireless/ti/wlcore/main.c | 3 ---
drivers/net/wireless/ti/wlcore/wlcore.h | 3 ---
4 files changed, 1 insertion(+), 11 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 617e8d1..635f8b7 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -639,8 +639,7 @@ static int wl18xx_identify_chip(struct wl1271 *wl)
wl->sr_fw_name = WL18XX_FW_NAME;
/* wl18xx uses the same firmware for PLT */
wl->plt_fw_name = WL18XX_FW_NAME;
- wl->quirks |= WLCORE_QUIRK_NO_ELP |
- WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
+ wl->quirks |= WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |
WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN |
WLCORE_QUIRK_TX_PAD_LAST_FRAME |
diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c
index 68828b4..5c6f11e 100644
--- a/drivers/net/wireless/ti/wlcore/init.c
+++ b/drivers/net/wireless/ti/wlcore/init.c
@@ -577,9 +577,6 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif)
/* Configure for power according to debugfs */
if (sta_auth != WL1271_PSM_ILLEGAL)
ret = wl1271_acx_sleep_auth(wl, sta_auth);
- /* Configure for power always on */
- else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
- ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
/* Configure for ELP power saving */
else
ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 56f20d6..05ba72a 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2484,9 +2484,6 @@ deinit:
/* Configure for power according to debugfs */
if (sta_auth != WL1271_PSM_ILLEGAL)
wl1271_acx_sleep_auth(wl, sta_auth);
- /* Configure for power always on */
- else if (wl->quirks & WLCORE_QUIRK_NO_ELP)
- wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
/* Configure for ELP power saving */
else
wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 4d5c69e..07898de 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -501,9 +501,6 @@ wlcore_set_min_fw_ver(struct wl1271 *wl, unsigned int chip,
/* Older firmwares use an old NVS format */
#define WLCORE_QUIRK_LEGACY_NVS BIT(5)

-/* Some firmwares may not support ELP */
-#define WLCORE_QUIRK_NO_ELP BIT(6)
-
/* pad only the last frame in the aggregate buffer */
#define WLCORE_QUIRK_TX_PAD_LAST_FRAME BIT(7)

--
1.7.9.5


2012-11-27 06:45:38

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 17/20] wlcore: count packets held per AC in each vif

This accounting will help find a vif that has data in a specific AC.
Otherwise we have to traverse all the links, which can be lengthy for
the AP case.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/debugfs.c | 4 ++++
drivers/net/wireless/ti/wlcore/main.c | 2 ++
drivers/net/wireless/ti/wlcore/ps.c | 8 ++++++--
drivers/net/wireless/ti/wlcore/tx.c | 16 ++++++++++++++--
drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 +++
5 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index e61fc2b..f115fba 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -577,6 +577,10 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
VIF_STATE_PRINT_INT(ap.ucast_rate_idx[3]);
}
VIF_STATE_PRINT_INT(last_tx_hlid);
+ VIF_STATE_PRINT_INT(tx_queue_count[0]);
+ VIF_STATE_PRINT_INT(tx_queue_count[1]);
+ VIF_STATE_PRINT_INT(tx_queue_count[2]);
+ VIF_STATE_PRINT_INT(tx_queue_count[3]);
VIF_STATE_PRINT_LHEX(links_map[0]);
VIF_STATE_PRINT_NSTR(ssid, wlvif->ssid_len);
VIF_STATE_PRINT_INT(band);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 3ced59b..d858deb 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -1221,6 +1221,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw,
skb_queue_tail(&wl->links[hlid].tx_queue[q], skb);

wl->tx_queue_count[q]++;
+ if (wlvif)
+ wlvif->tx_queue_count[q]++;

/*
* The workqueue is slow to process the tx_queue and we need stop
diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c
index ffcd843..9b7b6e2 100644
--- a/drivers/net/wireless/ti/wlcore/ps.c
+++ b/drivers/net/wireless/ti/wlcore/ps.c
@@ -239,11 +239,12 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
struct ieee80211_tx_info *info;
unsigned long flags;
int filtered[NUM_TX_QUEUES];
+ struct wl1271_link *lnk = &wl->links[hlid];

/* filter all frames currently in the low level queues for this hlid */
for (i = 0; i < NUM_TX_QUEUES; i++) {
filtered[i] = 0;
- while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+ while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
filtered[i]++;

if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
@@ -257,8 +258,11 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
}

spin_lock_irqsave(&wl->wl_lock, flags);
- for (i = 0; i < NUM_TX_QUEUES; i++)
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
wl->tx_queue_count[i] -= filtered[i];
+ if (lnk->wlvif)
+ lnk->wlvif->tx_queue_count[i] -= filtered[i];
+ }
spin_unlock_irqrestore(&wl->wl_lock, flags);

wl1271_handle_tx_low_watermark(wl);
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 2e91a37..a5fa54d 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -509,6 +509,10 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
spin_lock_irqsave(&wl->wl_lock, flags);
WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
wl->tx_queue_count[q]--;
+ if (lnk->wlvif) {
+ WARN_ON_ONCE(lnk->wlvif->tx_queue_count[q] <= 0);
+ lnk->wlvif->tx_queue_count[q]--;
+ }
spin_unlock_irqrestore(&wl->wl_lock, flags);
}

@@ -623,6 +627,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,

spin_lock_irqsave(&wl->wl_lock, flags);
wl->tx_queue_count[q]++;
+ if (wlvif)
+ wlvif->tx_queue_count[q]++;
spin_unlock_irqrestore(&wl->wl_lock, flags);
}

@@ -972,10 +978,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
unsigned long flags;
struct ieee80211_tx_info *info;
int total[NUM_TX_QUEUES];
+ struct wl1271_link *lnk = &wl->links[hlid];

for (i = 0; i < NUM_TX_QUEUES; i++) {
total[i] = 0;
- while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+ while ((skb = skb_dequeue(&lnk->tx_queue[i]))) {
wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb);

if (!wl12xx_is_dummy_packet(wl, skb)) {
@@ -990,8 +997,11 @@ void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid)
}

spin_lock_irqsave(&wl->wl_lock, flags);
- for (i = 0; i < NUM_TX_QUEUES; i++)
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
wl->tx_queue_count[i] -= total[i];
+ if (lnk->wlvif)
+ lnk->wlvif->tx_queue_count[i] -= total[i];
+ }
spin_unlock_irqrestore(&wl->wl_lock, flags);

wl1271_handle_tx_low_watermark(wl);
@@ -1015,6 +1025,8 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
}
wlvif->last_tx_hlid = 0;

+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ wlvif->tx_queue_count[i] = 0;
}
/* caller must hold wl->mutex and TX must be stopped */
void wl12xx_tx_reset(struct wl1271 *wl)
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index f86c716..e9fd879 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -370,6 +370,9 @@ struct wl12xx_vif {
/* the hlid of the last transmitted skb */
int last_tx_hlid;

+ /* counters of packets per AC, across all links in the vif */
+ int tx_queue_count[NUM_TX_QUEUES];
+
unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];

u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
--
1.7.9.5


2012-11-27 06:45:16

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 05/20] wlcore: allow ACX_BA_SESSION_RX_SETUP to fail

From: Eliad Peller <[email protected]>

Under some circumstances, that fw might be asked to
remove a rx ba sessions it doesn't know about. In
this case, instead of triggering a recovery, accept
the error code (CMD_STATUS_NO_RX_BA_SESSION) and
ignore it.

[Arik - indicate failure up when the BA session cannot be setup]

Signed-off-by: Eliad Peller <[email protected]>
Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/acx.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c
index ce108a7..9c32f0c 100644
--- a/drivers/net/wireless/ti/wlcore/acx.c
+++ b/drivers/net/wireless/ti/wlcore/acx.c
@@ -1433,13 +1433,22 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index,
acx->win_size = wl->conf.ht.rx_ba_win_size;
acx->ssn = ssn;

- ret = wl1271_cmd_configure(wl, ACX_BA_SESSION_RX_SETUP, acx,
- sizeof(*acx));
+ ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx,
+ sizeof(*acx),
+ BIT(CMD_STATUS_NO_RX_BA_SESSION));
if (ret < 0) {
wl1271_warning("acx ba receiver session failed: %d", ret);
goto out;
}

+ /* sometimes we can't start the session */
+ if (ret == CMD_STATUS_NO_RX_BA_SESSION) {
+ wl1271_warning("no fw rx ba on tid %d", tid_index);
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = 0;
out:
kfree(acx);
return ret;
--
1.7.9.5


2012-11-29 15:33:38

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH 20/20] wl18xx: set last Tx rate from FW status

On Thu, Nov 29, 2012 at 2:55 PM, Luciano Coelho <[email protected]> wrote:
> On Tue, 2012-11-27 at 08:45 +0200, Arik Nemtsov wrote:
>> Obtain the last Tx rate from the FW status and translate it to
>> the mac80211 rate+flag format before sending it up via the Tx status.
>>
>> Bump up the min FW version to the first FW that supports the rate byte.
>
> This part of the comment doesn't apply anymore, because we already
> bumped and this change is not part of this rebased patch. ;)

Ah thanks. I'll remove it.

Arik

2012-11-27 12:46:00

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 03/20] wlcore: Allow memory access when the FW crashes

On Tue, 2012-11-27 at 13:54 +0200, Arik Nemtsov wrote:
> On Tue, Nov 27, 2012 at 11:34 AM, Luciano Coelho <[email protected]> wrote:
> > On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> >> From: Ido Yariv <[email protected]>
> >>
> >> When the no_recovery flag is used, the recovery work will not restart
> >> the FW and the state will not be set to 'on'. To enable post-mortem
> >> analysis, allow memory access in the 'restarting' state.
> >>
> >> Also, since the FW might not be operational, don't fail the read/write
> >> operations if elp_wakeup fails.
> >>
> >> Reported-by: Arkady Miasnikov <[email protected]>
> >> Signed-off-by: Ido Yariv <[email protected]>
> >> Signed-off-by: Arik Nemtsov <[email protected]>
> >> ---
> >
> > This is weird. This patch is already in (d5560238) and the one here is
> > actually just part of it and probably won't apply.
>
> Heh. It seems Eliad and I are to blame (all the rebases can really
> mess with the mind).

Yeah, all these internal rebases suck.


> There's a patch by Eliad to reverse the patch you just mentioned:
>
> commit fd8e73af670e6b57708eafe927f5d0364018b4e5
> Author: Eliad Peller <[email protected]>
> Date: Wed Nov 14 02:28:26 2012 +0200
>
> wlcore: don't allow access to FW mem when chip is off
>
> (which I submitted earlier in my part 1 series)
>
> And the current patch simply cancels it out. So please ignore the
> previous patch as well as this one. Good catch :)

Okay, I'll ignore this one and Eliad's in the previous series.

--
Luca.


2012-11-29 12:56:39

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 20/20] wl18xx: set last Tx rate from FW status

On Tue, 2012-11-27 at 08:45 +0200, Arik Nemtsov wrote:
> Obtain the last Tx rate from the FW status and translate it to
> the mac80211 rate+flag format before sending it up via the Tx status.
>
> Bump up the min FW version to the first FW that supports the rate byte.

This part of the comment doesn't apply anymore, because we already
bumped and this change is not part of this rebased patch. ;)

--
Luca.


2012-11-27 06:45:43

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 20/20] wl18xx: set last Tx rate from FW status

Obtain the last Tx rate from the FW status and translate it to
the mac80211 rate+flag format before sending it up via the Tx status.

Bump up the min FW version to the first FW that supports the rate byte.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl18xx/tx.c | 56 +++++++++++++++++++++++++---
drivers/net/wireless/ti/wlcore/conf.h | 57 ++++++++++++++++++++++-------
drivers/net/wireless/ti/wlcore/wlcore_i.h | 5 ++-
3 files changed, 98 insertions(+), 20 deletions(-)

diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c
index 5b1fb10..3c826d1 100644
--- a/drivers/net/wireless/ti/wl18xx/tx.c
+++ b/drivers/net/wireless/ti/wl18xx/tx.c
@@ -28,6 +28,49 @@
#include "wl18xx.h"
#include "tx.h"

+static
+void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
+ u8 *tx_rate, u8 *tx_rate_flags)
+{
+ u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
+
+ if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
+ wl1271_error("last Tx rate invalid: %d", fw_rate);
+ *tx_rate = 0;
+ *tx_rate_flags = 0;
+ return;
+ }
+
+ if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) {
+ *tx_rate = fw_rate;
+ *tx_rate_flags = 0;
+ } else {
+ *tx_rate_flags = IEEE80211_TX_RC_MCS;
+ *tx_rate = fw_rate - CONF_HW_RATE_INDEX_MCS0;
+
+ /* SGI modifier is counted as a separate rate */
+ if (fw_rate >= CONF_HW_RATE_INDEX_MCS7_SGI)
+ (*tx_rate)--;
+ if (fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+ (*tx_rate)--;
+
+ /* this also covers the 40Mhz SGI case (= MCS15) */
+ if (fw_rate == CONF_HW_RATE_INDEX_MCS7_SGI ||
+ fw_rate == CONF_HW_RATE_INDEX_MCS15_SGI)
+ *tx_rate_flags |= IEEE80211_TX_RC_SHORT_GI;
+
+ if (fw_rate > CONF_HW_RATE_INDEX_MCS7_SGI && vif) {
+ struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+ if (wlvif->channel_type == NL80211_CHAN_HT40MINUS ||
+ wlvif->channel_type == NL80211_CHAN_HT40PLUS) {
+ /* adjustment needed for range 0-7 */
+ *tx_rate -= 8;
+ *tx_rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
+ }
+ }
+ }
+}
+
static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
{
struct ieee80211_tx_info *info;
@@ -44,7 +87,6 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
/* a zero bit indicates Tx success */
tx_success = !(tx_stat_byte & BIT(WL18XX_TX_STATUS_STAT_BIT_IDX));

-
skb = wl->tx_frames[id];
info = IEEE80211_SKB_CB(skb);

@@ -56,11 +98,15 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
/* update the TX status info */
if (tx_success && !(info->flags & IEEE80211_TX_CTL_NO_ACK))
info->flags |= IEEE80211_TX_STAT_ACK;
+ /*
+ * first pass info->control.vif while it's valid, and then fill out
+ * the info->status structures
+ */
+ wl18xx_get_last_tx_rate(wl, info->control.vif,
+ &info->status.rates[0].idx,
+ &info->status.rates[0].flags);

- /* no real data about Tx completion */
- info->status.rates[0].idx = -1;
- info->status.rates[0].count = 0;
- info->status.rates[0].flags = 0;
+ info->status.rates[0].count = 1; /* no data about retries */
info->status.ack_signal = -1;

if (!tx_success)
diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h
index e07d74d..ad15cae 100644
--- a/drivers/net/wireless/ti/wlcore/conf.h
+++ b/drivers/net/wireless/ti/wlcore/conf.h
@@ -57,20 +57,49 @@ enum {
};

enum {
- CONF_HW_RATE_INDEX_1MBPS = 0,
- CONF_HW_RATE_INDEX_2MBPS = 1,
- CONF_HW_RATE_INDEX_5_5MBPS = 2,
- CONF_HW_RATE_INDEX_6MBPS = 3,
- CONF_HW_RATE_INDEX_9MBPS = 4,
- CONF_HW_RATE_INDEX_11MBPS = 5,
- CONF_HW_RATE_INDEX_12MBPS = 6,
- CONF_HW_RATE_INDEX_18MBPS = 7,
- CONF_HW_RATE_INDEX_22MBPS = 8,
- CONF_HW_RATE_INDEX_24MBPS = 9,
- CONF_HW_RATE_INDEX_36MBPS = 10,
- CONF_HW_RATE_INDEX_48MBPS = 11,
- CONF_HW_RATE_INDEX_54MBPS = 12,
- CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS,
+ CONF_HW_RATE_INDEX_1MBPS = 0,
+ CONF_HW_RATE_INDEX_2MBPS = 1,
+ CONF_HW_RATE_INDEX_5_5MBPS = 2,
+ CONF_HW_RATE_INDEX_11MBPS = 3,
+ CONF_HW_RATE_INDEX_6MBPS = 4,
+ CONF_HW_RATE_INDEX_9MBPS = 5,
+ CONF_HW_RATE_INDEX_12MBPS = 6,
+ CONF_HW_RATE_INDEX_18MBPS = 7,
+ CONF_HW_RATE_INDEX_24MBPS = 8,
+ CONF_HW_RATE_INDEX_36MBPS = 9,
+ CONF_HW_RATE_INDEX_48MBPS = 10,
+ CONF_HW_RATE_INDEX_54MBPS = 11,
+ CONF_HW_RATE_INDEX_MCS0 = 12,
+ CONF_HW_RATE_INDEX_MCS1 = 13,
+ CONF_HW_RATE_INDEX_MCS2 = 14,
+ CONF_HW_RATE_INDEX_MCS3 = 15,
+ CONF_HW_RATE_INDEX_MCS4 = 16,
+ CONF_HW_RATE_INDEX_MCS5 = 17,
+ CONF_HW_RATE_INDEX_MCS6 = 18,
+ CONF_HW_RATE_INDEX_MCS7 = 19,
+ CONF_HW_RATE_INDEX_MCS7_SGI = 20,
+ CONF_HW_RATE_INDEX_MCS0_40MHZ = 21,
+ CONF_HW_RATE_INDEX_MCS1_40MHZ = 22,
+ CONF_HW_RATE_INDEX_MCS2_40MHZ = 23,
+ CONF_HW_RATE_INDEX_MCS3_40MHZ = 24,
+ CONF_HW_RATE_INDEX_MCS4_40MHZ = 25,
+ CONF_HW_RATE_INDEX_MCS5_40MHZ = 26,
+ CONF_HW_RATE_INDEX_MCS6_40MHZ = 27,
+ CONF_HW_RATE_INDEX_MCS7_40MHZ = 28,
+ CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI = 29,
+
+ /* MCS8+ rates overlap with 40Mhz rates */
+ CONF_HW_RATE_INDEX_MCS8 = 21,
+ CONF_HW_RATE_INDEX_MCS9 = 22,
+ CONF_HW_RATE_INDEX_MCS10 = 23,
+ CONF_HW_RATE_INDEX_MCS11 = 24,
+ CONF_HW_RATE_INDEX_MCS12 = 25,
+ CONF_HW_RATE_INDEX_MCS13 = 26,
+ CONF_HW_RATE_INDEX_MCS14 = 27,
+ CONF_HW_RATE_INDEX_MCS15 = 28,
+ CONF_HW_RATE_INDEX_MCS15_SGI = 29,
+
+ CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_MCS7_40MHZ_SGI,
};

#define CONF_HW_RXTX_RATE_UNSUPPORTED 0xff
diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index e9fd879..a664662 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -141,7 +141,10 @@ struct wl_fw_packet_counters {
/* Cumulative counter of released Voice memory blocks */
u8 tx_voice_released_blks;

- u8 padding[3];
+ /* Tx rate of the last transmitted packet */
+ u8 tx_last_rate;
+
+ u8 padding[2];
} __packed;

/* FW status registers */
--
1.7.9.5


2012-11-27 09:35:33

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 03/20] wlcore: Allow memory access when the FW crashes

On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> From: Ido Yariv <[email protected]>
>
> When the no_recovery flag is used, the recovery work will not restart
> the FW and the state will not be set to 'on'. To enable post-mortem
> analysis, allow memory access in the 'restarting' state.
>
> Also, since the FW might not be operational, don't fail the read/write
> operations if elp_wakeup fails.
>
> Reported-by: Arkady Miasnikov <[email protected]>
> Signed-off-by: Ido Yariv <[email protected]>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

This is weird. This patch is already in (d5560238) and the one here is
actually just part of it and probably won't apply.

--
Luca.


2012-12-11 12:10:23

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 00/20] wlcore fixes for 3.8 (part2)

On Tue, 2012-11-27 at 08:44 +0200, Arik Nemtsov wrote:
> This series applies on top of part 1 sent yesterday.
>
> The main addition is a new Tx scheduling algorithm for
> better multi-channel operation.
>
> Arik Nemtsov (9):
> wlcore: clear roc_vif on iface removal
> wlcore: take the mutex before resetting Tx queues
> wlcore: consolidate free_link and always call it
> wlcore: initialize per-link FW freed blocks correctly
> wlcore: track wlvif inside per-link structure
> wlcore: count packets held per AC in each vif
> wlcore: track FW-allocated packets per link
> wlcore: improved Tx scheduling algorithm
> wl18xx: set last Tx rate from FW status
>
> Eliad Peller (4):
> wlcore: allow fw commands to fail
> wlcore: allow ACX_BA_SESSION_RX_SETUP to fail
> wlcore: remove WLCORE_QUIRK_NO_ELP
> wl18xx: declare support for greenfield ht_cap
>
> Ido Reis (3):
> wl18xx: update default mac/phy parameters
> wlcore: fwlog dynamic mem_block control
> wl18xx: FDSP Code RAM Corruption fix
>
> Ido Yariv (1):
> wlcore: Allow memory access when the FW crashes
>
> Victor Goldenshtein (2):
> wlcore: remove unnecessary WARN_ON in wl12xx_tx_reset
> wlcore: restore default channel configuration
>
> Yair Shapira (1):
> wl18xx: support 2nd set of mac/phy tx-power params

Applied the whole series, except for 10/20 (still trying to get more
information about it). Some changes as discussed in the respective
threads.

Thanks Arik!

--
Luca.