2010-12-22 14:27:46

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 00/18] AP mode support for wl12xx

These patches add access point mode support to the wl12xx driver.
This mode uses a separate firmware and has a different initialization
sequence.

In all instances, the flow has been split according to the operating
mode of the driver (AP/STA), so as not to affect STA mode functionality.

v1->2: rebased on latest wl12xx tree

Arik Nemtsov (18):
wl1271: Add AP related configuration to conf_drv_settings
wl1271: AP mode - AP specific CMD_CONFIGURE sub-commands
wl1271: AP mode - add AP specific event
wl1271: AP-mode high level commands
wl1271: AP mode - workaround for FW bug on station remove
wl1271: AP mode - init sequence
wl1271: AP specific RX filter configuration
wl1271: Add AP related definitions to HOST-FW interface
wl1271: Configure AP on BSS info change
wl1271: AP mode config in ieee80211_ops.config
wl1271: AP mode - change filter config
wl1271: AP mode - add STA add/remove ops
wl1271: AP mode - changes in TX path
wl1271: AP mode - record TX configuration settings
wl1271: AP mode - encryption support
wl1271: AP mode - fetch appropriate firmware for AP
wl1271: Read MAC address from NVS file on HW startup
wl1271: Enable AP-mode

drivers/net/wireless/wl12xx/acx.c | 62 +++-
drivers/net/wireless/wl12xx/acx.h | 31 ++-
drivers/net/wireless/wl12xx/boot.c | 6 +-
drivers/net/wireless/wl12xx/boot.h | 3 +
drivers/net/wireless/wl12xx/cmd.c | 300 +++++++++++-
drivers/net/wireless/wl12xx/cmd.h | 143 +++++-
drivers/net/wireless/wl12xx/conf.h | 26 +-
drivers/net/wireless/wl12xx/event.c | 7 +-
drivers/net/wireless/wl12xx/event.h | 8 +-
drivers/net/wireless/wl12xx/init.c | 352 ++++++++++---
drivers/net/wireless/wl12xx/init.h | 5 +-
drivers/net/wireless/wl12xx/main.c | 752 ++++++++++++++++++++++------
drivers/net/wireless/wl12xx/rx.h | 12 +-
drivers/net/wireless/wl12xx/tx.c | 70 ++-
drivers/net/wireless/wl12xx/tx.h | 13 +-
drivers/net/wireless/wl12xx/wl12xx.h | 68 +++-
drivers/net/wireless/wl12xx/wl12xx_80211.h | 5 +
17 files changed, 1582 insertions(+), 281 deletions(-)



2010-12-28 17:16:36

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 15/18] wl1271: AP mode - encryption support

On Tue, Dec 28, 2010 at 14:03, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Encryption key configuration is different for AP/STA modes.
>>
>> AP encryption keys are recorded when the BSS is not started. On BSS
>> start they are propagated to the AP (in wl1271_ap_init_hwenc).
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
>> index 0662b19..81ba75d 100644
>> --- a/drivers/net/wireless/wl12xx/tx.h
>> +++ b/drivers/net/wireless/wl12xx/tx.h
>> @@ -152,4 +152,8 @@ void wl1271_tx_flush(struct wl1271 *wl);
>> ?u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
>> ?u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);
>>
>> +/* Functions from wl1271_main.c */
>> +
>> +int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id);
>> +
>> ?#endif
>
> Again, same thing. ?Can we put this somewhere else?
>

Actually the WEP default key is used for TX, so we can put this in tx.c.

Regards,
Arik

2010-12-22 14:28:43

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 17/18] wl1271: Read MAC address from NVS file on HW startup

Try to read the MAC address from the on-disk NVS file.
A non-zero MAC address is required to add an AP interface.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index eb2c3c5..a9f95c6 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -3025,6 +3025,18 @@ int wl1271_register_hw(struct wl1271 *wl)
if (wl->mac80211_registered)
return 0;

+ ret = wl1271_fetch_nvs(wl);
+ if (ret == 0) {
+ u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
+
+ wl->mac_addr[0] = nvs_ptr[11];
+ wl->mac_addr[1] = nvs_ptr[10];
+ wl->mac_addr[2] = nvs_ptr[6];
+ wl->mac_addr[3] = nvs_ptr[5];
+ wl->mac_addr[4] = nvs_ptr[4];
+ wl->mac_addr[5] = nvs_ptr[3];
+ }
+
SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);

ret = ieee80211_register_hw(wl->hw);
--
1.7.1


2010-12-22 14:28:13

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 07/18] wl1271: AP specific RX filter configuration

Set filters according to the mode of operation.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/boot.c | 3 +--
drivers/net/wireless/wl12xx/boot.h | 3 +++
drivers/net/wireless/wl12xx/main.c | 18 ++++++++++++++----
drivers/net/wireless/wl12xx/wl12xx.h | 13 +++++++++++--
4 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c
index bb4f283..f0c35f2 100644
--- a/drivers/net/wireless/wl12xx/boot.c
+++ b/drivers/net/wireless/wl12xx/boot.c
@@ -599,8 +599,7 @@ int wl1271_boot(struct wl1271 *wl)
wl1271_boot_enable_interrupts(wl);

/* set the wl1271 default filters */
- wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
- wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
+ wl1271_set_default_filters(wl);

wl1271_event_mbox_config(wl);

diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h
index d67dcff..5f19a40 100644
--- a/drivers/net/wireless/wl12xx/boot.h
+++ b/drivers/net/wireless/wl12xx/boot.h
@@ -69,4 +69,7 @@ struct wl1271_static_data {
#define FREF_CLK_POLARITY_BITS 0xfffff8ff
#define CLK_REQ_OUTN_SEL 0x700

+/* Functions from wl1271_main.c */
+void wl1271_set_default_filters(struct wl1271 *wl);
+
#endif
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 5354190..05adde6 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1225,10 +1225,20 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
cancel_work_sync(&wl->recovery_work);
}

+void wl1271_set_default_filters(struct wl1271 *wl)
+{
+ if (wl->bss_type == BSS_TYPE_AP_BSS) {
+ wl->rx_config = WL1271_DEFAULT_AP_RX_CONFIG;
+ wl->rx_filter = WL1271_DEFAULT_AP_RX_FILTER;
+ } else {
+ wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG;
+ wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER;
+ }
+}
+
static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
{
- wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
- wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
+ wl1271_set_default_filters(wl);

/* combine requested filters with current filter config */
filters = wl->filters | filters;
@@ -2776,8 +2786,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
wl->default_key = 0;
wl->rx_counter = 0;
- wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
- wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
+ wl->rx_config = WL1271_DEFAULT_STA_RX_CONFIG;
+ wl->rx_filter = WL1271_DEFAULT_STA_RX_FILTER;
wl->psm_entry_retry = 0;
wl->power_level = WL1271_DEFAULT_POWER_LEVEL;
wl->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index aba8f47..136e87b 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -103,15 +103,24 @@ extern u32 wl12xx_debug_level;
true); \
} while (0)

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

-#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \
+#define WL1271_DEFAULT_STA_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \
CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
CFG_RX_CTL_EN | CFG_RX_BCN_EN | \
CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN)

+#define WL1271_DEFAULT_AP_RX_CONFIG 0
+
+#define WL1271_DEFAULT_AP_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PREQ_EN | \
+ CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \
+ CFG_RX_CTL_EN | CFG_RX_AUTH_EN | \
+ CFG_RX_ASSOC_EN)
+
+
+
#define WL1271_FW_NAME "wl1271-fw.bin"
#define WL1271_NVS_NAME "wl1271-nvs.bin"

--
1.7.1


2010-12-27 21:53:52

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 09/18] wl1271: Configure AP on BSS info change

On Mon, Dec 27, 2010 at 15:11, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Configure AP-specific beacon and probe response templates.
>> Start the AP when beaconing is enabled.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> ?static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
>> @@ -1898,73 +1900,84 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
>
> This whole wl1271_op_bss_info_changed() function was already quite long
> and hard to read. ?Now, with support for AP and STA, it got even worse.
> I think this should be broken down into smaller functions. ?No need to
> clean the whole function up now, but would it be possible to separate at
> least the AP and STA parts into separate functions?

Sounds like a good idea. I'll separate it into 3 functions - common,
ap and sta/ibss.

>
>
>> @@ -1983,11 +2018,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bss_conf->cqm_rssi_thold,
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? bss_conf->cqm_rssi_hyst);
>> ? ? ? ? ? ? ? if (ret < 0)
>> - ? ? ? ? ? ? ? ? ? ? goto out;
>> + ? ? ? ? ? ? ? ? ? ? goto out_sleep;
>
> Hmmm, nice catch. ?We were going out without sleeping in case of error
> here. ?But this is a cross-patch change, could you separate it and send
> as a standalone patch?

Sure.

Regards,
Arik

2010-12-22 14:27:48

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 01/18] wl1271: Add AP related configuration to conf_drv_settings

Rate class configuration has been split up for AP and STA modes.
Template related configuration likewise separated.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/acx.c | 2 +-
drivers/net/wireless/wl12xx/cmd.c | 4 +-
drivers/net/wireless/wl12xx/conf.h | 26 +++++++++++++++++++-
drivers/net/wireless/wl12xx/main.c | 45 ++++++++++++++++++++++++++++++++++-
4 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index cc4068d..30ff32a 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -754,7 +754,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
int wl1271_acx_rate_policies(struct wl1271 *wl)
{
struct acx_rate_policy *acx;
- struct conf_tx_rate_class *c = &wl->conf.tx.rc_conf;
+ struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf;
int idx = 0;
int ret = 0;

diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index 0106628..15f4077 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -490,8 +490,8 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
cmd->len = cpu_to_le16(buf_len);
cmd->template_type = template_id;
cmd->enabled_rates = cpu_to_le32(rates);
- cmd->short_retry_limit = wl->conf.tx.rc_conf.short_retry_limit;
- cmd->long_retry_limit = wl->conf.tx.rc_conf.long_retry_limit;
+ cmd->short_retry_limit = wl->conf.tx.tmpl_short_retry_limit;
+ cmd->long_retry_limit = wl->conf.tx.tmpl_long_retry_limit;
cmd->index = index;

if (buf)
diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h
index a16b361..47094f0 100644
--- a/drivers/net/wireless/wl12xx/conf.h
+++ b/drivers/net/wireless/wl12xx/conf.h
@@ -636,9 +636,9 @@ struct conf_tx_settings {

/*
* Configuration for rate classes for TX (currently only one
- * rate class supported.)
+ * rate class supported). Used in non-AP mode.
*/
- struct conf_tx_rate_class rc_conf;
+ struct conf_tx_rate_class sta_rc_conf;

/*
* Configuration for access categories for TX rate control.
@@ -647,6 +647,22 @@ struct conf_tx_settings {
struct conf_tx_ac_category ac_conf[CONF_TX_MAX_AC_COUNT];

/*
+ * Configuration for rate classes in AP-mode. These rate classes
+ * are for the AC TX queues
+ */
+ struct conf_tx_rate_class ap_rc_conf[CONF_TX_MAX_AC_COUNT];
+
+ /*
+ * Management TX rate class for AP-mode.
+ */
+ struct conf_tx_rate_class ap_mgmt_conf;
+
+ /*
+ * Broadcast TX rate class for AP-mode.
+ */
+ struct conf_tx_rate_class ap_bcst_conf;
+
+ /*
* Configuration for TID parameters.
*/
u8 tid_conf_count;
@@ -687,6 +703,12 @@ struct conf_tx_settings {
* Range: CONF_HW_BIT_RATE_* bit mask
*/
u32 basic_rate_5;
+
+ /*
+ * TX retry limits for templates
+ */
+ u8 tmpl_short_retry_limit;
+ u8 tmpl_long_retry_limit;
};

enum {
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 062247e..7ead0ea 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -116,11 +116,11 @@ static struct conf_drv_settings default_conf = {
},
.tx = {
.tx_energy_detection = 0,
- .rc_conf = {
+ .sta_rc_conf = {
.enabled_rates = 0,
.short_retry_limit = 10,
.long_retry_limit = 10,
- .aflags = 0
+ .aflags = 0,
},
.ac_conf_count = 4,
.ac_conf = {
@@ -153,6 +153,45 @@ static struct conf_drv_settings default_conf = {
.tx_op_limit = 1504,
},
},
+ .ap_rc_conf = {
+ [0] = {
+ .enabled_rates = 0x1EFF,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0,
+ },
+ [1] = {
+ .enabled_rates = 0x1EFF,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0,
+ },
+ [2] = {
+ .enabled_rates = 0x1EFF,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0,
+ },
+ [3] = {
+ .enabled_rates = 0x1EFF,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0,
+ },
+ },
+ .ap_mgmt_conf = {
+ .enabled_rates = 0x7,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0,
+ },
+ .ap_bcst_conf = {
+ .enabled_rates = 0x1,
+ .short_retry_limit = 10,
+ .long_retry_limit = 10,
+ .aflags = 0,
+ },
+
.tid_conf_count = 4,
.tid_conf = {
[CONF_TX_AC_BE] = {
@@ -193,6 +232,8 @@ static struct conf_drv_settings default_conf = {
.tx_compl_threshold = 4,
.basic_rate = CONF_HW_BIT_RATE_1MBPS,
.basic_rate_5 = CONF_HW_BIT_RATE_6MBPS,
+ .tmpl_short_retry_limit = 10,
+ .tmpl_long_retry_limit = 10,
},
.conn = {
.wake_up_event = CONF_WAKE_UP_EVENT_DTIM,
--
1.7.1


2010-12-22 14:28:47

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 18/18] wl1271: Enable AP-mode

Indicate support for the NL80211_IFTYPE_AP interface mode to enable AP
mode operation.

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

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index a9f95c6..ee5b262 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1073,6 +1073,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
wl->bss_type = BSS_TYPE_IBSS;
wl->set_bss_type = BSS_TYPE_STA_BSS;
break;
+ case NL80211_IFTYPE_AP:
+ wl->bss_type = BSS_TYPE_AP_BSS;
+ break;
default:
ret = -EOPNOTSUPP;
goto out;
@@ -3097,7 +3100,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);

wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
- BIT(NL80211_IFTYPE_ADHOC);
+ BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP);
wl->hw->wiphy->max_scan_ssids = 1;
/*
* Maximum length of elements in scanning probe request templates
--
1.7.1


2010-12-28 05:38:09

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 12/18] wl1271: AP mode - add STA add/remove ops

On Mon, Dec 27, 2010 at 15:34, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Allocate and free host link IDs (HLIDs) for each link. A per-STA
>> data structure keeps the HLID of each STA.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> +static int wl1271_op_sta_add(struct ieee80211_hw *hw,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ?struct ieee80211_vif *vif,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ?struct ieee80211_sta *sta)
>> +{
>> + ? ? struct wl1271 *wl = hw->priv;
>> + ? ? int ret = 0;
>> + ? ? u8 hlid;
>> +
>> + ? ? mutex_lock(&wl->mutex);
>> +
>> + ? ? if (unlikely(wl->state == WL1271_STATE_OFF))
>> + ? ? ? ? ? ? goto out;
>> +
>> + ? ? if (wl->bss_type != BSS_TYPE_AP_BSS)
>> + ? ? ? ? ? ? goto out;
>
> Should we ever bump into these two conditions? Shouldn't we warn or
> return errors?

It will happen in IBSS as well, in which case we don't want to do anything.

>
>
>> +static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct ieee80211_vif *vif,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct ieee80211_sta *sta)
>> +{
>> + ? ? struct wl1271 *wl = hw->priv;
>> + ? ? struct wl1271_station *wl_sta;
>> + ? ? int ret = 0;
>> +
>> + ? ? mutex_lock(&wl->mutex);
>> +
>> + ? ? if (unlikely(wl->state == WL1271_STATE_OFF))
>> + ? ? ? ? ? ? goto out;
>> +
>> + ? ? if (wl->bss_type != BSS_TYPE_AP_BSS)
>> + ? ? ? ? ? ? goto out;
>
> Same here...

As in the above :)

>
>
>> + ? ? wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
>> +
>> + ? ? ret = wl1271_ps_elp_wakeup(wl, false);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? goto out;
>> +
>> + ? ? wl_sta = (struct wl1271_station *)sta->drv_priv;
>> + ? ? ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? goto out_sleep;
>
> Would it make sense to check if the sta is really in use (ie. it is
> marked in the ap_sta_map) before trying to remove it?

It does. I'll add the check.

Regards,
Arik

2010-12-22 14:28:07

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 05/18] wl1271: AP mode - workaround for FW bug on station remove

Sometimes an event indicating station removal is not sent up by
firmware. We work around this by always indicating success in when
a wait for the event timeouts.

Temporary workaround until a FW fix is introduced.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/cmd.c | 26 +++++++++++++++++++++-----
1 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index aab1b77..0a1eb0e 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -222,7 +222,7 @@ int wl1271_cmd_ext_radio_parms(struct wl1271 *wl)
* Poll the mailbox event field until any of the bits in the mask is set or a
* timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
*/
-static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
+static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask)
{
u32 events_vector, event;
unsigned long timeout;
@@ -231,7 +231,8 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)

do {
if (time_after(jiffies, timeout)) {
- ieee80211_queue_work(wl->hw, &wl->recovery_work);
+ wl1271_debug(DEBUG_CMD, "timeout waiting for event %d",
+ (int)mask);
return -ETIMEDOUT;
}

@@ -249,6 +250,19 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
return 0;
}

+static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
+{
+ int ret;
+
+ ret = wl1271_cmd_wait_for_event_or_timeout(wl, mask);
+ if (ret != 0) {
+ ieee80211_queue_work(wl->hw, &wl->recovery_work);
+ return ret;
+ }
+
+ return 0;
+}
+
int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
{
struct wl1271_cmd_join *join;
@@ -1108,9 +1122,11 @@ int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
goto out_free;
}

- ret = wl1271_cmd_wait_for_event(wl, STA_REMOVE_COMPLETE_EVENT_ID);
- if (ret < 0)
- wl1271_error("cmd remove sta event completion error");
+ /*
+ * We are ok with a timeout here. The event is sometimes not sent
+ * due to a firmware bug.
+ */
+ wl1271_cmd_wait_for_event_or_timeout(wl, STA_REMOVE_COMPLETE_EVENT_ID);

out_free:
kfree(cmd);
--
1.7.1


2010-12-27 13:11:58

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 09/18] wl1271: Configure AP on BSS info change

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Configure AP-specific beacon and probe response templates.
> Start the AP when beaconing is enabled.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
> @@ -1898,73 +1900,84 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,

This whole wl1271_op_bss_info_changed() function was already quite long
and hard to read. Now, with support for AP and STA, it got even worse.
I think this should be broken down into smaller functions. No need to
clean the whole function up now, but would it be possible to separate at
least the AP and STA parts into separate functions?


> @@ -1983,11 +2018,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
> bss_conf->cqm_rssi_thold,
> bss_conf->cqm_rssi_hyst);
> if (ret < 0)
> - goto out;
> + goto out_sleep;

Hmmm, nice catch. We were going out without sleeping in case of error
here. But this is a cross-patch change, could you separate it and send
as a standalone patch?


--
Cheers,
Luca.


2010-12-22 14:28:05

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 04/18] wl1271: AP-mode high level commands

Add commands to start/stop BSS, add/remove STA and configure encryption
keys. Split the encryption commands "set key" and "set default key" into
AP and STA specific versions.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/cmd.c | 276 +++++++++++++++++++++++++++++++++-
drivers/net/wireless/wl12xx/cmd.h | 135 ++++++++++++++++-
drivers/net/wireless/wl12xx/init.c | 2 +-
drivers/net/wireless/wl12xx/main.c | 6 +-
drivers/net/wireless/wl12xx/tx.c | 2 +-
drivers/net/wireless/wl12xx/wl12xx.h | 8 +
6 files changed, 417 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index 15f4077..aab1b77 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -36,6 +36,7 @@
#include "wl12xx_80211.h"
#include "cmd.h"
#include "event.h"
+#include "tx.h"

#define WL1271_CMD_FAST_POLL_COUNT 50

@@ -702,9 +703,9 @@ int wl1271_build_qos_null_data(struct wl1271 *wl)
wl->basic_rate);
}

