2010-11-18 11:32:35

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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.

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 | 141 +++++-
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 | 747 ++++++++++++++++++++++------
drivers/net/wireless/wl12xx/rx.h | 12 +-
drivers/net/wireless/wl12xx/tx.c | 69 ++-
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, 1577 insertions(+), 278 deletions(-)



2010-11-18 11:32:41

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 1eafb81..a2b496b 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-11-18 11:32:56

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 30d7a77..ee866d2 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1591,7 +1591,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);

@@ -1605,15 +1606,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-11-18 11:37:11

by Luciano Coelho

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

Hi Arik,


On Thu, 2010-11-18 at 13:32 +0200, ext Arik Nemtsov wrote:
> These patches add access point mode support to the wl12xx driver.
> This mode uses a separate firmware and has a different initialization
> sequence.

This is cool! It will take me some time to review it though... let's
see.

Any chance I could get this firmware so that I can at least try the code
out?

--
Cheers,
Luca.


2010-11-18 11:32:54

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 49def62..30d7a77 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1371,7 +1371,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;

@@ -1412,14 +1412,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
@@ -1448,31 +1451,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-11-29 06:50:39

by Arik Nemtsov

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

Hey Luca,

>
> Unfortunately I didn't have much time to look into your AP patches yet.
> We need to review this very carefully. ?It touches so many parts of the
> code that I'm a bit worried that this would break normal STA
> functionality.

Most of the patches add AP related functionality without touching the
flow in STA mode.
In fact the only patch that touches STA is "[PATCH 06/18] wl1271: AP
mode - init sequence". The init sequence has been modified to unify
the STA and AP common parts and separate the different parts into
different functions.
For STA mode this means a slight change in the order of initialization.

Other changes are basically:
1. STA function renames
2. removing STA functionality when operating in AP mode by protecting
code segments with "if (!is_ap)"
3. adding AP mode only functionality. This includes code segments with
"if (is_ap)" in common STA/AP code.

>
> How well have you tested these patches in STA mode? Do you think there
> would be any possibility of making this Kconfigurable so that there
> would be no (or very little) binary difference in the code if AP is not
> enabled?

We have performed extensive stress tests on the init sequence part
(constant on/off for several days). Basic throughput and stress tests
were performed for STA mode operation (as well as AP mode operation).
A third kind of test was a stress test of the switch between STA and AP modes.

We feel making the AP mode Kconfigurable will make the code uglier and
hurt maintainability. We were hoping we could avoid it by structuring
the code as it is.

Regards,
Arik

2010-11-18 11:32:45

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 7df3012..282e476 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;
@@ -1039,9 +1053,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-11-18 11:33:07

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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.

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

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index bf13569..708b699 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -657,9 +657,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);
@@ -673,6 +690,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);