-int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
+int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id)
{
- struct wl1271_cmd_set_keys *cmd;
+ struct wl1271_cmd_set_sta_keys *cmd;
int ret = 0;

wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id);
@@ -731,11 +732,42 @@ out:
return ret;
}

-int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id)
+{
+ struct wl1271_cmd_set_ap_keys *cmd;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_CMD, "cmd set_ap_default_wep_key %d", id);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->hlid = WL1271_AP_BROADCAST_HLID;
+ cmd->key_id = id;
+ cmd->lid_key_type = WEP_DEFAULT_LID_TYPE;
+ cmd->key_action = cpu_to_le16(KEY_SET_ID);
+ cmd->key_type = KEY_WEP;
+
+ ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_warning("cmd set_ap_default_wep_key failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(cmd);
+
+ return ret;
+}
+
+int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, const u8 *addr,
u32 tx_seq_32, u16 tx_seq_16)
{
- struct wl1271_cmd_set_keys *cmd;
+ struct wl1271_cmd_set_sta_keys *cmd;
int ret = 0;

cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
@@ -788,6 +820,67 @@ out:
return ret;
}

+int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+ u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
+ u16 tx_seq_16)
+{
+ struct wl1271_cmd_set_ap_keys *cmd;
+ int ret = 0;
+ u8 lid_type;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd)
+ return -ENOMEM;
+
+ if (hlid == WL1271_AP_BROADCAST_HLID) {
+ if (key_type == KEY_WEP)
+ lid_type = WEP_DEFAULT_LID_TYPE;
+ else
+ lid_type = BROADCAST_LID_TYPE;
+ } else {
+ lid_type = UNICAST_LID_TYPE;
+ }
+
+ wl1271_debug(DEBUG_CRYPT, "ap key action: %d id: %d lid: %d type: %d"
+ " hlid: %d", (int)action, (int)id, (int)lid_type,
+ (int)key_type, (int)hlid);
+
+ cmd->lid_key_type = lid_type;
+ cmd->hlid = hlid;
+ cmd->key_action = cpu_to_le16(action);
+ cmd->key_size = key_size;
+ cmd->key_type = key_type;
+ cmd->key_id = id;
+ cmd->ac_seq_num16[0] = cpu_to_le16(tx_seq_16);
+ cmd->ac_seq_num32[0] = cpu_to_le32(tx_seq_32);
+
+ if (key_type == KEY_TKIP) {
+ /*
+ * We get the key in the following form:
+ * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes)
+ * but the target is expecting:
+ * TKIP - RX MIC - TX MIC
+ */
+ memcpy(cmd->key, key, 16);
+ memcpy(cmd->key + 16, key + 24, 8);
+ memcpy(cmd->key + 24, key + 16, 8);
+ } else {
+ memcpy(cmd->key, key, key_size);
+ }
+
+ wl1271_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd));
+
+ ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_warning("could not set ap keys");
+ goto out;
+ }
+
+out:
+ kfree(cmd);
+ return ret;
+}
+
int wl1271_cmd_disconnect(struct wl1271 *wl)
{
struct wl1271_cmd_disconnect *cmd;
@@ -850,3 +943,178 @@ out_free:
out:
return ret;
}
+
+int wl1271_cmd_start_bss(struct wl1271 *wl)
+{
+ struct wl1271_cmd_bss_start *cmd;
+ struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
+ int ret;
+
+ wl1271_debug(DEBUG_CMD, "cmd start bss");
+
+ /*
+ * FIXME: We currently do not support hidden SSID. The real SSID
+ * should be fetched from mac80211 first.
+ */
+ if (wl->ssid_len == 0) {
+ wl1271_warning("Hidden SSID currently not supported for AP");
+ return -EINVAL;
+ }
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(cmd->bssid, bss_conf->bssid, ETH_ALEN);
+
+ cmd->aging_period = WL1271_AP_DEF_INACTIV_SEC;
+ cmd->bss_index = WL1271_AP_BSS_INDEX;
+ cmd->global_hlid = WL1271_AP_GLOBAL_HLID;
+ cmd->broadcast_hlid = WL1271_AP_BROADCAST_HLID;
+ cmd->basic_rate_set = cpu_to_le32(wl->basic_rate_set);
+ cmd->beacon_interval = cpu_to_le16(wl->beacon_int);
+ cmd->dtim_interval = bss_conf->dtim_period;
+ cmd->beacon_expiry = WL1271_AP_DEF_BEACON_EXP;
+ cmd->channel = wl->channel;
+ cmd->ssid_len = wl->ssid_len;
+ cmd->ssid_type = SSID_TYPE_PUBLIC;
+ memcpy(cmd->ssid, wl->ssid, wl->ssid_len);
+
+ switch (wl->band) {
+ case IEEE80211_BAND_2GHZ:
+ cmd->band = RADIO_BAND_2_4GHZ;
+ break;
+ case IEEE80211_BAND_5GHZ:
+ cmd->band = RADIO_BAND_5GHZ;
+ break;
+ default:
+ wl1271_warning("bss start - unknown band: %d", (int)wl->band);
+ cmd->band = RADIO_BAND_2_4GHZ;
+ break;
+ }
+
+ ret = wl1271_cmd_send(wl, CMD_BSS_START, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("failed to initiate cmd start bss");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int wl1271_cmd_stop_bss(struct wl1271 *wl)
+{
+ struct wl1271_cmd_bss_start *cmd;
+ int ret;
+
+ wl1271_debug(DEBUG_CMD, "cmd stop bss");
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->bss_index = WL1271_AP_BSS_INDEX;
+
+ ret = wl1271_cmd_send(wl, CMD_BSS_STOP, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("failed to initiate cmd stop bss");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
+{
+ struct wl1271_cmd_add_sta *cmd;
+ int ret;
+
+ wl1271_debug(DEBUG_CMD, "cmd add sta %d", (int)hlid);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* currently we don't support UAPSD */
+ memset(cmd->psd_type, 0, sizeof(cmd->psd_type));
+ cmd->sp_len = 0;
+
+ memcpy(cmd->addr, sta->addr, ETH_ALEN);
+ cmd->bss_index = WL1271_AP_BSS_INDEX;
+ cmd->aid = sta->aid;
+ cmd->hlid = hlid;
+
+ /*
+ * FIXME: Does STA support QOS? We need to propagate this info from
+ * hostapd. Currently not that important since this is only used for
+ * sending the correct flavor of null-data packet in response to a
+ * trigger.
+ */
+ cmd->wmm = 0;
+
+ cmd->supported_rates = cpu_to_le32(wl1271_tx_enabled_rates_get(wl,
+ sta->supp_rates[wl->band]));
+
+ wl1271_debug(DEBUG_CMD, "new sta rates: 0x%x", cmd->supported_rates);
+
+ ret = wl1271_cmd_send(wl, CMD_ADD_STA, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("failed to initiate cmd add sta");
+ goto out_free;
+ }
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
+
+int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
+{
+ struct wl1271_cmd_remove_sta *cmd;
+ int ret;
+
+ wl1271_debug(DEBUG_CMD, "cmd remove sta %d", (int)hlid);
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cmd->hlid = hlid;
+ /* We never send a deauth, mac80211 is in charge of this */
+ cmd->reason_opcode = 0;
+ cmd->send_deauth_flag = 0;
+
+ ret = wl1271_cmd_send(wl, CMD_REMOVE_STA, cmd, sizeof(*cmd), 0);
+ if (ret < 0) {
+ wl1271_error("failed to initiate cmd remove sta");
+ goto out_free;
+ }
+
+ ret = wl1271_cmd_wait_for_event(wl, STA_REMOVE_COMPLETE_EVENT_ID);
+ if (ret < 0)
+ wl1271_error("cmd remove sta event completion error");
+
+out_free:
+ kfree(cmd);
+
+out:
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index 2a1d9db..02f843c 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -54,12 +54,20 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr);
int wl1271_build_qos_null_data(struct wl1271 *wl);
int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
-int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
-int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id);
+int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id);
+int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, const u8 *addr,
u32 tx_seq_32, u16 tx_seq_16);
+int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+ u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
+ u16 tx_seq_16);
int wl1271_cmd_disconnect(struct wl1271 *wl);
int wl1271_cmd_set_sta_state(struct wl1271 *wl);
+int wl1271_cmd_start_bss(struct wl1271 *wl);
+int wl1271_cmd_stop_bss(struct wl1271 *wl);
+int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid);
+int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid);

enum wl1271_commands {
CMD_INTERROGATE = 1, /*use this to read information elements*/
@@ -98,6 +106,12 @@ enum wl1271_commands {
CMD_STOP_PERIODIC_SCAN = 51,
CMD_SET_STA_STATE = 52,

+ /* AP mode commands */
+ CMD_BSS_START = 60,
+ CMD_BSS_STOP = 61,
+ CMD_ADD_STA = 62,
+ CMD_REMOVE_STA = 63,
+
NUM_COMMANDS,
MAX_COMMAND_ID = 0xFFFF,
};
@@ -289,7 +303,7 @@ enum wl1271_cmd_key_type {

/* FIXME: Add description for key-types */

-struct wl1271_cmd_set_keys {
+struct wl1271_cmd_set_sta_keys {
struct wl1271_cmd_header header;

/* Ignored for default WEP key */
@@ -318,6 +332,57 @@ struct wl1271_cmd_set_keys {
__le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __packed;

+enum wl1271_cmd_lid_key_type {
+ UNICAST_LID_TYPE = 0,
+ BROADCAST_LID_TYPE = 1,
+ WEP_DEFAULT_LID_TYPE = 2
+};
+
+struct wl1271_cmd_set_ap_keys {
+ struct wl1271_cmd_header header;
+
+ /*
+ * Indicates whether the HLID is a unicast key set
+ * or broadcast key set. A special value 0xFF is
+ * used to indicate that the HLID is on WEP-default
+ * (multi-hlids). of type wl1271_cmd_lid_key_type.
+ */
+ u8 hlid;
+
+ /*
+ * In WEP-default network (hlid == 0xFF) used to
+ * indicate which network STA/IBSS/AP role should be
+ * changed
+ */
+ u8 lid_key_type;
+
+ /*
+ * Key ID - For TKIP and AES key types, this field
+ * indicates the value that should be inserted into
+ * the KeyID field of frames transmitted using this
+ * key entry. For broadcast keys the index use as a
+ * marker for TX/RX key.
+ * For WEP default network (HLID=0xFF), this field
+ * indicates the ID of the key to add or remove.
+ */
+ u8 key_id;
+ u8 reserved_1;
+
+ /* key_action_e */
+ __le16 key_action;
+
+ /* key size in bytes */
+ u8 key_size;
+
+ /* key_type_e */
+ u8 key_type;
+
+ /* This field holds the security key data to add to the STA table */
+ u8 key[MAX_KEY_SIZE];
+ __le16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY];
+ __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
+} __packed;
+
struct wl1271_cmd_test_header {
u8 id;
u8 padding[3];
@@ -412,4 +477,68 @@ struct wl1271_cmd_set_sta_state {
u8 padding[3];
} __packed;

+enum wl1271_ssid_type {
+ SSID_TYPE_PUBLIC = 0,
+ SSID_TYPE_HIDDEN = 1
+};
+
+struct wl1271_cmd_bss_start {
+ struct wl1271_cmd_header header;
+
+ /* wl1271_ssid_type */
+ u8 ssid_type;
+ u8 ssid_len;
+ u8 ssid[IW_ESSID_MAX_SIZE];
+ u8 padding_1[2];
+
+ /* Basic rate set */
+ __le32 basic_rate_set;
+ /* Aging period in seconds*/
+ __le16 aging_period;
+
+ /*
+ * This field specifies the time between target beacon
+ * transmission times (TBTTs), in time units (TUs).
+ * Valid values are 1 to 1024.
+ */
+ __le16 beacon_interval;
+ u8 bssid[ETH_ALEN];
+ u8 bss_index;
+ /* Radio band */
+ u8 band;
+ u8 channel;
+ /* The host link id for the AP's global queue */
+ u8 global_hlid;
+ /* The host link id for the AP's broadcast queue */
+ u8 broadcast_hlid;
+ /* DTIM count */
+ u8 dtim_interval;
+ /* Beacon expiry time in ms */
+ u8 beacon_expiry;
+ u8 padding_2[3];
+} __packed;
+
+struct wl1271_cmd_add_sta {
+ struct wl1271_cmd_header header;
+
+ u8 addr[ETH_ALEN];
+ u8 hlid;
+ u8 aid;
+ u8 psd_type[NUM_ACCESS_CATEGORIES_COPY];
+ __le32 supported_rates;
+ u8 bss_index;
+ u8 sp_len;
+ u8 wmm;
+ u8 padding1;
+} __packed;
+
+struct wl1271_cmd_remove_sta {
+ struct wl1271_cmd_header header;
+
+ u8 hlid;
+ u8 reason_opcode;
+ u8 send_deauth_flag;
+ u8 padding1;
+} __packed;
+
#endif /* __WL1271_CMD_H__ */
diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
index f468d71..799c369 100644
--- a/drivers/net/wireless/wl12xx/init.c
+++ b/drivers/net/wireless/wl12xx/init.c
@@ -41,7 +41,7 @@ static int wl1271_init_hwenc_config(struct wl1271 *wl)
return ret;
}

- ret = wl1271_cmd_set_default_wep_key(wl, wl->default_key);
+ ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key);
if (ret < 0) {
wl1271_warning("couldn't set default key");
return ret;
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 383631e..d241a12 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1712,7 +1712,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,

switch (cmd) {
case SET_KEY:
- ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE,
+ ret = wl1271_cmd_set_sta_key(wl, KEY_ADD_OR_REPLACE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
addr, tx_seq_32, tx_seq_16);
@@ -1723,7 +1723,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,

/* the default WEP key needs to be configured at least once */
if (key_type == KEY_WEP) {
- ret = wl1271_cmd_set_default_wep_key(wl,
+ ret = wl1271_cmd_set_sta_default_wep_key(wl,
wl->default_key);
if (ret < 0)
goto out_sleep;
@@ -1738,7 +1738,7 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
if (!is_broadcast_ether_addr(addr))
break;

- ret = wl1271_cmd_set_key(wl, KEY_REMOVE,
+ ret = wl1271_cmd_set_sta_key(wl, KEY_REMOVE,
key_conf->keyidx, key_type,
key_conf->keylen, key_conf->key,
addr, 0, 0);
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 0cf210d..442a7bd 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -170,7 +170,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,

/* FIXME: do we have to do this if we're not using WEP? */
if (unlikely(wl->default_key != idx)) {
- ret = wl1271_cmd_set_default_wep_key(wl, idx);
+ ret = wl1271_cmd_set_sta_default_wep_key(wl, idx);
if (ret < 0)
return ret;
wl->default_key = idx;
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 01711fe..aba8f47 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -129,6 +129,14 @@ extern u32 wl12xx_debug_level;
#define WL1271_DEFAULT_BEACON_INT 100
#define WL1271_DEFAULT_DTIM_PERIOD 1

+#define WL1271_AP_GLOBAL_HLID 0
+#define WL1271_AP_BROADCAST_HLID 1
+#define WL1271_AP_STA_HLID_START 2
+
+#define WL1271_AP_BSS_INDEX 0
+#define WL1271_AP_DEF_INACTIV_SEC 300
+#define WL1271_AP_DEF_BEACON_EXP 20
+
#define ACX_TX_DESCRIPTORS 32

#define WL1271_AGGR_BUFFER_SIZE (4 * PAGE_SIZE)
--
1.7.1


2010-12-23 07:48:17

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 04/18] wl1271: AP-mode high level commands

On Wed, Dec 22, 2010 at 23:16, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Add commands to start/stop BSS, add/remove STA and configure encryption
>> keys. Split the encryption commands "set key" and "set default key" into
>> AP and STA specific versions.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> @@ -850,3 +943,178 @@ out_free:
>> ?out:
>> ? ? ? return ret;
>> ?}
>> +
>> +int wl1271_cmd_start_bss(struct wl1271 *wl)
>> +{
>> + ? ? struct wl1271_cmd_bss_start *cmd;
>> + ? ? struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
>> + ? ? int ret;
>> +
>> + ? ? wl1271_debug(DEBUG_CMD, "cmd start bss");
>> +
>> + ? ? /*
>> + ? ? ?* FIXME: We currently do not support hidden SSID. The real SSID
>> + ? ? ?* should be fetched from mac80211 first.
>> + ? ? ?*/
>> + ? ? if (wl->ssid_len == 0) {
>> + ? ? ? ? ? ? wl1271_warning("Hidden SSID currently not supported for AP");
>> + ? ? ? ? ? ? return -EINVAL;
>> + ? ? }
>> +
>> + ? ? cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
>> + ? ? if (!cmd) {
>> + ? ? ? ? ? ? ret = -ENOMEM;
>> + ? ? ? ? ? ? goto out;
>> + ? ? }
>
> Please decide if you want to return directly here or set ret and goto
> out. ?For consistency, I'd suggest to set ret and goto out, since this
> is how (most of) the rest of the code does.

Fixed.

>
>
>> +int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
>> +{
>> + ? ? struct wl1271_cmd_add_sta *cmd;
>> + ? ? int ret;
>> +
>> + ? ? wl1271_debug(DEBUG_CMD, "cmd add sta %d", (int)hlid);
>> +
>> + ? ? cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
>> + ? ? if (!cmd) {
>> + ? ? ? ? ? ? ret = -ENOMEM;
>> + ? ? ? ? ? ? goto out;
>> + ? ? }
>> +
>> + ? ? /* currently we don't support UAPSD */
>> + ? ? memset(cmd->psd_type, 0, sizeof(cmd->psd_type));
>> + ? ? cmd->sp_len = 0;
>
> No need to memset, since you're kzalloc'ing anyway. ?I guess just a good
> comment here is enough.

Fixed.

>
>
>> diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
>> index 2a1d9db..02f843c 100644
>> --- a/drivers/net/wireless/wl12xx/cmd.h
>> +++ b/drivers/net/wireless/wl12xx/cmd.h
>> @@ -54,12 +54,20 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
>> ?int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr);
>> ?int wl1271_build_qos_null_data(struct wl1271 *wl);
>> ?int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
>> -int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
>> -int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
>> +int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id);
>> +int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id);
>> +int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
>> ? ? ? ? ? ? ? ? ? ? ?u8 key_size, const u8 *key, const u8 *addr,
>> ? ? ? ? ? ? ? ? ? ? ?u32 tx_seq_32, u16 tx_seq_16);
>
> Indentation?
>
>> +int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
>> + ? ? ? ? ? ? ? ? ? ? u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
>> + ? ? ? ? ? ? ? ? ? ? u16 tx_seq_16);
>
> Ditto.
>

Corrected indentations.

Regards,
Arik

2010-12-22 14:28:20

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 09/18] wl1271: Configure AP on BSS info change

Configure AP-specific beacon and probe response templates.
Start the AP when beaconing is enabled.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 129 +++++++++++++++++++++------------
drivers/net/wireless/wl12xx/wl12xx.h | 3 +
2 files changed, 85 insertions(+), 47 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 05adde6..59c7fb3 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1872,7 +1872,7 @@ out:
return ret;
}

-static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
+static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
int offset)
{
u8 *ptr = skb->data + offset;
@@ -1882,11 +1882,13 @@ static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb,
if (ptr[0] == WLAN_EID_SSID) {
wl->ssid_len = ptr[1];
memcpy(wl->ssid, ptr+2, wl->ssid_len);
- return;
+ return 0;
}
ptr += (ptr[1] + 2);
}
+
wl1271_error("No SSID in IEs!\n");
+ return -ENOENT;
}

static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
@@ -1898,73 +1900,84 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
struct wl1271 *wl = hw->priv;
struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid);
bool do_join = false;
- bool set_assoc = false;
+ bool set_assoc = false, is_ap, is_ibss;
int ret;

- wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed");
+ wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed 0x%x",
+ (int)changed);

mutex_lock(&wl->mutex);

if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;

+ is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+ is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
+
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;

- if ((changed & BSS_CHANGED_BEACON_INT) &&
- (wl->bss_type == BSS_TYPE_IBSS)) {
- wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon interval updated: %d",
+ if ((changed & BSS_CHANGED_BEACON_INT) && (is_ap || is_ibss)) {
+ wl1271_debug(DEBUG_MASTER, "beacon interval updated: %d",
bss_conf->beacon_int);

wl->beacon_int = bss_conf->beacon_int;
- do_join = true;
+
+ if (!is_ap)
+ do_join = true;
}

- if ((changed & BSS_CHANGED_BEACON) &&
- (wl->bss_type == BSS_TYPE_IBSS)) {
+ if ((changed & BSS_CHANGED_BEACON) && (is_ap || is_ibss)) {
+ struct ieee80211_hdr *hdr;
+ int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+ u16 tmpl_id;

- wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated");
-
- if (beacon) {
- struct ieee80211_hdr *hdr;
- int ieoffset = offsetof(struct ieee80211_mgmt,
- u.beacon.variable);
-
- wl1271_ssid_set(wl, beacon, ieoffset);
-
- ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON,
- beacon->data,
- beacon->len, 0,
- wl1271_min_rate_get(wl));
-
- if (ret < 0) {
- dev_kfree_skb(beacon);
- goto out_sleep;
- }
+ if (!beacon)
+ goto end_changed_bcn;

- hdr = (struct ieee80211_hdr *) beacon->data;
- hdr->frame_control = cpu_to_le16(
- IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_RESP);
+ wl1271_debug(DEBUG_MASTER, "beacon updated");

- ret = wl1271_cmd_template_set(wl,
- CMD_TEMPL_PROBE_RESPONSE,
- beacon->data,
- beacon->len, 0,
- wl1271_min_rate_get(wl));
+ ret = wl1271_ssid_set(wl, beacon, ieoffset);
+ if (ret < 0) {
dev_kfree_skb(beacon);
- if (ret < 0)
- goto out_sleep;
+ goto out_sleep;
+ }
+ tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON :
+ CMD_TEMPL_BEACON;
+ ret = wl1271_cmd_template_set(wl, tmpl_id,
+ beacon->data,
+ beacon->len, 0,
+ wl1271_min_rate_get(wl));
+ if (ret < 0) {
+ dev_kfree_skb(beacon);
+ goto out_sleep;
+ }

- /* Need to update the SSID (for filtering etc) */
+ hdr = (struct ieee80211_hdr *) beacon->data;
+ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_PROBE_RESP);
+
+ tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE :
+ CMD_TEMPL_PROBE_RESPONSE;
+ ret = wl1271_cmd_template_set(wl,
+ tmpl_id,
+ beacon->data,
+ beacon->len, 0,
+ wl1271_min_rate_get(wl));
+ dev_kfree_skb(beacon);
+ if (ret < 0)
+ goto out_sleep;
+
+ /* Need to update the SSID (for filtering etc) */
+ if (!is_ap)
do_join = true;
- }
}

- if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
- (wl->bss_type == BSS_TYPE_IBSS)) {
+end_changed_bcn:
+
+ if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ibss) {
wl1271_debug(DEBUG_ADHOC, "ad-hoc beaconing: %s",
bss_conf->enable_beacon ? "enabled" : "disabled");

@@ -1975,7 +1988,29 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
do_join = true;
}

- if (changed & BSS_CHANGED_CQM) {
+ if ((changed & BSS_CHANGED_BEACON_ENABLED) && is_ap) {
+ if (bss_conf->enable_beacon) {
+ if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
+ ret = wl1271_cmd_start_bss(wl);
+ if (ret < 0)
+ goto out_sleep;
+
+ set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
+ wl1271_debug(DEBUG_AP, "started AP");
+ }
+ } else {
+ if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
+ ret = wl1271_cmd_stop_bss(wl);
+ if (ret < 0)
+ goto out_sleep;
+
+ clear_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
+ wl1271_debug(DEBUG_AP, "stopped AP");
+ }
+ }
+ }
+
+ if ((changed & BSS_CHANGED_CQM) && !is_ap) {
bool enable = false;
if (bss_conf->cqm_rssi_thold)
enable = true;
@@ -1983,11 +2018,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
bss_conf->cqm_rssi_thold,
bss_conf->cqm_rssi_hyst);
if (ret < 0)
- goto out;
+ goto out_sleep;
wl->rssi_thold = bss_conf->cqm_rssi_thold;
}

- if ((changed & BSS_CHANGED_BSSID) &&
+ if ((changed & BSS_CHANGED_BSSID) && !is_ap &&
/*
* Now we know the correct bssid, so we send a new join command
* and enable the BSSID filter
@@ -2010,7 +2045,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
do_join = true;
}

- if (changed & BSS_CHANGED_ASSOC) {
+ if ((changed & BSS_CHANGED_ASSOC) && !is_ap) {
if (bss_conf->assoc) {
u32 rates;
int ieoffset;
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index ebb4a4d..a378d52 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -57,6 +57,8 @@ enum {
DEBUG_SDIO = BIT(14),
DEBUG_FILTERS = BIT(15),
DEBUG_ADHOC = BIT(16),
+ DEBUG_AP = BIT(17),
+ DEBUG_MASTER = (DEBUG_ADHOC | DEBUG_AP),
DEBUG_ALL = ~0,
};

@@ -284,6 +286,7 @@ struct wl1271 {
#define WL1271_FLAG_PSPOLL_FAILURE (12)
#define WL1271_FLAG_STA_STATE_SENT (13)
#define WL1271_FLAG_FW_TX_BUSY (14)
+#define WL1271_FLAG_AP_STARTED (15)
unsigned long flags;

struct wl1271_partition_set part;
--
1.7.1


2010-12-28 11:37:43

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 14/18] wl1271: AP mode - record TX configuration settings

On Tue, Dec 28, 2010 at 13:35, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Record TX configuration settings in the "conf" member of our global
>> structure (struct wl1271) if conf_tx is called when the firmware is
>> not loaded.
>>
>> Later on when the firmware is loaded, we apply the tx conf as part of
>> the init sequence.
>>
>> Important for AP mode since conf_tx is called before add_interface
>> (where the firmware is initialized).
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
>> index 3cc4f79..71a67f1 100644
>> --- a/drivers/net/wireless/wl12xx/main.c
>> +++ b/drivers/net/wireless/wl12xx/main.c
>> @@ -1462,6 +1462,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
>> ? ? ? ? ? ? ? goto out;
>> ? ? ? }
>>
>> + ? ? is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
>> +
>> ? ? ? ret = wl1271_ps_elp_wakeup(wl, false);
>> ? ? ? if (ret < 0)
>> ? ? ? ? ? ? ? goto out;
>
> This is cross-patch and should be in 10/18.

You're right. Actually fixed this already.

Regards,
Arik

2010-12-28 08:34:14

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 06/18] wl1271: AP mode - init sequence

On Tue, 2010-12-28 at 08:16 +0200, ext Arik Nemtsov wrote:
> >> diff --git a/drivers/net/wireless/wl12xx/init.h
> b/drivers/net/wireless/wl12xx/init.h
> >> index 7762421..4d37210 100644
> >> --- a/drivers/net/wireless/wl12xx/init.h
> >> +++ b/drivers/net/wireless/wl12xx/init.h
> >> @@ -27,10 +27,13 @@
> >> #include "wl12xx.h"
> >>
> >> int wl1271_hw_init_power_auth(struct wl1271 *wl);
> >> -int wl1271_init_templates_config(struct wl1271 *wl);
> >> +int wl1271_sta_init_templates_config(struct wl1271 *wl);
> >> int wl1271_init_phy_config(struct wl1271 *wl);
> >> int wl1271_init_pta(struct wl1271 *wl);
> >> int wl1271_init_energy_detection(struct wl1271 *wl);
> >> int wl1271_hw_init(struct wl1271 *wl);
> >>
> >> +/* Functions from wl1271_main.c */
> >> +u32 wl1271_min_rate_get(struct wl1271 *wl);
> >> +
> >
> > This is bad and shouldn't be here. Need to find a better place for
> it.
>
> How about tx.h? It's TX rate we're talking about here after all.
> I can also change the name to wl1271_tx_min_rate_get().

Sounds good.

--
Cheers,
Luca.


2010-12-22 14:27:59

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 02/18] wl1271: AP mode - AP specific CMD_CONFIGURE sub-commands

Add AP max retries and rate policy configuration.
Rename STA rate policy configuration function.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/acx.c | 60 ++++++++++++++++++++++++++++++++++-
drivers/net/wireless/wl12xx/acx.h | 31 +++++++++++++++++-
drivers/net/wireless/wl12xx/init.c | 2 +-
drivers/net/wireless/wl12xx/main.c | 8 ++--
drivers/net/wireless/wl12xx/tx.c | 2 +-
5 files changed, 93 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index 30ff32a..99b7abd 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -751,9 +751,9 @@ int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats)
return 0;
}

-int wl1271_acx_rate_policies(struct wl1271 *wl)
+int wl1271_acx_sta_rate_policies(struct wl1271 *wl)
{
- struct acx_rate_policy *acx;
+ struct acx_sta_rate_policy *acx;
struct conf_tx_rate_class *c = &wl->conf.tx.sta_rc_conf;
int idx = 0;
int ret = 0;
@@ -794,6 +794,38 @@ out:
return ret;
}

+int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
+ u8 idx)
+{
+ struct acx_ap_rate_policy *acx;
+ int ret = 0;
+
+ wl1271_debug(DEBUG_ACX, "acx ap rate policy");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->rate_policy.enabled_rates = cpu_to_le32(c->enabled_rates);
+ acx->rate_policy.short_retry_limit = c->short_retry_limit;
+ acx->rate_policy.long_retry_limit = c->long_retry_limit;
+ acx->rate_policy.aflags = c->aflags;
+
+ acx->rate_policy_idx = idx;
+
+ ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("Setting of ap rate policy failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
+
int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
u8 aifsn, u16 txop)
{
@@ -1334,3 +1366,27 @@ out:
kfree(tsf_info);
return ret;
}
+
+int wl1271_acx_max_tx_retry(struct wl1271 *wl)
+{
+ struct wl1271_acx_max_tx_retry *acx = NULL;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx max tx retry");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx)
+ return -ENOMEM;
+
+ acx->max_tx_retry = cpu_to_le16(WL1271_AP_MAX_TX_RETRY);
+
+ ret = wl1271_cmd_configure(wl, ACX_MAX_TX_FAILURE, acx, sizeof(*acx));
+ if (ret < 0) {
+ wl1271_warning("acx max tx retry failed: %d", ret);
+ goto out;
+ }
+
+out:
+ kfree(acx);
+ return ret;
+}
diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
index 9cbc3f4..d2a68bf 100644
--- a/drivers/net/wireless/wl12xx/acx.h
+++ b/drivers/net/wireless/wl12xx/acx.h
@@ -747,13 +747,23 @@ struct acx_rate_class {
#define ACX_TX_BASIC_RATE 0
#define ACX_TX_AP_FULL_RATE 1
#define ACX_TX_RATE_POLICY_CNT 2
-struct acx_rate_policy {
+struct acx_sta_rate_policy {
struct acx_header header;

__le32 rate_class_cnt;
struct acx_rate_class rate_class[CONF_TX_MAX_RATE_CLASSES];
} __packed;

+
+#define ACX_TX_AP_MODE_MGMT_RATE 4
+#define ACX_TX_AP_MODE_BCST_RATE 5
+struct acx_ap_rate_policy {
+ struct acx_header header;
+
+ __le32 rate_policy_idx; /* The index of the rate policy */
+ struct acx_rate_class rate_policy;
+} __packed;
+
struct acx_ac_cfg {
struct acx_header header;
u8 ac;
@@ -1062,6 +1072,19 @@ struct wl1271_acx_fw_tsf_information {
u8 padding[3];
} __packed;

+#define WL1271_AP_MAX_TX_RETRY 100
+
+struct wl1271_acx_max_tx_retry {
+ struct acx_header header;
+
+ /*
+ * the number of frames transmission failures before
+ * issuing the aging event.
+ */
+ __le16 max_tx_retry;
+ u8 padding_1[2];
+} __packed;
+
enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
@@ -1119,6 +1142,7 @@ enum {
ACX_HT_BSS_OPERATION = 0x0058,
ACX_COEX_ACTIVITY = 0x0059,
ACX_SET_DCO_ITRIM_PARAMS = 0x0061,
+ ACX_MAX_TX_FAILURE = 0x0072,
DOT11_RX_MSDU_LIFE_TIME = 0x1004,
DOT11_CUR_TX_PWR = 0x100D,
DOT11_RX_DOT11_MODE = 0x1012,
@@ -1160,7 +1184,9 @@ int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble);
int wl1271_acx_cts_protect(struct wl1271 *wl,
enum acx_ctsprotect_type ctsprotect);
int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats);
-int wl1271_acx_rate_policies(struct wl1271 *wl);
+int wl1271_acx_sta_rate_policies(struct wl1271 *wl);
+int wl1271_acx_ap_rate_policy(struct wl1271 *wl, struct conf_tx_rate_class *c,
+ u8 idx);
int wl1271_acx_ac_cfg(struct wl1271 *wl, u8 ac, u8 cw_min, u16 cw_max,
u8 aifsn, u16 txop);
int wl1271_acx_tid_cfg(struct wl1271 *wl, u8 queue_id, u8 channel_type,
@@ -1186,5 +1212,6 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl,
int wl1271_acx_set_ht_information(struct wl1271 *wl,
u16 ht_operation_mode);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
+int wl1271_acx_max_tx_retry(struct wl1271 *wl);

#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
index 785a530..f468d71 100644
--- a/drivers/net/wireless/wl12xx/init.c
+++ b/drivers/net/wireless/wl12xx/init.c
@@ -322,7 +322,7 @@ int wl1271_hw_init(struct wl1271 *wl)
}

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

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 7ead0ea..383631e 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1393,7 +1393,7 @@ static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
}
wl->rate_set = wl1271_min_rate_get(wl);
wl->sta_rate_set = 0;
- ret = wl1271_acx_rate_policies(wl);
+ ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
goto out;
ret = wl1271_acx_keep_alive_config(
@@ -1468,7 +1468,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
wl1271_set_band_rate(wl);

wl->basic_rate = wl1271_min_rate_get(wl);
- ret = wl1271_acx_rate_policies(wl);
+ ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
wl1271_warning("rate policy for update channel "
"failed %d", ret);
@@ -2017,7 +2017,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
rates);
wl->basic_rate = wl1271_min_rate_get(wl);
- ret = wl1271_acx_rate_policies(wl);
+ ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
goto out_sleep;

@@ -2071,7 +2071,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
/* revert back to minimum rates for the current band */
wl1271_set_band_rate(wl);
wl->basic_rate = wl1271_min_rate_get(wl);
- ret = wl1271_acx_rate_policies(wl);
+ ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
goto out_sleep;

diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index b44c75c..0cf210d 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -303,7 +303,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl)
woken_up = true;

wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
- wl1271_acx_rate_policies(wl);
+ wl1271_acx_sta_rate_policies(wl);
}