@@ -683,7 +701,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:
@@ -819,7 +837,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;
@@ -1006,6 +1025,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;
@@ -3098,6 +3120,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 8ba2812..aeb8673 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -123,6 +123,8 @@ enum {


#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))
@@ -412,6 +414,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-11-18 11:32:37

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 7cbaeb6..4f1cb84 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 f3d0541..40f15c1 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 31f0e2f..1bb2575 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-11-18 11:33:03

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 7e7b392..91bbb72 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1442,6 +1442,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;
@@ -2216,42 +2218,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-11-18 11:33:09

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 708b699..e5fbbb6 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -2987,6 +2987,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-11-18 11:32:39

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 4f1cb84..1a9ef46 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 75a6306..f8b1d97 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;
@@ -1057,6 +1067,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,
@@ -1114,6 +1137,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,
@@ -1155,7 +1179,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,
@@ -1181,5 +1207,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 7949d34..52d1aa2 100644
--- a/drivers/net/wireless/wl12xx/init.c
+++ b/drivers/net/wireless/wl12xx/init.c
@@ -317,7 +317,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 1bb2575..6ada706 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1373,7 +1373,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(
@@ -1448,7 +1448,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);
@@ -1993,7 +1993,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;

@@ -2043,7 +2043,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 d332b3f..67888f3 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -267,7 +267,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 = skb_dequeue(&wl->tx_queue))) {
--
1.7.1


2010-11-30 16:04:41

by Arik Nemtsov

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

>
> The code flow of the add if (wl1271_op_add_interface) overwrites it from
> vif.

Thats the point of the patch. The MAC can be overwritten at any point
between HW initialization and add_interface(). Either by a TESTMODE
cmd or even by "ifconfig wlan0 hw ether xx:xx:xx:xx:xx:xx". The patch
makes sure the MAC address is set (the default value is in the NVS)
when the hardware is initialized.

This allows the creation of monitor interfaces for instance. These are
created without calling the driver's add_interface() and require a
valid MAC address to be set. Such a monitor interface is added by
hostapd when creating an AP (which explains why this patch is included
in the AP-support patch-set).

When this patch is applied one should be careful to not set the mac
twice with different MAC addresses, as this might cause problems for
the upper layers.

Regards,
Arik

2010-11-18 11:32:47

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 | 5 +
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, 294 insertions(+), 77 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index 47366ff..e8e0610 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -136,6 +136,11 @@ enum cmd_templ {
CMD_TEMPL_CTS, /*
* For CTS-to-self (FastCTS) mechanism
* for BT/WLAN coexistence (SoftGemini). */
+ CMD_TEMPL_AP_BEACON,
+ CMD_TEMPL_AP_PROBE_RESPONSE,
+ CMD_TEMPL_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 a4196f5..d543464 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;
size_t size;
@@ -113,6 +95,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;
@@ -140,10 +248,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;
@@ -208,11 +312,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)
@@ -222,12 +465,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;

@@ -254,16 +497,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)
@@ -274,21 +507,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)
@@ -316,23 +539,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;

@@ -341,21 +554,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 0d507f5..1c6f399 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -433,7 +433,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;

@@ -1343,7 +1343,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 1846280..5683c71 100644
--- a/drivers/net/wireless/wl12xx/wl12xx_80211.h
+++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h
@@ -153,4 +153,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-11-30 19:01:29

by Kahn, Gery

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