while ((skb = wl1271_skb_dequeue(wl))) {
--
1.7.1


2010-12-22 14:28:25

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 11/18] wl1271: AP mode - change filter config

Do not configure a group address table in AP mode

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 22 ++++++++++++----------
1 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 4b26451..f777073 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1611,7 +1611,8 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
struct wl1271 *wl = hw->priv;
int ret;

- wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter");
+ wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter changed %x"
+ " total %x", changed, *total);

mutex_lock(&wl->mutex);

@@ -1625,15 +1626,16 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
if (ret < 0)
goto out;

-
- if (*total & FIF_ALLMULTI)
- ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
- else if (fp)
- ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
- fp->mc_list,
- fp->mc_list_length);
- if (ret < 0)
- goto out_sleep;
+ if (wl->bss_type != BSS_TYPE_AP_BSS) {
+ if (*total & FIF_ALLMULTI)
+ ret = wl1271_acx_group_address_tbl(wl, false, NULL, 0);
+ else if (fp)
+ ret = wl1271_acx_group_address_tbl(wl, fp->enabled,
+ fp->mc_list,
+ fp->mc_list_length);
+ if (ret < 0)
+ goto out_sleep;
+ }

/* determine, whether supported filter values have changed */
if (changed == 0)
--
1.7.1


2010-12-23 11:05:21

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 01/18] wl1271: Add AP related configuration to conf_drv_settings

On Wed, Dec 22, 2010 at 22:22, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Rate class configuration has been split up for AP and STA modes.
>> Template related configuration likewise separated.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> @@ -153,6 +153,45 @@ static struct conf_drv_settings default_conf = {
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .tx_op_limit = 1504,
>> ? ? ? ? ? ? ? ? ? ? ? },
>> ? ? ? ? ? ? ? },
>> + ? ? ? ? ? ? .ap_rc_conf ? ? ? ? ? ? ? ? ?= {
>> + ? ? ? ? ? ? ? ? ? ? [0] = {
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .enabled_rates = 0x1EFF,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .short_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .long_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .aflags ? ? ?= 0,
>> + ? ? ? ? ? ? ? ? ? ? },
>> + ? ? ? ? ? ? ? ? ? ? [1] = {
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .enabled_rates = 0x1EFF,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .short_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .long_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .aflags ? ? ?= 0,
>> + ? ? ? ? ? ? ? ? ? ? },
>> + ? ? ? ? ? ? ? ? ? ? [2] = {
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .enabled_rates = 0x1EFF,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .short_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .long_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .aflags ? ? ?= 0,
>> + ? ? ? ? ? ? ? ? ? ? },
>> + ? ? ? ? ? ? ? ? ? ? [3] = {
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .enabled_rates = 0x1EFF,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .short_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .long_retry_limit = 10,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? .aflags ? ? ?= 0,
>> + ? ? ? ? ? ? ? ? ? ? },
>> + ? ? ? ? ? ? },
>> + ? ? ? ? ? ? .ap_mgmt_conf = {
>> + ? ? ? ? ? ? ? ? ? ? .enabled_rates ? ? ? = 0x7,
>> + ? ? ? ? ? ? ? ? ? ? .short_retry_limit ? = 10,
>> + ? ? ? ? ? ? ? ? ? ? .long_retry_limit ? ?= 10,
>> + ? ? ? ? ? ? ? ? ? ? .aflags ? ? ? ? ? ? ?= 0,
>> + ? ? ? ? ? ? },
>> + ? ? ? ? ? ? .ap_bcst_conf = {
>> + ? ? ? ? ? ? ? ? ? ? .enabled_rates ? ? ? = 0x1,
>> + ? ? ? ? ? ? ? ? ? ? .short_retry_limit ? = 10,
>> + ? ? ? ? ? ? ? ? ? ? .long_retry_limit ? ?= 10,
>> + ? ? ? ? ? ? ? ? ? ? .aflags ? ? ? ? ? ? ?= 0,
>> + ? ? ? ? ? ? },
>> +
>
> All the enabled_rates values here look quite magic. ?Is there any way
> these could be changed to macros or something more descriptive?

These are simply bitmasks of enabled rates. I can change these to
definitions and add some explanation about the limitations (basically
the FW supports up to 12 rates).

>
> Also, how do you deal with the different basic rates used by 11bg and
> 11a?

Currently AP mode is not supported for 11a. Obviously we'd need
different rate configuration there. This is controlled by the NVS file
right now, but its a good idea to hard-code only 11bg support for
AP-mode. We expect users to have only one NVS file on the device, and
we do want 11a support for STA.

Also looking at the rate config part made me realize that
"basic_rate_set" is not updated where it should be in AP mode. I'll
fix this as well.

Regards,
Arik

2010-12-28 06:16:30

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 06/18] wl1271: AP mode - init sequence

On Mon, Dec 27, 2010 at 14:57, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Split HW init sequence into AP/STA specific parts
>>
>> The AP specific init sequence includes configuration of templates, rate
>> classes, power mode, etc. Also unmask AP specific events in the event mbox.
>>
>> Separate the differences between AP and STA init into mode
>> specific functions called from wl1271_hw_init. The first is called after
>> radio configuration and the second after memory configuration.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
>> index 02f843c..d51324d 100644
>> --- a/drivers/net/wireless/wl12xx/cmd.h
>> +++ b/drivers/net/wireless/wl12xx/cmd.h
>> @@ -140,6 +140,13 @@ enum cmd_templ {
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? * For CTS-to-self (FastCTS) mechanism
>> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? * for BT/WLAN coexistence (SoftGemini). */
>> ? ? ? CMD_TEMPL_ARP_RSP,
>> +
>> + ? ? /* AP-mode specific */
>> + ? ? CMD_TEMPL_AP_BEACON = 13,
>> + ? ? CMD_TEMPL_AP_PROBE_RESPONSE,
>> + ? ? CMD_TEMPL_AP_ARP_RSP,
>> + ? ? CMD_TEMPL_DEAUTH_AP,
>> +
>> ? ? ? CMD_TEMPL_MAX = 0xff
>> ?};
>
> Cool, I see that you fixed the issue we had before. ?Just one question,
> is the CMD_TEMPL_AP_ARP_RSP the same as the original CMD_TEMPL_ARP_RSP?
> I mean is it only the order in the enum that is different between the AP
> and the STA firmwares? If that's the case, we should *really* try to get
> this fixed in the AP firmware.
>
> It's still new enough and not so many people are using it yet, right? So
> it's the time to fix it if it makes sense.

Actually the AP and STA modes will be merged in one FW in the future.
So its already unified there.
Also note arp-filtering is only relevant for the STA role for now.
Only stations get the BSS_CHANGED_ARP_FILTER notification.
Implementing this for AP will require changes in mac80211.

>
>
>> diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
>> index 799c369..df6666b 100644
>> --- a/drivers/net/wireless/wl12xx/init.c
>> +++ b/drivers/net/wireless/wl12xx/init.c
>
> [...]
>
>> @@ -145,10 +253,6 @@ int wl1271_init_phy_config(struct wl1271 *wl)
>> ? ? ? if (ret < 0)
>> ? ? ? ? ? ? ? return ret;
>>
>> - ? ? ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
>> - ? ? if (ret < 0)
>> - ? ? ? ? ? ? return ret;
>> -
>
> Here we're changing the order of these initialization ACXs, but it
> shouldn't be a problem. ?If it is a problem, it's a firmware bug that
> (hopefully) can be fixed.

Haven't seen a problem with this one. Also note this ACX will be
reconfigured again on the first wl1271_op_configure_filter() call.

>
>
>> @@ -213,11 +317,150 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
>> ? ? ? return 0;
>> ?}
>>
>> +static int wl1271_sta_hw_init(struct wl1271 *wl)
>> +{
>> + ? ? int ret;
>> +
>> + ? ? ret = wl1271_cmd_ext_radio_parms(wl);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? ret = wl1271_sta_init_templates_config(wl);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? /* Initialize connection monitoring thresholds */
>> + ? ? ret = wl1271_acx_conn_monit_params(wl, false);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? /* Beacon filtering */
>> + ? ? ret = wl1271_init_beacon_filter(wl);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? /* Bluetooth WLAN coexistence */
>> + ? ? ret = wl1271_init_pta(wl);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? /* Beacons and boradcast settings */
>
> You copied the typo, maybe you could take the opportunity to fix it?

Sure.

>
>> + ? ? ret = wl1271_init_beacon_broadcast(wl);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? /* Configure for ELP power saving */
>> + ? ? ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? /* Configure rssi/snr averaging weights */
>> + ? ? ret = wl1271_acx_rssi_snr_avg_weights(wl);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? ret = wl1271_acx_sta_rate_policies(wl);
>> + ? ? if (ret < 0)
>> + ? ? ? ? ? ? return ret;
>> +
>> + ? ? return 0;
>> +}
>
> Also here, this changes the initialization order a bit. ?I think this
> was Juuso's concern earlier. ?At least while we were having debug camps,
> the TI people supporting us were always asking us to be extremely
> cautious with the order of the initialization. ?They were saying that it
> was really important to do the initialization in the same way as the
> tiwlan driver does. ?Hopefully this is not an issue now...

We haven't encountered any issues in our internal testing (in both STA
and AP roles).

>
>
>> @@ -279,21 +512,11 @@ int wl1271_hw_init(struct wl1271 *wl)
>> ? ? ? if (ret < 0)
>> ? ? ? ? ? ? ? goto out_free_memmap;
>>
>> - ? ? /* Bluetooth WLAN coexistence */
>> - ? ? ret = wl1271_init_pta(wl);
>> - ? ? if (ret < 0)
>> - ? ? ? ? ? ? goto out_free_memmap;
>> -
>
> Just out of curiosity... Is AP coexistence with Bluetooth possible? This
> could be important in some scenarios.

It is possible and will be implemented in the future.

>
>
>> diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h
>> index 7762421..4d37210 100644
>> --- a/drivers/net/wireless/wl12xx/init.h
>> +++ b/drivers/net/wireless/wl12xx/init.h
>> @@ -27,10 +27,13 @@
>> ?#include "wl12xx.h"
>>
>> ?int wl1271_hw_init_power_auth(struct wl1271 *wl);
>> -int wl1271_init_templates_config(struct wl1271 *wl);
>> +int wl1271_sta_init_templates_config(struct wl1271 *wl);
>> ?int wl1271_init_phy_config(struct wl1271 *wl);
>> ?int wl1271_init_pta(struct wl1271 *wl);
>> ?int wl1271_init_energy_detection(struct wl1271 *wl);
>> ?int wl1271_hw_init(struct wl1271 *wl);
>>
>> +/* Functions from wl1271_main.c */
>> +u32 wl1271_min_rate_get(struct wl1271 *wl);
>> +
>
> This is bad and shouldn't be here. ?Need to find a better place for it.

How about tx.h? It's TX rate we're talking about here after all.
I can also change the name to wl1271_tx_min_rate_get().

>
>
>> diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h
>> index be21032..b230e72 100644
>> --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h
>> +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h
>> @@ -160,4 +160,9 @@ struct wl12xx_probe_resp_template {
>> ? ? ? struct wl12xx_ie_country country;
>> ?} __packed;
>>
>> +struct wl12xx_disconn_template {
>> + ? ? struct ieee80211_header header;
>> + ? ? __le16 disconn_reason;
>> +} __packed;
>> +
>
> Isn't padding necessary here to keep the struct 32-bit aligned?

AFAIK, templates should not be 32-bit aligned. Most of the templates
are not aligned.

Regards,
Arik

2010-12-23 07:51:30

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 05/18] wl1271: AP mode - workaround for FW bug on station remove

On Wed, Dec 22, 2010 at 23:19, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Sometimes an event indicating station removal is not sent up by
>> firmware. We work around this by always indicating success in when
>> a wait for the event timeouts.
>>
>> Temporary workaround until a FW fix is introduced.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> @@ -1108,9 +1122,11 @@ int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
>> ? ? ? ? ? ? ? goto out_free;
>> ? ? ? }
>>
>> - ? ? ret = wl1271_cmd_wait_for_event(wl, STA_REMOVE_COMPLETE_EVENT_ID);
>> - ? ? if (ret < 0)
>> - ? ? ? ? ? ? wl1271_error("cmd remove sta event completion error");
>> + ? ? /*
>> + ? ? ?* We are ok with a timeout here. The event is sometimes not sent
>> + ? ? ?* due to a firmware bug.
>> + ? ? ?*/
>> + ? ? wl1271_cmd_wait_for_event_or_timeout(wl, STA_REMOVE_COMPLETE_EVENT_ID);
>
> Why wait for the event at all then?
>

Well the bug is pretty rare (something like 1 in 100). Most of the
time we get the event immediately. This way a station can reconnect
very quickly in most cases.

Regards,
Arik

2010-12-27 13:34:23

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 12/18] wl1271: AP mode - add STA add/remove ops

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Allocate and free host link IDs (HLIDs) for each link. A per-STA
> data structure keeps the HLID of each STA.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> +static int wl1271_op_sta_add(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta)
> +{
> + struct wl1271 *wl = hw->priv;
> + int ret = 0;
> + u8 hlid;
> +
> + mutex_lock(&wl->mutex);
> +
> + if (unlikely(wl->state == WL1271_STATE_OFF))
> + goto out;
> +
> + if (wl->bss_type != BSS_TYPE_AP_BSS)
> + goto out;

Should we ever bump into these two conditions? Shouldn't we warn or
return errors?


> +static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + struct ieee80211_sta *sta)
> +{
> + struct wl1271 *wl = hw->priv;
> + struct wl1271_station *wl_sta;
> + int ret = 0;
> +
> + mutex_lock(&wl->mutex);
> +
> + if (unlikely(wl->state == WL1271_STATE_OFF))
> + goto out;
> +
> + if (wl->bss_type != BSS_TYPE_AP_BSS)
> + goto out;

Same here...


> + wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
> +
> + ret = wl1271_ps_elp_wakeup(wl, false);
> + if (ret < 0)
> + goto out;
> +
> + wl_sta = (struct wl1271_station *)sta->drv_priv;
> + ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
> + if (ret < 0)
> + goto out_sleep;

Would it make sense to check if the sta is really in use (ie. it is
marked in the ap_sta_map) before trying to remove it?


--
Cheers,
Luca.


2010-12-23 08:11:59

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 08/18] wl1271: Add AP related definitions to HOST-FW interface

On Wed, Dec 22, 2010 at 23:36, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Change structures in a non-destructive manner. This means no
>> changes in size or location of existing members used by STA.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h
>> index 3abb26f..19fd591 100644
>> --- a/drivers/net/wireless/wl12xx/rx.h
>> +++ b/drivers/net/wireless/wl12xx/rx.h
>> @@ -86,10 +86,11 @@
>> ?/*
>> ? * RX Descriptor status
>> ? *
>> - * Bits 0-2 - status
>> - * Bits 3-7 - reserved
>> + * Bits 0-2 - error code
>> + * Bits 3-5 - process_id tag (AP mode FW)
>> + * Bits 6-7 - reserved
>> ? */
>> -#define WL1271_RX_DESC_STATUS_MASK ? ? ?0x07
>> +#define WL1271_RX_DESC_STATUS_MASK ? ? ?0x03
>
> What happened with bit 2?

You're right. Actually ported over a bug from the original TI driver.
Since only errors 1-3 are defined this still works. But its a bug
anyway. Will revert.

Regards,
Arik

2010-12-22 14:28:22

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 10/18] wl1271: AP mode config in ieee80211_ops.config

Separate configuration according to mode. AP has different rate
set configuration and no handling of idle-state.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 58 +++++++++++++++++++++--------------
1 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 59c7fb3..4b26451 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1391,7 +1391,7 @@ u32 wl1271_min_rate_get(struct wl1271 *wl)
return rate;
}

-static int wl1271_handle_idle(struct wl1271 *wl, bool idle)
+static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle)
{
int ret;

@@ -1432,14 +1432,17 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
struct wl1271 *wl = hw->priv;
struct ieee80211_conf *conf = &hw->conf;
int channel, ret = 0;
+ bool is_ap;

channel = ieee80211_frequency_to_channel(conf->channel->center_freq);

- wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s",
+ wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d %s"
+ " changed 0x%x",
channel,
conf->flags & IEEE80211_CONF_PS ? "on" : "off",
conf->power_level,
- conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use");
+ conf->flags & IEEE80211_CONF_IDLE ? "idle" : "in use",
+ changed);

/*
* mac80211 will go to idle nearly immediately after transmitting some
@@ -1468,31 +1471,40 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
wl->band = conf->channel->band;
wl->channel = channel;

- /*
- * FIXME: the mac80211 should really provide a fixed rate
- * to use here. for now, just use the smallest possible rate
- * for the band as a fixed rate for association frames and
- * other control messages.
- */
- if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
- wl1271_set_band_rate(wl);
-
- wl->basic_rate = wl1271_min_rate_get(wl);
- ret = wl1271_acx_sta_rate_policies(wl);
- if (ret < 0)
- wl1271_warning("rate policy for update channel "
- "failed %d", ret);
+ if (is_ap) {
+ /* set basic rates according to HW */
+ u32 rates = wl->vif->bss_conf.basic_rates;
+ wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl,
+ rates);
+ wl->basic_rate = wl1271_min_rate_get(wl);
+ } else {
+ /*
+ * FIXME: the mac80211 should really provide a fixed
+ * rate to use here. for now, just use the smallest
+ * possible rate for the band as a fixed rate for
+ * association frames and other control messages.
+ */
+ if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
+ wl1271_set_band_rate(wl);

- if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
- ret = wl1271_join(wl, false);
+ wl->basic_rate = wl1271_min_rate_get(wl);
+ ret = wl1271_acx_sta_rate_policies(wl);
if (ret < 0)
- wl1271_warning("cmd join to update channel "
+ wl1271_warning("rate policy for channel "
"failed %d", ret);
+
+ if (test_bit(WL1271_FLAG_JOINED, &wl->flags)) {
+ ret = wl1271_join(wl, false);
+ if (ret < 0)
+ wl1271_warning("cmd join on channel "
+ "failed %d", ret);
+ }
}
}

- if (changed & IEEE80211_CONF_CHANGE_IDLE) {
- ret = wl1271_handle_idle(wl, conf->flags & IEEE80211_CONF_IDLE);
+ if (changed & IEEE80211_CONF_CHANGE_IDLE && !is_ap) {
+ ret = wl1271_sta_handle_idle(wl,
+ conf->flags & IEEE80211_CONF_IDLE);
if (ret < 0)
wl1271_warning("idle mode change failed %d", ret);
}
--
1.7.1


2010-12-22 21:29:49

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 07/18] wl1271: AP specific RX filter configuration

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Set filters according to the mode of operation.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h
> index d67dcff..5f19a40 100644
> --- a/drivers/net/wireless/wl12xx/boot.h
> +++ b/drivers/net/wireless/wl12xx/boot.h
> @@ -69,4 +69,7 @@ struct wl1271_static_data {
> #define FREF_CLK_POLARITY_BITS 0xfffff8ff
> #define CLK_REQ_OUTN_SEL 0x700
>
> +/* Functions from wl1271_main.c */
> +void wl1271_set_default_filters(struct wl1271 *wl);
> +

This is not good. Can't this be defined in rx.c and rx.h instead?


--
Cheers,
Luca.


2010-12-22 14:28:03

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 03/18] wl1271: AP mode - add AP specific event

Add STA-remove completion event. Unmask it during boot if operating in
AP-mode.
Ignore unrelated events in AP-mode.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/boot.c | 3 +++
drivers/net/wireless/wl12xx/event.c | 7 ++++---
drivers/net/wireless/wl12xx/event.h | 1 +
3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c
index 4a9f929..bb4f283 100644
--- a/drivers/net/wireless/wl12xx/boot.c
+++ b/drivers/net/wireless/wl12xx/boot.c
@@ -432,6 +432,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID;

+ if (wl->bss_type == BSS_TYPE_AP_BSS)
+ wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID;
+
ret = wl1271_event_unmask(wl);
if (ret < 0) {
wl1271_error("EVENT mask setting failed");
diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c
index f9146f5..3376a5d 100644
--- a/drivers/net/wireless/wl12xx/event.c
+++ b/drivers/net/wireless/wl12xx/event.c
@@ -186,6 +186,7 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
int ret;
u32 vector;
bool beacon_loss = false;
+ bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);

wl1271_event_mbox_dump(mbox);

@@ -218,21 +219,21 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
* BSS_LOSE_EVENT, beacon loss has to be reported to the stack.
*
*/
- if (vector & BSS_LOSE_EVENT_ID) {
+ if ((vector & BSS_LOSE_EVENT_ID) && !is_ap) {
wl1271_info("Beacon loss detected.");

/* indicate to the stack, that beacons have been lost */
beacon_loss = true;
}

- if (vector & PS_REPORT_EVENT_ID) {
+ if ((vector & PS_REPORT_EVENT_ID) && !is_ap) {
wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
ret = wl1271_event_ps_report(wl, mbox, &beacon_loss);
if (ret < 0)
return ret;
}

- if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID)
+ if ((vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) && !is_ap)
wl1271_event_pspoll_delivery_fail(wl);

if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h
index 6cce014..fd955f3 100644
--- a/drivers/net/wireless/wl12xx/event.h
+++ b/drivers/net/wireless/wl12xx/event.h
@@ -59,6 +59,7 @@ enum {
BSS_LOSE_EVENT_ID = BIT(18),
REGAINED_BSS_EVENT_ID = BIT(19),
ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20),
+ STA_REMOVE_COMPLETE_EVENT_ID = BIT(21), /* AP */
SOFT_GEMINI_SENSE_EVENT_ID = BIT(22),
SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23),
SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24),
--
1.7.1


2010-12-22 14:28:40

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 16/18] wl1271: AP mode - fetch appropriate firmware for AP

AP and STA modes use different firmwares.

Differentiate the firmware files by name and fetch the appropriate one
when add_interface is called by mac80211. The STA firmware is chosen for
PLT mode.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 33 ++++++++++++++++++++++++++++++---
drivers/net/wireless/wl12xx/wl12xx.h | 3 +++
2 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 5536079..eb2c3c5 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -658,9 +658,26 @@ out:
static int wl1271_fetch_firmware(struct wl1271 *wl)
{
const struct firmware *fw;
+ const char *fw_name;
int ret;

- ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl));
+ switch (wl->bss_type) {
+ case BSS_TYPE_AP_BSS:
+ fw_name = WL1271_AP_FW_NAME;
+ break;
+ case BSS_TYPE_IBSS:
+ case BSS_TYPE_STA_BSS:
+ fw_name = WL1271_FW_NAME;
+ break;
+ default:
+ wl1271_error("no compatible firmware for bss_type %d",
+ wl->bss_type);
+ return -EINVAL;
+ }
+
+ wl1271_debug(DEBUG_BOOT, "booting firmware %s", fw_name);
+
+ ret = request_firmware(&fw, fw_name, wl1271_wl_to_dev(wl));

if (ret < 0) {
wl1271_error("could not get firmware: %d", ret);
@@ -674,6 +691,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
goto out;
}

+ vfree(wl->fw);
wl->fw_len = fw->size;
wl->fw = vmalloc(wl->fw_len);

@@ -684,7 +702,7 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
}

memcpy(wl->fw, fw->data, wl->fw_len);
-
+ wl->fw_bss_type = wl->bss_type;
ret = 0;

out:
@@ -820,7 +838,8 @@ static int wl1271_chip_wakeup(struct wl1271 *wl)
goto out;
}

- if (wl->fw == NULL) {
+ /* Make sure the firmware type matches the BSS type */
+ if (wl->fw == NULL || wl->fw_bss_type != wl->bss_type) {
ret = wl1271_fetch_firmware(wl);
if (ret < 0)
goto out;
@@ -853,6 +872,8 @@ int wl1271_plt_start(struct wl1271 *wl)
goto out;
}

+ wl->bss_type = BSS_TYPE_STA_BSS;
+
while (retries) {
retries--;
ret = wl1271_chip_wakeup(wl);
@@ -1010,6 +1031,9 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
*
* The MAC address is first known when the corresponding interface
* is added. That is where we will initialize the hardware.
+ *
+ * In addition, we currently have different firmwares for AP and managed
+ * operation. We will know which to boot according to interface type.
*/

return 0;
@@ -3144,6 +3168,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->flags = 0;
wl->sg_enabled = true;
wl->hw_pg_ver = -1;
+ wl->bss_type = MAX_BSS_TYPE;
+ wl->set_bss_type = MAX_BSS_TYPE;
+ wl->fw_bss_type = MAX_BSS_TYPE;

memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 98532fc..80e0e39 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -124,6 +124,8 @@ extern u32 wl12xx_debug_level;


#define WL1271_FW_NAME "wl1271-fw.bin"
+#define WL1271_AP_FW_NAME "wl1271-fw-ap.bin"
+
#define WL1271_NVS_NAME "wl1271-nvs.bin"

#define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff))
@@ -311,6 +313,7 @@ struct wl1271 {

u8 *fw;
size_t fw_len;
+ u8 fw_bss_type;
struct wl1271_nvs_file *nvs;
size_t nvs_len;

--
1.7.1


2010-12-22 14:28:11

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 06/18] wl1271: AP mode - init sequence

Split HW init sequence into AP/STA specific parts

The AP specific init sequence includes configuration of templates, rate
classes, power mode, etc. Also unmask AP specific events in the event mbox.

Separate the differences between AP and STA init into mode
specific functions called from wl1271_hw_init. The first is called after
radio configuration and the second after memory configuration.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/cmd.h | 7 +
drivers/net/wireless/wl12xx/init.c | 352 ++++++++++++++++++++++------
drivers/net/wireless/wl12xx/init.h | 5 +-
drivers/net/wireless/wl12xx/main.c | 4 +-
drivers/net/wireless/wl12xx/wl12xx_80211.h | 5 +
5 files changed, 296 insertions(+), 77 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index 02f843c..d51324d 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -140,6 +140,13 @@ enum cmd_templ {
* For CTS-to-self (FastCTS) mechanism
* for BT/WLAN coexistence (SoftGemini). */
CMD_TEMPL_ARP_RSP,
+
+ /* AP-mode specific */
+ CMD_TEMPL_AP_BEACON = 13,
+ CMD_TEMPL_AP_PROBE_RESPONSE,
+ CMD_TEMPL_AP_ARP_RSP,
+ CMD_TEMPL_DEAUTH_AP,
+
CMD_TEMPL_MAX = 0xff
};

diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
index 799c369..df6666b 100644
--- a/drivers/net/wireless/wl12xx/init.c
+++ b/drivers/net/wireless/wl12xx/init.c
@@ -30,27 +30,9 @@
#include "acx.h"
#include "cmd.h"
#include "reg.h"
+#include "tx.h"

-static int wl1271_init_hwenc_config(struct wl1271 *wl)
-{
- int ret;
-
- ret = wl1271_acx_feature_cfg(wl);
- if (ret < 0) {
- wl1271_warning("couldn't set feature config");
- return ret;
- }
-
- ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key);
- if (ret < 0) {
- wl1271_warning("couldn't set default key");
- return ret;
- }
-
- return 0;
-}
-
-int wl1271_init_templates_config(struct wl1271 *wl)
+int wl1271_sta_init_templates_config(struct wl1271 *wl)
{
int ret, i;

@@ -118,6 +100,132 @@ int wl1271_init_templates_config(struct wl1271 *wl)
return 0;
}

+static int wl1271_ap_init_deauth_template(struct wl1271 *wl)
+{
+ struct wl12xx_disconn_template *tmpl;
+ int ret;
+
+ tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL);
+ if (!tmpl) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_DEAUTH);
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP,
+ tmpl, sizeof(*tmpl), 0,
+ wl1271_min_rate_get(wl));
+
+out:
+ kfree(tmpl);
+ return ret;
+}
+
+static int wl1271_ap_init_null_template(struct wl1271 *wl)
+{
+ struct ieee80211_hdr_3addr *nullfunc;
+ int ret;
+
+ nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL);
+ if (!nullfunc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_NULLFUNC |
+ IEEE80211_FCTL_FROMDS);
+
+ /* nullfunc->addr1 is filled by FW */
+
+ memcpy(nullfunc->addr2, wl->mac_addr, ETH_ALEN);
+ memcpy(nullfunc->addr3, wl->mac_addr, ETH_ALEN);
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc,
+ sizeof(*nullfunc), 0,
+ wl1271_min_rate_get(wl));
+
+out:
+ kfree(nullfunc);
+ return ret;
+}
+
+static int wl1271_ap_init_qos_null_template(struct wl1271 *wl)
+{
+ struct ieee80211_qos_hdr *qosnull;
+ int ret;
+
+ qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL);
+ if (!qosnull) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ qosnull->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_NULLFUNC |
+ IEEE80211_FCTL_FROMDS);
+
+ /* qosnull->addr1 is filled by FW */
+
+ memcpy(qosnull->addr2, wl->mac_addr, ETH_ALEN);
+ memcpy(qosnull->addr3, wl->mac_addr, ETH_ALEN);
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull,
+ sizeof(*qosnull), 0,
+ wl1271_min_rate_get(wl));
+
+out:
+ kfree(qosnull);
+ return ret;
+}
+
+static int wl1271_ap_init_templates_config(struct wl1271 *wl)
+{
+ int ret;
+
+ /*
+ * Put very large empty placeholders for all templates. These
+ * reserve memory for later.
+ */
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_PROBE_RESPONSE, NULL,
+ sizeof
+ (struct wl12xx_probe_resp_template),
+ 0, WL1271_RATE_AUTOMATIC);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_AP_BEACON, NULL,
+ sizeof
+ (struct wl12xx_beacon_template),
+ 0, WL1271_RATE_AUTOMATIC);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, NULL,
+ sizeof
+ (struct wl12xx_disconn_template),
+ 0, WL1271_RATE_AUTOMATIC);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL,
+ sizeof(struct wl12xx_null_data_template),
+ 0, WL1271_RATE_AUTOMATIC);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL,
+ sizeof
+ (struct wl12xx_qos_null_data_template),
+ 0, WL1271_RATE_AUTOMATIC);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter)
{
int ret;
@@ -145,10 +253,6 @@ int wl1271_init_phy_config(struct wl1271 *wl)
if (ret < 0)
return ret;

- ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
- if (ret < 0)
- return ret;
-
ret = wl1271_acx_service_period_timeout(wl);
if (ret < 0)
return ret;
@@ -213,11 +317,150 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
return 0;
}