On 30.Nov.10 18:04, Arik Nemtsov wrote:
>> The code flow of the add if (wl1271_op_add_interface) overwrites it from
>> vif.
> Thats the point of the patch. The MAC can be overwritten at any point
> between HW initialization and add_interface(). Either by a TESTMODE
> cmd or even by "ifconfig wlan0 hw ether xx:xx:xx:xx:xx:xx". The patch
> makes sure the MAC address is set (the default value is in the NVS)
> when the hardware is initialized.
>
> This allows the creation of monitor interfaces for instance. These are
> created without calling the driver's add_interface() and require a
> valid MAC address to be set. Such a monitor interface is added by
> hostapd when creating an AP (which explains why this patch is included
> in the AP-support patch-set).
>
> When this patch is applied one should be careful to not set the mac
> twice with different MAC addresses, as this might cause problems for
> the upper layers.
>
> Regards,
> Arik
The idea is to eliminate the situation where anyone "should be careful
to not set the
MAC twice".
If there is MAC from NVS, make it system wide, w/o need to care about
it. On the other hand,
if MAC set by `ifconfig' same story, make it system wide.

Best regards,
Gery

2010-11-18 11:32:49

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 a2b496b..f9e0c41 100644
--- a/drivers/net/wireless/wl12xx/boot.c
+++ b/drivers/net/wireless/wl12xx/boot.c
@@ -584,8 +584,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 c7d7719..7ac6c4a 100644
--- a/drivers/net/wireless/wl12xx/boot.h
+++ b/drivers/net/wireless/wl12xx/boot.h
@@ -68,4 +68,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 1c6f399..e6682f2 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1205,10 +1205,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;
@@ -2727,8 +2737,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 69720c0..cbad378 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -102,15 +102,24 @@ enum {
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-11-18 11:33:01

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 | 54 ++++++++++++++++++++++++++++++------
2 files changed, 47 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index c901051..7e7b392 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -941,7 +941,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 74a0c07..485171b 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;
@@ -126,7 +131,40 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
desc->tid = 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) */
@@ -137,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-11-18 11:33:11

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 e5fbbb6..4dbca5e 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1067,6 +1067,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;
@@ -3057,7 +3060,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;
wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz;
wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
--
1.7.1


2010-11-18 11:42:56

by Arik Nemtsov

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

Hey Luca,

>
> Any chance I could get this firmware so that I can at least try the code
> out?
>

Sure i'll send it to you privately. We'll have to wait a bit for the
official FW from TI.

If anyone else needs the AP-mode FW for testing, you're welcome to email me.

Regards,
Arik

2010-11-18 11:32:51

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 cbad378..ee357c4 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -296,6 +296,11 @@ struct wl1271_debugfs {
#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;
@@ -306,7 +311,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-11-18 11:33:05

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 e8e0610..ed999b7 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -285,7 +285,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 91bbb72..bf13569 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)
@@ -1179,6 +1180,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++)
@@ -1642,38 +1644,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)) {
@@ -1720,36 +1893,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;
@@ -1768,7 +1926,6 @@ out_sleep:
out_unlock:
mutex_unlock(&wl->mutex);

-out:
return ret;
}

@@ -1995,6 +2152,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 485171b..12e65c7 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 c8a0f1d..8ba2812 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -354,6 +354,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;
@@ -536,6 +549,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-11-18 11:32:52

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 | 126 ++++++++++++++++++++++------------
drivers/net/wireless/wl12xx/wl12xx.h | 3 +
2 files changed, 85 insertions(+), 44 deletions(-)

diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index e6682f2..49def62 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1852,7 +1852,7 @@ out:
return ret;
}

-static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
+static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
{
u8 *ptr = beacon->data +
offsetof(struct ieee80211_mgmt, u.beacon.variable);
@@ -1862,11 +1862,13 @@ static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon)
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];
}
- wl1271_error("ad-hoc beacon template has no SSID!\n");
+
+ wl1271_error("beacon template has no SSID!");
+ return -ENOENT;
}

static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
@@ -1878,70 +1880,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;
struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+ u16 tmpl_id;

- wl1271_debug(DEBUG_ADHOC, "ad-hoc beacon updated");
+ if (!beacon)
+ goto end_changed_bcn;

- if (beacon) {
- struct ieee80211_hdr *hdr;
+ wl1271_debug(DEBUG_MASTER, "beacon updated");

- wl1271_ssid_set(wl, beacon);
- 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;
- }
-
- hdr = (struct ieee80211_hdr *) beacon->data;
- hdr->frame_control = cpu_to_le16(
- IEEE80211_FTYPE_MGMT |
- IEEE80211_STYPE_PROBE_RESP);
+ ret = wl1271_ssid_set(wl, beacon);
+ if (ret < 0) {
+ dev_kfree_skb(beacon);
+ goto out_sleep;
+ }

- ret = wl1271_cmd_template_set(wl,
- CMD_TEMPL_PROBE_RESPONSE,
- beacon->data,
- beacon->len, 0,
- wl1271_min_rate_get(wl));
+ 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);
- if (ret < 0)
- goto out_sleep;
+ 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");

@@ -1952,7 +1968,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;
@@ -1960,11 +1998,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
@@ -1987,7 +2025,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;
wl->aid = bss_conf->aid;
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index ee357c4..cc654dd 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,
};

@@ -385,6 +387,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-11-30 19:36:26

by Kahn, Gery

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

On 30.Nov.10 21:20, Arik Nemtsov wrote:
>> The idea is to eliminate the situation where anyone "should be careful to
>> not set the
>> MAC twice".
>> If there is MAC from NVS, make it system wide, w/o need to care about it. On
>> the other hand,
>> if MAC set by `ifconfig' same story, make it system wide.
> No one has to set the MAC address twice. This patch degrades
> gracefully - if there is no NVS file on disk, no harm done.
> This means a solution that only uses ifconfig or TESTMODE commands
> before adding an interface will also work.
>
> This patch only gives one the option to set the MAC address right when
> the HW is initialized, without resorting to scripts or usermode
> daemons. One has to simply put the NVS file on the filesystem and the
> MAC will be read from there. I found this easier from an engineering
> perspective.
>
> Note that even without this patch its possible to set the MAC address
> twice - use ifconfig, add a monitor interface and then use ifconfig
> again to change the MAC.
>
Fully agree. Just set vif where u set wl and the solution would be
synced for your flow.
> Regards,
> Arik