+static int wl1271_sta_hw_init(struct wl1271 *wl)
+{
+ int ret;
+
+ ret = wl1271_cmd_ext_radio_parms(wl);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_sta_init_templates_config(wl);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Initialize connection monitoring thresholds */
+ ret = wl1271_acx_conn_monit_params(wl, false);
+ if (ret < 0)
+ return ret;
+
+ /* Beacon filtering */
+ ret = wl1271_init_beacon_filter(wl);
+ if (ret < 0)
+ return ret;
+
+ /* Bluetooth WLAN coexistence */
+ ret = wl1271_init_pta(wl);
+ if (ret < 0)
+ return ret;
+
+ /* Beacons and boradcast settings */
+ ret = wl1271_init_beacon_broadcast(wl);
+ if (ret < 0)
+ return ret;
+
+ /* Configure for ELP power saving */
+ ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
+ if (ret < 0)
+ return ret;
+
+ /* Configure rssi/snr averaging weights */
+ ret = wl1271_acx_rssi_snr_avg_weights(wl);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_acx_sta_rate_policies(wl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int wl1271_sta_hw_init_post_mem(struct wl1271 *wl)
+{
+ int ret, i;
+
+ ret = wl1271_cmd_set_sta_default_wep_key(wl, wl->default_key);
+ if (ret < 0) {
+ wl1271_warning("couldn't set default key");
+ return ret;
+ }
+
+ /* disable all keep-alive templates */
+ for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
+ ret = wl1271_acx_keep_alive_config(wl, i,
+ ACX_KEEP_ALIVE_TPL_INVALID);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* disable the keep-alive feature */
+ ret = wl1271_acx_keep_alive_mode(wl, false);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int wl1271_ap_hw_init(struct wl1271 *wl)
+{
+ int ret, i;
+
+ ret = wl1271_ap_init_templates_config(wl);
+ if (ret < 0)
+ return ret;
+
+ /* Configure for power always on */
+ ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM);
+ if (ret < 0)
+ return ret;
+
+ /* Configure initial TX rate classes */
+ for (i = 0; i < wl->conf.tx.ac_conf_count; i++) {
+ ret = wl1271_acx_ap_rate_policy(wl,
+ &wl->conf.tx.ap_rc_conf[i], i);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = wl1271_acx_ap_rate_policy(wl,
+ &wl->conf.tx.ap_mgmt_conf,
+ ACX_TX_AP_MODE_MGMT_RATE);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_acx_ap_rate_policy(wl,
+ &wl->conf.tx.ap_bcst_conf,
+ ACX_TX_AP_MODE_BCST_RATE);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_acx_max_tx_retry(wl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int wl1271_ap_hw_init_post_mem(struct wl1271 *wl)
+{
+ int ret;
+
+ ret = wl1271_ap_init_deauth_template(wl);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_ap_init_null_template(wl);
+ if (ret < 0)
+ return ret;
+
+ ret = wl1271_ap_init_qos_null_template(wl);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
int wl1271_hw_init(struct wl1271 *wl)
{
struct conf_tx_ac_category *conf_ac;
struct conf_tx_tid *conf_tid;
int ret, i;
+ bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);

ret = wl1271_cmd_general_parms(wl);
if (ret < 0)
@@ -227,12 +470,12 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
return ret;

- ret = wl1271_cmd_ext_radio_parms(wl);
- if (ret < 0)
- return ret;
+ /* Mode specific init */
+ if (is_ap)
+ ret = wl1271_ap_hw_init(wl);
+ else
+ ret = wl1271_sta_hw_init(wl);

- /* Template settings */
- ret = wl1271_init_templates_config(wl);
if (ret < 0)
return ret;

@@ -259,16 +502,6 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;

- /* Initialize connection monitoring thresholds */
- ret = wl1271_acx_conn_monit_params(wl, false);
- if (ret < 0)
- goto out_free_memmap;
-
- /* Beacon filtering */
- ret = wl1271_init_beacon_filter(wl);
- if (ret < 0)
- goto out_free_memmap;
-
/* Configure TX patch complete interrupt behavior */
ret = wl1271_acx_tx_config_options(wl);
if (ret < 0)
@@ -279,21 +512,11 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;

- /* Bluetooth WLAN coexistence */
- ret = wl1271_init_pta(wl);
- if (ret < 0)
- goto out_free_memmap;
-
/* Energy detection */
ret = wl1271_init_energy_detection(wl);
if (ret < 0)
goto out_free_memmap;

- /* Beacons and boradcast settings */
- ret = wl1271_init_beacon_broadcast(wl);
- if (ret < 0)
- goto out_free_memmap;
-
/* Default fragmentation threshold */
ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold);
if (ret < 0)
@@ -321,23 +544,13 @@ int wl1271_hw_init(struct wl1271 *wl)
goto out_free_memmap;
}

- /* Configure TX rate classes */
- ret = wl1271_acx_sta_rate_policies(wl);
- if (ret < 0)
- goto out_free_memmap;
-
/* Enable data path */
ret = wl1271_cmd_data_path(wl, 1);
if (ret < 0)
goto out_free_memmap;

- /* Configure for ELP power saving */
- ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
- if (ret < 0)
- goto out_free_memmap;
-
/* Configure HW encryption */
- ret = wl1271_init_hwenc_config(wl);
+ ret = wl1271_acx_feature_cfg(wl);
if (ret < 0)
goto out_free_memmap;

@@ -346,21 +559,12 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;

- /* disable all keep-alive templates */
- for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) {
- ret = wl1271_acx_keep_alive_config(wl, i,
- ACX_KEEP_ALIVE_TPL_INVALID);
- if (ret < 0)
- goto out_free_memmap;
- }
+ /* Mode specific init - post mem init */
+ if (is_ap)
+ ret = wl1271_ap_hw_init_post_mem(wl);
+ else
+ ret = wl1271_sta_hw_init_post_mem(wl);

- /* disable the keep-alive feature */
- ret = wl1271_acx_keep_alive_mode(wl, false);
- if (ret < 0)
- goto out_free_memmap;
-
- /* Configure rssi/snr averaging weights */
- ret = wl1271_acx_rssi_snr_avg_weights(wl);
if (ret < 0)
goto out_free_memmap;

diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h
index 7762421..4d37210 100644
--- a/drivers/net/wireless/wl12xx/init.h
+++ b/drivers/net/wireless/wl12xx/init.h
@@ -27,10 +27,13 @@
#include "wl12xx.h"

int wl1271_hw_init_power_auth(struct wl1271 *wl);
-int wl1271_init_templates_config(struct wl1271 *wl);
+int wl1271_sta_init_templates_config(struct wl1271 *wl);
int wl1271_init_phy_config(struct wl1271 *wl);
int wl1271_init_pta(struct wl1271 *wl);
int wl1271_init_energy_detection(struct wl1271 *wl);
int wl1271_hw_init(struct wl1271 *wl);

+/* Functions from wl1271_main.c */
+u32 wl1271_min_rate_get(struct wl1271 *wl);
+
#endif
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index d241a12..5354190 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -434,7 +434,7 @@ static int wl1271_plt_init(struct wl1271 *wl)
if (ret < 0)
return ret;

- ret = wl1271_init_templates_config(wl);
+ ret = wl1271_sta_init_templates_config(wl);
if (ret < 0)
return ret;

@@ -1363,7 +1363,7 @@ static void wl1271_set_band_rate(struct wl1271 *wl)
wl->basic_rate_set = wl->conf.tx.basic_rate_5;
}

-static u32 wl1271_min_rate_get(struct wl1271 *wl)
+u32 wl1271_min_rate_get(struct wl1271 *wl)
{
int i;
u32 rate = 0;
diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h
index be21032..b230e72 100644
--- a/drivers/net/wireless/wl12xx/wl12xx_80211.h
+++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h
@@ -160,4 +160,9 @@ struct wl12xx_probe_resp_template {
struct wl12xx_ie_country country;
} __packed;

+struct wl12xx_disconn_template {
+ struct ieee80211_header header;
+ __le16 disconn_reason;
+} __packed;
+
#endif
--
1.7.1


2010-12-22 14:28:17

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 08/18] wl1271: Add AP related definitions to HOST-FW interface

Change structures in a non-destructive manner. This means no
changes in size or location of existing members used by STA.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/event.h | 7 ++++++-
drivers/net/wireless/wl12xx/rx.h | 12 ++++++++----
drivers/net/wireless/wl12xx/tx.h | 9 +++++++--
drivers/net/wireless/wl12xx/wl12xx.h | 18 +++++++++++++++++-
4 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/event.h
index fd955f3..1d5ef67 100644
--- a/drivers/net/wireless/wl12xx/event.h
+++ b/drivers/net/wireless/wl12xx/event.h
@@ -116,7 +116,12 @@ struct event_mailbox {
u8 scheduled_scan_status;
u8 ps_status;

- u8 reserved_5[29];
+ /* AP FW only */
+ u8 hlid_removed;
+ __le16 sta_aging_status;
+ __le16 sta_tx_retry_exceeded;
+
+ u8 reserved_5[24];
} __packed;

int wl1271_event_unmask(struct wl1271 *wl);
diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h
index 3abb26f..19fd591 100644
--- a/drivers/net/wireless/wl12xx/rx.h
+++ b/drivers/net/wireless/wl12xx/rx.h
@@ -86,10 +86,11 @@
/*
* RX Descriptor status
*
- * Bits 0-2 - status
- * Bits 3-7 - reserved
+ * Bits 0-2 - error code
+ * Bits 3-5 - process_id tag (AP mode FW)
+ * Bits 6-7 - reserved
*/
-#define WL1271_RX_DESC_STATUS_MASK 0x07
+#define WL1271_RX_DESC_STATUS_MASK 0x03

#define WL1271_RX_DESC_SUCCESS 0x00
#define WL1271_RX_DESC_DECRYPT_FAIL 0x01
@@ -110,7 +111,10 @@ struct wl1271_rx_descriptor {
u8 snr;
__le32 timestamp;
u8 packet_class;
- u8 process_id;
+ union {
+ u8 process_id; /* STA FW */
+ u8 hlid; /* AP FW */
+ } __packed;
u8 pad_len;
u8 reserved;
} __packed;
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
index 903e5dc..0662b19 100644
--- a/drivers/net/wireless/wl12xx/tx.h
+++ b/drivers/net/wireless/wl12xx/tx.h
@@ -29,6 +29,7 @@
#define TX_HW_BLOCK_SIZE 252

#define TX_HW_MGMT_PKT_LIFETIME_TU 2000
+#define TX_HW_AP_MODE_PKT_LIFETIME_TU 8000
/* The chipset reference driver states, that the "aid" value 1
* is for infra-BSS, but is still always used */
#define TX_HW_DEFAULT_AID 1
@@ -77,8 +78,12 @@ struct wl1271_tx_hw_descr {
u8 id;
/* The packet TID value (as User-Priority) */
u8 tid;
- /* Identifier of the remote STA in IBSS, 1 in infra-BSS */
- u8 aid;
+ union {
+ /* STA - Identifier of the remote STA in IBSS, 1 in infra-BSS */
+ u8 aid;
+ /* AP - host link ID (HLID) */
+ u8 hlid;
+ } __packed;
u8 reserved;
} __packed;

diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 136e87b..ebb4a4d 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -195,6 +195,11 @@ struct wl1271_stats {
#define NUM_TX_QUEUES 4
#define NUM_RX_PKT_DESC 8

+#define AP_MAX_STATIONS 5
+
+/* Broadcast and Global links + links to stations */
+#define AP_MAX_LINKS (AP_MAX_STATIONS + 2)
+
/* FW status registers */
struct wl1271_fw_status {
__le32 intr;
@@ -205,7 +210,18 @@ struct wl1271_fw_status {
__le32 rx_pkt_descs[NUM_RX_PKT_DESC];
__le32 tx_released_blks[NUM_TX_QUEUES];
__le32 fw_localtime;
- __le32 padding[2];
+
+ /* Next fields valid only in AP FW */
+
+ /*
+ * A bitmap (where each bit represents a single HLID)
+ * to indicate if the station is in PS mode.
+ */
+ __le32 link_ps_bitmap;
+
+ /* Number of freed MBs per HLID */
+ u8 tx_lnk_free_blks[AP_MAX_LINKS];
+ u8 padding_1[1];
} __packed;

struct wl1271_rx_mem_pool_addr {
--
1.7.1


2010-12-22 20:44:24

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] wl1271: AP mode - AP specific CMD_CONFIGURE sub-commands

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Add AP max retries and rate policy configuration.
> Rename STA rate policy configuration function.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
> index 9cbc3f4..d2a68bf 100644
> --- a/drivers/net/wireless/wl12xx/acx.h
> +++ b/drivers/net/wireless/wl12xx/acx.h

[...]

> + __le32 rate_policy_idx; /* The index of the rate policy */

This comment is quite useless. ;)


> @@ -1062,6 +1072,19 @@ struct wl1271_acx_fw_tsf_information {
> u8 padding[3];
> } __packed;
>
> +#define WL1271_AP_MAX_TX_RETRY 100

Should this be part of the conf.h settings? Should it be made more
easily configurable?


--
Cheers,
Luca.


2010-12-28 12:03:46

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 15/18] wl1271: AP mode - encryption support

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Encryption key configuration is different for AP/STA modes.
>
> AP encryption keys are recorded when the BSS is not started. On BSS
> start they are propagated to the AP (in wl1271_ap_init_hwenc).
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
> index 0662b19..81ba75d 100644
> --- a/drivers/net/wireless/wl12xx/tx.h
> +++ b/drivers/net/wireless/wl12xx/tx.h
> @@ -152,4 +152,8 @@ void wl1271_tx_flush(struct wl1271 *wl);
> u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
> u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);
>
> +/* Functions from wl1271_main.c */
> +
> +int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id);
> +
> #endif

Again, same thing. Can we put this somewhere else?


--
Cheers,
Luca.


2010-12-23 07:59:10

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 07/18] wl1271: AP specific RX filter configuration

On Wed, Dec 22, 2010 at 23:29, Luciano Coelho <[email protected]> wrote:
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
>> Set filters according to the mode of operation.
>>
>> Signed-off-by: Arik Nemtsov <[email protected]>
>> ---
>
> [...]
>
>> diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h
>> index d67dcff..5f19a40 100644
>> --- a/drivers/net/wireless/wl12xx/boot.h
>> +++ b/drivers/net/wireless/wl12xx/boot.h
>> @@ -69,4 +69,7 @@ struct wl1271_static_data {
>> ?#define FREF_CLK_POLARITY_BITS 0xfffff8ff
>> ?#define CLK_REQ_OUTN_SEL ? ? ? 0x700
>>
>> +/* Functions from wl1271_main.c */
>> +void wl1271_set_default_filters(struct wl1271 *wl);
>> +
>
> This is not good. ?Can't this be defined in rx.c and rx.h instead?

Good point. Will do.

Regards,
Arik

2010-12-22 14:28:31

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 13/18] wl1271: AP mode - changes in TX path

When in AP mode set appropriate HLID and rate policy for each skb.
Respond to supported-rates related changes in op_tx only when acting
as STA.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 3 +-
drivers/net/wireless/wl12xx/tx.c | 55 ++++++++++++++++++++++++++++++------
2 files changed, 48 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 13a1c6e..3cc4f79 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -943,7 +943,8 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
spin_lock_irqsave(&wl->wl_lock, flags);
if (sta &&
(sta->supp_rates[conf->channel->band] !=
- (wl->sta_rate_set & HW_BG_RATES_MASK))) {
+ (wl->sta_rate_set & HW_BG_RATES_MASK)) &&
+ wl->bss_type != BSS_TYPE_AP_BSS) {
wl->sta_rate_set = sta->supp_rates[conf->channel->band];
set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags);
}
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 442a7bd..70a7bd4 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -23,6 +23,7 @@

#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/etherdevice.h>

#include "wl12xx.h"
#include "io.h"
@@ -99,7 +100,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
{
struct timespec ts;
struct wl1271_tx_hw_descr *desc;
- int pad, ac;
+ int pad, ac, rate_idx;
s64 hosttime;
u16 tx_attr;

@@ -117,7 +118,11 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
getnstimeofday(&ts);
hosttime = (timespec_to_ns(&ts) >> 10);
desc->start_time = cpu_to_le32(hosttime - wl->time_offset);
- desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
+
+ if (wl->bss_type != BSS_TYPE_AP_BSS)
+ desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
+ else
+ desc->life_time = cpu_to_le16(TX_HW_AP_MODE_PKT_LIFETIME_TU);

/* configure the tx attributes */
tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER;
@@ -125,7 +130,41 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
/* queue (we use same identifiers for tid's and ac's */
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
desc->tid = ac;
- desc->aid = TX_HW_DEFAULT_AID;
+
+ if (wl->bss_type != BSS_TYPE_AP_BSS) {
+ desc->aid = TX_HW_DEFAULT_AID;
+
+ /* if the packets are destined for AP (have a STA entry)
+ send them with AP rate policies, otherwise use default
+ basic rates */
+ if (control->control.sta)
+ rate_idx = ACX_TX_AP_FULL_RATE;
+ else
+ rate_idx = ACX_TX_BASIC_RATE;
+ } else {
+ if (control->control.sta) {
+ struct wl1271_station *wl_sta;
+
+ wl_sta = (struct wl1271_station *)
+ control->control.sta->drv_priv;
+ desc->hlid = wl_sta->hlid;
+ rate_idx = ac;
+ } else {
+ struct ieee80211_hdr *hdr;
+
+ hdr = (struct ieee80211_hdr *)
+ (skb->data + sizeof(*desc));
+ if (ieee80211_is_mgmt(hdr->frame_control)) {
+ desc->hlid = WL1271_AP_GLOBAL_HLID;
+ rate_idx = ACX_TX_AP_MODE_MGMT_RATE;
+ } else {
+ desc->hlid = WL1271_AP_BROADCAST_HLID;
+ rate_idx = ACX_TX_AP_MODE_BCST_RATE;
+ }
+ }
+ }
+
+ tx_attr |= rate_idx << TX_HW_ATTR_OFST_RATE_POLICY;
desc->reserved = 0;

/* align the length (and store in terms of words) */
@@ -136,14 +175,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
pad = pad - skb->len;
tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD;

- /* if the packets are destined for AP (have a STA entry) send them
- with AP rate policies, otherwise use default basic rates */
- if (control->control.sta)
- tx_attr |= ACX_TX_AP_FULL_RATE << TX_HW_ATTR_OFST_RATE_POLICY;
-
desc->tx_attr = cpu_to_le16(tx_attr);

- wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad);
+ wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d hlid: %d "
+ "tx_attr: 0x%x len: %d life: %d mem: %d", pad, (int)desc->hlid,
+ (int)desc->tx_attr, (int)desc->length, (int)desc->life_time,
+ (int)desc->total_mem_blocks);
}

/* caller must hold wl->mutex */
--
1.7.1


2010-12-23 06:48:24

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH v2 02/18] wl1271: AP mode - AP specific CMD_CONFIGURE sub-commands

On Wed, Dec 22, 2010 at 22:44, Luciano Coelho <[email protected]> wrote:
>
> On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> > Add AP max retries and rate policy configuration.
> > Rename STA rate policy configuration function.
> >
> > Signed-off-by: Arik Nemtsov <[email protected]>
> > ---
>
> [...]
>
> > diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h
> > index 9cbc3f4..d2a68bf 100644
> > --- a/drivers/net/wireless/wl12xx/acx.h
> > +++ b/drivers/net/wireless/wl12xx/acx.h
>
> [...]
>
> > + ? ? __le32 rate_policy_idx; /* The index of the rate policy */
>
> This comment is quite useless. ;)

Removed :)

>
> > @@ -1062,6 +1072,19 @@ struct wl1271_acx_fw_tsf_information {
> > ? ? ? u8 padding[3];
> > ?} __packed;
> >
> > +#define WL1271_AP_MAX_TX_RETRY ? 100
>
> Should this be part of the conf.h settings? Should it be made more
> easily configurable?

Actually this configures the FW for sending up an event once the
number of TX retries was exceeded. For now this event is masked out
and we just set to to a sane minimum (to prevent the FW from working
too hard sending these events).
Recently Johannes sent patches that allow this to be hooked up to
hostapd and actually disassociate the station -
http://www.spinics.net/lists/linux-wireless/msg59831.html

When this is implemented we should perhaps make this easily
configurable. For now conf.h sounds like a good place.

Regards,
Arik

2010-12-22 20:22:17

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 01/18] wl1271: Add AP related configuration to conf_drv_settings

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Rate class configuration has been split up for AP and STA modes.
> Template related configuration likewise separated.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> @@ -153,6 +153,45 @@ static struct conf_drv_settings default_conf = {
> .tx_op_limit = 1504,
> },
> },
> + .ap_rc_conf = {
> + [0] = {
> + .enabled_rates = 0x1EFF,
> + .short_retry_limit = 10,
> + .long_retry_limit = 10,
> + .aflags = 0,
> + },
> + [1] = {
> + .enabled_rates = 0x1EFF,
> + .short_retry_limit = 10,
> + .long_retry_limit = 10,
> + .aflags = 0,
> + },
> + [2] = {
> + .enabled_rates = 0x1EFF,
> + .short_retry_limit = 10,
> + .long_retry_limit = 10,
> + .aflags = 0,
> + },
> + [3] = {
> + .enabled_rates = 0x1EFF,
> + .short_retry_limit = 10,
> + .long_retry_limit = 10,
> + .aflags = 0,
> + },
> + },
> + .ap_mgmt_conf = {
> + .enabled_rates = 0x7,
> + .short_retry_limit = 10,
> + .long_retry_limit = 10,
> + .aflags = 0,
> + },
> + .ap_bcst_conf = {
> + .enabled_rates = 0x1,
> + .short_retry_limit = 10,
> + .long_retry_limit = 10,
> + .aflags = 0,
> + },
> +

All the enabled_rates values here look quite magic. Is there any way
these could be changed to macros or something more descriptive?

Also, how do you deal with the different basic rates used by 11bg and
11a?

--
Cheers,
Luca.


2010-12-22 14:28:33

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 14/18] wl1271: AP mode - record TX configuration settings

Record TX configuration settings in the "conf" member of our global
structure (struct wl1271) if conf_tx is called when the firmware is
not loaded.

Later on when the firmware is loaded, we apply the tx conf as part of
the init sequence.

Important for AP mode since conf_tx is called before add_interface
(where the firmware is initialized).

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 74 ++++++++++++++++++++++++------------
1 files changed, 50 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 3cc4f79..71a67f1 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1462,6 +1462,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
goto out;
}

+ is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
@@ -2260,42 +2262,66 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
{
struct wl1271 *wl = hw->priv;
u8 ps_scheme;
- int ret;
+ int ret = 0;

mutex_lock(&wl->mutex);

wl1271_debug(DEBUG_MAC80211, "mac80211 conf tx %d", queue);

- if (unlikely(wl->state == WL1271_STATE_OFF)) {
- ret = -EAGAIN;
- goto out;
- }
-
- ret = wl1271_ps_elp_wakeup(wl, false);
- if (ret < 0)
- goto out;
-
- /* the txop is confed in units of 32us by the mac80211, we need us */
- ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
- params->cw_min, params->cw_max,
- params->aifs, params->txop << 5);
- if (ret < 0)
- goto out_sleep;
-
if (params->uapsd)
ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
else
ps_scheme = CONF_PS_SCHEME_LEGACY;

- ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
- CONF_CHANNEL_TYPE_EDCF,
- wl1271_tx_get_queue(queue),
- ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
- if (ret < 0)
- goto out_sleep;
+ if (wl->state == WL1271_STATE_OFF) {
+ /*
+ * If the state is off, the parameters will be recorded and
+ * configured on init. This happens in AP-mode.
+ */
+ struct conf_tx_ac_category *conf_ac =
+ &wl->conf.tx.ac_conf[wl1271_tx_get_queue(queue)];
+ struct conf_tx_tid *conf_tid =
+ &wl->conf.tx.tid_conf[wl1271_tx_get_queue(queue)];
+
+ conf_ac->ac = wl1271_tx_get_queue(queue);
+ conf_ac->cw_min = (u8)params->cw_min;
+ conf_ac->cw_max = params->cw_max;
+ conf_ac->aifsn = params->aifs;
+ conf_ac->tx_op_limit = params->txop << 5;
+
+ conf_tid->queue_id = wl1271_tx_get_queue(queue);
+ conf_tid->channel_type = CONF_CHANNEL_TYPE_EDCF;
+ conf_tid->tsid = wl1271_tx_get_queue(queue);
+ conf_tid->ps_scheme = ps_scheme;
+ conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
+ conf_tid->apsd_conf[0] = 0;
+ conf_tid->apsd_conf[1] = 0;
+ } else {
+ ret = wl1271_ps_elp_wakeup(wl, false);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * the txop is confed in units of 32us by the mac80211,
+ * we need us
+ */
+ ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
+ params->cw_min, params->cw_max,
+ params->aifs, params->txop << 5);
+ if (ret < 0)
+ goto out_sleep;
+
+ ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
+ CONF_CHANNEL_TYPE_EDCF,
+ wl1271_tx_get_queue(queue),
+ ps_scheme, CONF_ACK_POLICY_LEGACY,
+ 0, 0);
+ if (ret < 0)
+ goto out_sleep;

out_sleep:
- wl1271_ps_elp_sleep(wl);
+ wl1271_ps_elp_sleep(wl);
+ }

out:
mutex_unlock(&wl->mutex);
--
1.7.1


2010-12-28 12:58:01

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 17/18] wl1271: Read MAC address from NVS file on HW startup

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Try to read the MAC address from the on-disk NVS file.
> A non-zero MAC address is required to add an AP interface.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
> index eb2c3c5..a9f95c6 100644
> --- a/drivers/net/wireless/wl12xx/main.c
> +++ b/drivers/net/wireless/wl12xx/main.c
> @@ -3025,6 +3025,18 @@ int wl1271_register_hw(struct wl1271 *wl)
> if (wl->mac80211_registered)
> return 0;
>
> + ret = wl1271_fetch_nvs(wl);
> + if (ret == 0) {
> + u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
> +
> + wl->mac_addr[0] = nvs_ptr[11];
> + wl->mac_addr[1] = nvs_ptr[10];
> + wl->mac_addr[2] = nvs_ptr[6];
> + wl->mac_addr[3] = nvs_ptr[5];
> + wl->mac_addr[4] = nvs_ptr[4];
> + wl->mac_addr[5] = nvs_ptr[3];
> + }
> +
> SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
>
> ret = ieee80211_register_hw(wl->hw);

I think we need to check whether the MAC address is valid in the NVS.
Also this goes a little bit against the way we are doing things in Nokia
devices. In our devices we don't have different files during
production, the only part that is different from one individual device
to another is in a protected area. The NVS file in the file system is
always the same. The one that changes is in the protected area and is
passed to the driver by a special application.

Also, how would this work if the hw is registered before udev is
running?

We need to revise the entire NVS thing. But at least for now, while we
don't come up with a good and final solution, we should not be inventing
new ways of setting the MAC.


--
Cheers,
Luca.


2010-12-22 21:19:41

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 05/18] wl1271: AP mode - workaround for FW bug on station remove

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Sometimes an event indicating station removal is not sent up by
> firmware. We work around this by always indicating success in when
> a wait for the event timeouts.
>
> Temporary workaround until a FW fix is introduced.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> @@ -1108,9 +1122,11 @@ int wl1271_cmd_remove_sta(struct wl1271 *wl, u8 hlid)
> goto out_free;
> }
>
> - ret = wl1271_cmd_wait_for_event(wl, STA_REMOVE_COMPLETE_EVENT_ID);
> - if (ret < 0)
> - wl1271_error("cmd remove sta event completion error");
> + /*
> + * We are ok with a timeout here. The event is sometimes not sent
> + * due to a firmware bug.
> + */
> + wl1271_cmd_wait_for_event_or_timeout(wl, STA_REMOVE_COMPLETE_EVENT_ID);

Why wait for the event at all then?


--
Cheers,
Luca.


2010-12-22 14:28:37

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 15/18] wl1271: AP mode - encryption support

Encryption key configuration is different for AP/STA modes.

AP encryption keys are recorded when the BSS is not started. On BSS
start they are propagated to the AP (in wl1271_ap_init_hwenc).

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/cmd.h | 1 -
drivers/net/wireless/wl12xx/main.c | 237 ++++++++++++++++++++++++++++------
drivers/net/wireless/wl12xx/tx.c | 13 +-
drivers/net/wireless/wl12xx/tx.h | 4 +
drivers/net/wireless/wl12xx/wl12xx.h | 16 +++
5 files changed, 227 insertions(+), 44 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index d51324d..4f2764f 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -291,7 +291,6 @@ struct wl1271_cmd_ps_params {

/* HW encryption keys */
#define NUM_ACCESS_CATEGORIES_COPY 4
-#define MAX_KEY_SIZE 32

enum wl1271_cmd_key_action {
KEY_ADD_OR_REPLACE = 1,
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index 71a67f1..5536079 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -296,6 +296,7 @@ static struct conf_drv_settings default_conf = {
};

static void __wl1271_op_remove_interface(struct wl1271 *wl);
+static void wl1271_free_ap_keys(struct wl1271 *wl);


static void wl1271_device_release(struct device *dev)
@@ -1193,6 +1194,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->flags = 0;
wl->vif = NULL;
wl->filters = 0;
+ wl1271_free_ap_keys(wl);
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));

for (i = 0; i < NUM_TX_QUEUES; i++)
@@ -1662,38 +1664,209 @@ out:
kfree(fp);
}

+int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id)
+{
+ int ret;
+ bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+
+ if (is_ap)
+ ret = wl1271_cmd_set_ap_default_wep_key(wl, id);
+ else
+ ret = wl1271_cmd_set_sta_default_wep_key(wl, id);
+
+ if (ret < 0)
+ return ret;
+
+ wl1271_debug(DEBUG_CRYPT, "default wep key idx: %d", (int)id);
+ return 0;
+}
+
+static int wl1271_record_ap_key(struct wl1271 *wl, u8 id, u8 key_type,
+ u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
+ u16 tx_seq_16)
+{
+ struct wl1271_ap_key *ap_key;
+ int i;
+
+ wl1271_debug(DEBUG_CRYPT, "record ap key id %d", (int)id);
+
+ if (key_size > MAX_KEY_SIZE)
+ return -EINVAL;
+
+ /*
+ * Find next free entry in ap_keys. Also check we are not replacing
+ * an existing key.
+ */
+ for (i = 0; i < MAX_NUM_KEYS; i++) {
+ if (wl->recorded_ap_keys[i] == NULL)
+ break;
+
+ if (wl->recorded_ap_keys[i]->id == id) {
+ wl1271_warning("trying to record key replacement");
+ return -EINVAL;
+ }
+ }
+
+ if (i == MAX_NUM_KEYS)
+ return -EBUSY;
+
+ ap_key = kzalloc(sizeof(*ap_key), GFP_KERNEL);
+ if (!ap_key)
+ return -ENOMEM;
+
+ ap_key->id = id;
+ ap_key->key_type = key_type;
+ ap_key->key_size = key_size;
+ memcpy(ap_key->key, key, key_size);
+ ap_key->hlid = hlid;
+ ap_key->tx_seq_32 = tx_seq_32;
+ ap_key->tx_seq_16 = tx_seq_16;
+
+ wl->recorded_ap_keys[i] = ap_key;
+ return 0;
+}
+
+static void wl1271_free_ap_keys(struct wl1271 *wl)
+{
+ int i;
+
+ for (i = 0; i < MAX_NUM_KEYS; i++) {
+ kfree(wl->recorded_ap_keys[i]);
+ wl->recorded_ap_keys[i] = NULL;
+ }
+}
+
+static int wl1271_ap_init_hwenc(struct wl1271 *wl)
+{
+ int i, ret = 0;
+ struct wl1271_ap_key *key;
+ bool wep_key_added = false;
+
+ for (i = 0; i < MAX_NUM_KEYS; i++) {
+ if (wl->recorded_ap_keys[i] == NULL)
+ break;
+
+ key = wl->recorded_ap_keys[i];
+ ret = wl1271_cmd_set_ap_key(wl, KEY_ADD_OR_REPLACE,
+ key->id, key->key_type,
+ key->key_size, key->key,
+ key->hlid, key->tx_seq_32,
+ key->tx_seq_16);
+ if (ret < 0)
+ goto out;
+
+ if (key->key_type == KEY_WEP)
+ wep_key_added = true;
+ }
+
+ if (wep_key_added) {
+ ret = wl1271_cmd_set_ap_default_wep_key(wl, wl->default_key);
+ if (ret < 0)
+ goto out;
+ }
+
+out:
+ wl1271_free_ap_keys(wl);
+ return ret;
+}
+
+static int wl1271_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
+ u8 key_size, const u8 *key, u32 tx_seq_32,
+ u16 tx_seq_16, struct ieee80211_sta *sta)
+{
+ int ret;
+ bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
+
+ if (is_ap) {
+ struct wl1271_station *wl_sta;
+ u8 hlid;
+
+ if (sta) {
+ wl_sta = (struct wl1271_station *)sta->drv_priv;
+ hlid = wl_sta->hlid;
+ } else {
+ hlid = WL1271_AP_BROADCAST_HLID;
+ }
+
+ if (!test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
+ /*
+ * We do not support removing keys after AP shutdown.
+ * Pretend we do to make mac80211 happy.
+ */
+ if (action != KEY_ADD_OR_REPLACE)
+ return 0;
+
+ ret = wl1271_record_ap_key(wl, id,
+ key_type, key_size,
+ key, hlid, tx_seq_32,
+ tx_seq_16);
+ } else {
+ ret = wl1271_cmd_set_ap_key(wl, action,
+ id, key_type, key_size,
+ key, hlid, tx_seq_32,
+ tx_seq_16);
+ }
+
+ if (ret < 0)
+ return ret;
+ } else {
+ const u8 *addr;
+ static const u8 bcast_addr[ETH_ALEN] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+ addr = sta ? sta->addr : bcast_addr;
+
+ if (is_zero_ether_addr(addr)) {
+ /* We dont support TX only encryption */
+ return -EOPNOTSUPP;
+ }
+
+ /* The wl1271 does not allow to remove unicast keys - they
+ will be cleared automatically on next CMD_JOIN. Ignore the
+ request silently, as we dont want the mac80211 to emit
+ an error message. */
+ if (action == KEY_REMOVE && !is_broadcast_ether_addr(addr))
+ return 0;
+
+ ret = wl1271_cmd_set_sta_key(wl, action,
+ id, key_type, key_size,
+ key, addr, tx_seq_32,
+ tx_seq_16);
+ if (ret < 0)
+ return ret;
+
+ /* the default WEP key needs to be configured at least once */
+ if (key_type == KEY_WEP) {
+ ret = wl1271_cmd_set_sta_default_wep_key(wl,
+ wl->default_key);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
struct wl1271 *wl = hw->priv;
- const u8 *addr;
int ret;
u32 tx_seq_32 = 0;
u16 tx_seq_16 = 0;
u8 key_type;

- static const u8 bcast_addr[ETH_ALEN] =
- { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-
wl1271_debug(DEBUG_MAC80211, "mac80211 set key");

- addr = sta ? sta->addr : bcast_addr;
-
- wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd);
- wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN);
+ wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x sta: %p", cmd, sta);
wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x",
key_conf->cipher, key_conf->keyidx,
key_conf->keylen, key_conf->flags);
wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);

- if (is_zero_ether_addr(addr)) {
- /* We dont support TX only encryption */
- ret = -EOPNOTSUPP;
- goto out;
- }
-
mutex_lock(&wl->mutex);