2010-11-30 15:49:01

by Kahn, Gery

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

On 11/18/2010 01:32 PM, 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]>
> ---
> 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 708b699..e5fbbb6 100644
> --- a/drivers/net/wireless/wl12xx/main.c
> +++ b/drivers/net/wireless/wl12xx/main.c
> @@ -2987,6 +2987,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);
The code flow of the add if (wl1271_op_add_interface) overwrites it from vif.
There are 3 places for MAC: wl, vif and NVS :)
Should be considered also PLT flow which is not goes
through wl1271_op_add_interface, but from wl1271_tm_cmd().

2010-11-18 11:32:58

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 ee866d2..c901051 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1178,6 +1178,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;
@@ -2303,6 +2304,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,
@@ -2557,6 +2661,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)
};

@@ -2741,6 +2847,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 cc654dd..c8a0f1d 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -533,6 +533,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-11-18 11:32:43

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 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 40f15c1..7df3012 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

@@ -633,9 +634,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);
@@ -662,11 +663,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);
@@ -719,6 +751,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;
@@ -781,3 +874,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 16d1bf8..47366ff 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -51,12 +51,20 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl,
const u8 *ie, size_t ie_len, u8 band);
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*/
@@ -95,6 +103,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,
};
@@ -285,7 +299,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 */
@@ -314,6 +328,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];
@@ -408,4 +473,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 52d1aa2..a4196f5 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 6ada706..0d507f5 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -1692,7 +1692,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);
@@ -1703,7 +1703,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;
@@ -1718,7 +1718,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 67888f3..74a0c07 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -171,7 +171,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 3c836e6..69720c0 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -128,6 +128,14 @@ enum {
#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-11-30 19:21:06

by Arik Nemtsov

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

> The idea is to eliminate the situation where anyone "should be careful to
> not set the
> MAC twice".
> If there is MAC from NVS, make it system wide, w/o need to care about it. On
> the other hand,
> if MAC set by `ifconfig' same story, make it system wide.

No one has to set the MAC address twice. This patch degrades
gracefully - if there is no NVS file on disk, no harm done.
This means a solution that only uses ifconfig or TESTMODE commands
before adding an interface will also work.

This patch only gives one the option to set the MAC address right when
the HW is initialized, without resorting to scripts or usermode
daemons. One has to simply put the NVS file on the filesystem and the
MAC will be read from there. I found this easier from an engineering
perspective.

Note that even without this patch its possible to set the MAC address
twice - use ifconfig, add a monitor interface and then use ifconfig
again to change the MAC.

Regards,
Arik

2010-11-26 08:14:57

by Luciano Coelho

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

Hi Arik,

On Thu, 2010-11-18 at 13:32 +0200, ext Arik Nemtsov wrote:
> 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.

Unfortunately I didn't have much time to look into your AP patches yet.
We need to review this very carefully. It touches so many parts of the
code that I'm a bit worried that this would break normal STA
functionality.

How well have you tested these patches in STA mode? Do you think there
would be any possibility of making this Kconfigurable so that there
would be no (or very little) binary difference in the code if AP is not
enabled?

--
Cheers,
Luca.


2010-11-30 21:23:49

by Arik Nemtsov

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

> Fully agree. Just set vif where u set wl and the solution would be synced
> for your flow.

There is no way to set the vif addr from wl1271_register_hw() since no
vif has been created yet. The hardware was only just initialized..

The vif addr is set from the perm address before add_interface is
called (see ieee80211_if_add()). This means the last to write the MAC
address effectively sets the vif addr.

Regards,
Arik