if (unlikely(wl->state == WL1271_STATE_OFF)) {
@@ -1740,36 +1913,21 @@ static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,

switch (cmd) {
case SET_KEY:
- ret = wl1271_cmd_set_sta_key(wl, KEY_ADD_OR_REPLACE,
- key_conf->keyidx, key_type,
- key_conf->keylen, key_conf->key,
- addr, tx_seq_32, tx_seq_16);
+ ret = wl1271_set_key(wl, KEY_ADD_OR_REPLACE,
+ key_conf->keyidx, key_type,
+ key_conf->keylen, key_conf->key,
+ tx_seq_32, tx_seq_16, sta);
if (ret < 0) {
wl1271_error("Could not add or replace key");
goto out_sleep;
}
-
- /* the default WEP key needs to be configured at least once */
- if (key_type == KEY_WEP) {
- ret = wl1271_cmd_set_sta_default_wep_key(wl,
- wl->default_key);
- if (ret < 0)
- goto out_sleep;
- }
break;

case DISABLE_KEY:
- /* The wl1271 does not allow to remove unicast keys - they
- will be cleared automatically on next CMD_JOIN. Ignore the
- request silently, as we dont want the mac80211 to emit
- an error message. */
- if (!is_broadcast_ether_addr(addr))
- break;
-
- ret = wl1271_cmd_set_sta_key(wl, KEY_REMOVE,
- key_conf->keyidx, key_type,
- key_conf->keylen, key_conf->key,
- addr, 0, 0);
+ ret = wl1271_set_key(wl, KEY_REMOVE,
+ key_conf->keyidx, key_type,
+ key_conf->keylen, key_conf->key,
+ 0, 0, sta);
if (ret < 0) {
wl1271_error("Could not remove key");
goto out_sleep;
@@ -1788,7 +1946,6 @@ out_sleep:
out_unlock:
mutex_unlock(&wl->mutex);

-out:
return ret;
}

@@ -2015,6 +2172,10 @@ end_changed_bcn:

set_bit(WL1271_FLAG_AP_STARTED, &wl->flags);
wl1271_debug(DEBUG_AP, "started AP");
+
+ ret = wl1271_ap_init_hwenc(wl);
+ if (ret < 0)
+ goto out_sleep;
}
} else {
if (test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) {
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 70a7bd4..830f940 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -190,7 +190,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
struct ieee80211_tx_info *info;
u32 extra = 0;
int ret = 0;
- u8 idx;
u32 total_len;

if (!skb)
@@ -203,11 +202,15 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb,
extra = WL1271_TKIP_IV_SPACE;

if (info->control.hw_key) {
- idx = info->control.hw_key->hw_key_idx;
+ bool is_wep;
+ u8 idx = info->control.hw_key->hw_key_idx;
+ u32 cipher = info->control.hw_key->cipher;

- /* FIXME: do we have to do this if we're not using WEP? */
- if (unlikely(wl->default_key != idx)) {
- ret = wl1271_cmd_set_sta_default_wep_key(wl, idx);
+ is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) ||
+ (cipher == WLAN_CIPHER_SUITE_WEP104);
+
+ if (unlikely(is_wep && wl->default_key != idx)) {
+ ret = wl1271_set_default_wep_key(wl, idx);
if (ret < 0)
return ret;
wl->default_key = idx;
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
index 0662b19..81ba75d 100644
--- a/drivers/net/wireless/wl12xx/tx.h
+++ b/drivers/net/wireless/wl12xx/tx.h
@@ -152,4 +152,8 @@ void wl1271_tx_flush(struct wl1271 *wl);
u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set);

+/* Functions from wl1271_main.c */
+
+int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id);
+
#endif
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index 3bd03af..98532fc 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -253,6 +253,19 @@ struct wl1271_if_operations {
void (*disable_irq)(struct wl1271 *wl);
};

+#define MAX_NUM_KEYS 14
+#define MAX_KEY_SIZE 32
+
+struct wl1271_ap_key {
+ u8 id;
+ u8 key_type;
+ u8 key_size;
+ u8 key[MAX_KEY_SIZE];
+ u8 hlid;
+ u32 tx_seq_32;
+ u16 tx_seq_16;
+};
+
struct wl1271 {
struct platform_device *plat_dev;
struct ieee80211_hw *hw;
@@ -438,6 +451,9 @@ struct wl1271 {

/* map for HLIDs of associated stations - when operating in AP mode */
unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)];
+
+ /* recoreded keys for AP-mode - set here before AP startup */
+ struct wl1271_ap_key *recorded_ap_keys[MAX_NUM_KEYS];
};

struct wl1271_station {
--
1.7.1


2010-12-22 14:28:28

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH v2 12/18] wl1271: AP mode - add STA add/remove ops

Allocate and free host link IDs (HLIDs) for each link. A per-STA
data structure keeps the HLID of each STA.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/wl12xx/main.c | 108 ++++++++++++++++++++++++++++++++++
drivers/net/wireless/wl12xx/wl12xx.h | 7 ++
2 files changed, 115 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index f777073..13a1c6e 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1192,6 +1192,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl)
wl->flags = 0;
wl->vif = NULL;
wl->filters = 0;
+ memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));

for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_blocks_freed[i] = 0;
@@ -2347,6 +2348,109 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx,
return 0;
}

+static int wl1271_allocate_hlid(struct wl1271 *wl,
+ struct ieee80211_sta *sta,
+ u8 *hlid)
+{
+ struct wl1271_station *wl_sta;
+ int id;
+
+ id = find_first_zero_bit(wl->ap_hlid_map, AP_MAX_STATIONS);
+ if (id >= AP_MAX_STATIONS) {
+ wl1271_warning("could not allocate HLID - too much stations");
+ return -EBUSY;
+ }
+
+ wl_sta = (struct wl1271_station *)sta->drv_priv;
+
+ __set_bit(id, wl->ap_hlid_map);
+ wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
+ *hlid = wl_sta->hlid;
+ return 0;
+}
+
+static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid)
+{
+ int id = hlid - WL1271_AP_STA_HLID_START;
+
+ __clear_bit(id, wl->ap_hlid_map);
+}
+
+static int wl1271_op_sta_add(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct wl1271 *wl = hw->priv;
+ int ret = 0;
+ u8 hlid;
+
+ mutex_lock(&wl->mutex);
+
+ if (unlikely(wl->state == WL1271_STATE_OFF))
+ goto out;
+
+ if (wl->bss_type != BSS_TYPE_AP_BSS)
+ goto out;
+
+ wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid);
+
+ ret = wl1271_allocate_hlid(wl, sta, &hlid);
+ if (ret < 0)
+ goto out;
+
+ ret = wl1271_ps_elp_wakeup(wl, false);
+ if (ret < 0)
+ goto out;
+
+ ret = wl1271_cmd_add_sta(wl, sta, hlid);
+ if (ret < 0)
+ goto out_sleep;
+
+out_sleep:
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+ return ret;
+}
+
+static int wl1271_op_sta_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct wl1271 *wl = hw->priv;
+ struct wl1271_station *wl_sta;
+ int ret = 0;
+
+ mutex_lock(&wl->mutex);
+
+ if (unlikely(wl->state == WL1271_STATE_OFF))
+ goto out;
+
+ if (wl->bss_type != BSS_TYPE_AP_BSS)
+ goto out;
+
+ wl1271_debug(DEBUG_MAC80211, "mac80211 remove sta %d", (int)sta->aid);
+
+ ret = wl1271_ps_elp_wakeup(wl, false);
+ if (ret < 0)
+ goto out;
+
+ wl_sta = (struct wl1271_station *)sta->drv_priv;
+ ret = wl1271_cmd_remove_sta(wl, wl_sta->hlid);
+ if (ret < 0)
+ goto out_sleep;
+
+ wl1271_free_hlid(wl, wl_sta->hlid);
+
+out_sleep:
+ wl1271_ps_elp_sleep(wl);
+
+out:
+ mutex_unlock(&wl->mutex);
+ return ret;
+}
+
/* can't be const, mac80211 writes to this */
static struct ieee80211_rate wl1271_rates[] = {
{ .bitrate = 10,
@@ -2593,6 +2697,8 @@ static const struct ieee80211_ops wl1271_ops = {
.conf_tx = wl1271_op_conf_tx,
.get_tsf = wl1271_op_get_tsf,
.get_survey = wl1271_op_get_survey,
+ .sta_add = wl1271_op_sta_add,
+ .sta_remove = wl1271_op_sta_remove,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
};

@@ -2786,6 +2892,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)

SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));

+ wl->hw->sta_data_size = sizeof(struct wl1271_station);
+
return 0;
}
EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index a378d52..3bd03af 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -435,6 +435,13 @@ struct wl1271 {

/* Most recently reported noise in dBm */
s8 noise;
+
+ /* map for HLIDs of associated stations - when operating in AP mode */
+ unsigned long ap_hlid_map[BITS_TO_LONGS(AP_MAX_STATIONS)];
+};
+
+struct wl1271_station {
+ u8 hlid;
};

int wl1271_plt_start(struct wl1271 *wl);
--
1.7.1


2010-12-27 12:57:23

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 06/18] wl1271: AP mode - init sequence

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Split HW init sequence into AP/STA specific parts
>
> The AP specific init sequence includes configuration of templates, rate
> classes, power mode, etc. Also unmask AP specific events in the event mbox.
>
> Separate the differences between AP and STA init into mode
> specific functions called from wl1271_hw_init. The first is called after
> radio configuration and the second after memory configuration.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
> index 02f843c..d51324d 100644
> --- a/drivers/net/wireless/wl12xx/cmd.h
> +++ b/drivers/net/wireless/wl12xx/cmd.h
> @@ -140,6 +140,13 @@ enum cmd_templ {
> * For CTS-to-self (FastCTS) mechanism
> * for BT/WLAN coexistence (SoftGemini). */
> CMD_TEMPL_ARP_RSP,
> +
> + /* AP-mode specific */
> + CMD_TEMPL_AP_BEACON = 13,
> + CMD_TEMPL_AP_PROBE_RESPONSE,
> + CMD_TEMPL_AP_ARP_RSP,
> + CMD_TEMPL_DEAUTH_AP,
> +
> CMD_TEMPL_MAX = 0xff
> };

Cool, I see that you fixed the issue we had before. Just one question,
is the CMD_TEMPL_AP_ARP_RSP the same as the original CMD_TEMPL_ARP_RSP?
I mean is it only the order in the enum that is different between the AP
and the STA firmwares? If that's the case, we should *really* try to get
this fixed in the AP firmware.

It's still new enough and not so many people are using it yet, right? So
it's the time to fix it if it makes sense.


> diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
> index 799c369..df6666b 100644
> --- a/drivers/net/wireless/wl12xx/init.c
> +++ b/drivers/net/wireless/wl12xx/init.c

[...]

> @@ -145,10 +253,6 @@ int wl1271_init_phy_config(struct wl1271 *wl)
> if (ret < 0)
> return ret;
>
> - ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
> - if (ret < 0)
> - return ret;
> -

Here we're changing the order of these initialization ACXs, but it
shouldn't be a problem. If it is a problem, it's a firmware bug that
(hopefully) can be fixed.


> @@ -213,11 +317,150 @@ static int wl1271_init_beacon_broadcast(struct wl1271 *wl)
> return 0;
> }
>
> +static int wl1271_sta_hw_init(struct wl1271 *wl)
> +{
> + int ret;
> +
> + ret = wl1271_cmd_ext_radio_parms(wl);
> + if (ret < 0)
> + return ret;
> +
> + ret = wl1271_sta_init_templates_config(wl);
> + if (ret < 0)
> + return ret;
> +
> + ret = wl1271_acx_group_address_tbl(wl, true, NULL, 0);
> + if (ret < 0)
> + return ret;
> +
> + /* Initialize connection monitoring thresholds */
> + ret = wl1271_acx_conn_monit_params(wl, false);
> + if (ret < 0)
> + return ret;
> +
> + /* Beacon filtering */
> + ret = wl1271_init_beacon_filter(wl);
> + if (ret < 0)
> + return ret;
> +
> + /* Bluetooth WLAN coexistence */
> + ret = wl1271_init_pta(wl);
> + if (ret < 0)
> + return ret;
> +
> + /* Beacons and boradcast settings */

You copied the typo, maybe you could take the opportunity to fix it?

> + ret = wl1271_init_beacon_broadcast(wl);
> + if (ret < 0)
> + return ret;
> +
> + /* Configure for ELP power saving */
> + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP);
> + if (ret < 0)
> + return ret;
> +
> + /* Configure rssi/snr averaging weights */
> + ret = wl1271_acx_rssi_snr_avg_weights(wl);
> + if (ret < 0)
> + return ret;
> +
> + ret = wl1271_acx_sta_rate_policies(wl);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}

Also here, this changes the initialization order a bit. I think this
was Juuso's concern earlier. At least while we were having debug camps,
the TI people supporting us were always asking us to be extremely
cautious with the order of the initialization. They were saying that it
was really important to do the initialization in the same way as the
tiwlan driver does. Hopefully this is not an issue now...


> @@ -279,21 +512,11 @@ int wl1271_hw_init(struct wl1271 *wl)
> if (ret < 0)
> goto out_free_memmap;
>
> - /* Bluetooth WLAN coexistence */
> - ret = wl1271_init_pta(wl);
> - if (ret < 0)
> - goto out_free_memmap;
> -

Just out of curiosity... Is AP coexistence with Bluetooth possible? This
could be important in some scenarios.


> diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/init.h
> index 7762421..4d37210 100644
> --- a/drivers/net/wireless/wl12xx/init.h
> +++ b/drivers/net/wireless/wl12xx/init.h
> @@ -27,10 +27,13 @@
> #include "wl12xx.h"
>
> int wl1271_hw_init_power_auth(struct wl1271 *wl);
> -int wl1271_init_templates_config(struct wl1271 *wl);
> +int wl1271_sta_init_templates_config(struct wl1271 *wl);
> int wl1271_init_phy_config(struct wl1271 *wl);
> int wl1271_init_pta(struct wl1271 *wl);
> int wl1271_init_energy_detection(struct wl1271 *wl);
> int wl1271_hw_init(struct wl1271 *wl);
>
> +/* Functions from wl1271_main.c */
> +u32 wl1271_min_rate_get(struct wl1271 *wl);
> +

This is bad and shouldn't be here. Need to find a better place for it.


> diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h
> index be21032..b230e72 100644
> --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h
> +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h
> @@ -160,4 +160,9 @@ struct wl12xx_probe_resp_template {
> struct wl12xx_ie_country country;
> } __packed;
>
> +struct wl12xx_disconn_template {
> + struct ieee80211_header header;
> + __le16 disconn_reason;
> +} __packed;
> +

Isn't padding necessary here to keep the struct 32-bit aligned?

--
Cheers,
Luca.


2010-12-22 21:36:24

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 08/18] wl1271: Add AP related definitions to HOST-FW interface

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Change structures in a non-destructive manner. This means no
> changes in size or location of existing members used by STA.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h
> index 3abb26f..19fd591 100644
> --- a/drivers/net/wireless/wl12xx/rx.h
> +++ b/drivers/net/wireless/wl12xx/rx.h
> @@ -86,10 +86,11 @@
> /*
> * RX Descriptor status
> *
> - * Bits 0-2 - status
> - * Bits 3-7 - reserved
> + * Bits 0-2 - error code
> + * Bits 3-5 - process_id tag (AP mode FW)
> + * Bits 6-7 - reserved
> */
> -#define WL1271_RX_DESC_STATUS_MASK 0x07
> +#define WL1271_RX_DESC_STATUS_MASK 0x03

What happened with bit 2?


--
Cheers,
Luca.


2010-12-28 11:35:43

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 14/18] wl1271: AP mode - record TX configuration settings

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Record TX configuration settings in the "conf" member of our global
> structure (struct wl1271) if conf_tx is called when the firmware is
> not loaded.
>
> Later on when the firmware is loaded, we apply the tx conf as part of
> the init sequence.
>
> Important for AP mode since conf_tx is called before add_interface
> (where the firmware is initialized).
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
> index 3cc4f79..71a67f1 100644
> --- a/drivers/net/wireless/wl12xx/main.c
> +++ b/drivers/net/wireless/wl12xx/main.c
> @@ -1462,6 +1462,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
> goto out;
> }
>
> + is_ap = (wl->bss_type == BSS_TYPE_AP_BSS);
> +
> ret = wl1271_ps_elp_wakeup(wl, false);
> if (ret < 0)
> goto out;

This is cross-patch and should be in 10/18.


--
Cheers,
Luca.


2010-12-22 21:16:56

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH v2 04/18] wl1271: AP-mode high level commands

On Wed, 2010-12-22 at 16:27 +0200, ext Arik Nemtsov wrote:
> Add commands to start/stop BSS, add/remove STA and configure encryption
> keys. Split the encryption commands "set key" and "set default key" into
> AP and STA specific versions.
>
> Signed-off-by: Arik Nemtsov <[email protected]>
> ---

[...]

> @@ -850,3 +943,178 @@ out_free:
> out:
> return ret;
> }
> +
> +int wl1271_cmd_start_bss(struct wl1271 *wl)
> +{
> + struct wl1271_cmd_bss_start *cmd;
> + struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf;
> + int ret;
> +
> + wl1271_debug(DEBUG_CMD, "cmd start bss");
> +
> + /*
> + * FIXME: We currently do not support hidden SSID. The real SSID
> + * should be fetched from mac80211 first.
> + */
> + if (wl->ssid_len == 0) {
> + wl1271_warning("Hidden SSID currently not supported for AP");
> + return -EINVAL;
> + }
> +
> + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
> + if (!cmd) {
> + ret = -ENOMEM;
> + goto out;
> + }

Please decide if you want to return directly here or set ret and goto
out. For consistency, I'd suggest to set ret and goto out, since this
is how (most of) the rest of the code does.


> +int wl1271_cmd_add_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
> +{
> + struct wl1271_cmd_add_sta *cmd;
> + int ret;
> +
> + wl1271_debug(DEBUG_CMD, "cmd add sta %d", (int)hlid);
> +
> + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
> + if (!cmd) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + /* currently we don't support UAPSD */
> + memset(cmd->psd_type, 0, sizeof(cmd->psd_type));
> + cmd->sp_len = 0;

No need to memset, since you're kzalloc'ing anyway. I guess just a good
comment here is enough.


> diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
> index 2a1d9db..02f843c 100644
> --- a/drivers/net/wireless/wl12xx/cmd.h
> +++ b/drivers/net/wireless/wl12xx/cmd.h
> @@ -54,12 +54,20 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
> int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr);
> int wl1271_build_qos_null_data(struct wl1271 *wl);
> int wl1271_cmd_build_klv_null_data(struct wl1271 *wl);
> -int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
> -int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
> +int wl1271_cmd_set_sta_default_wep_key(struct wl1271 *wl, u8 id);
> +int wl1271_cmd_set_ap_default_wep_key(struct wl1271 *wl, u8 id);
> +int wl1271_cmd_set_sta_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
> u8 key_size, const u8 *key, const u8 *addr,
> u32 tx_seq_32, u16 tx_seq_16);

Indentation?

> +int wl1271_cmd_set_ap_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
> + u8 key_size, const u8 *key, u8 hlid, u32 tx_seq_32,
> + u16 tx_seq_16);

Ditto.


--
Cheers,
Luca.