aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJohn W. Linville <linville@tuxdriver.com>2011-09-19 15:42:21 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-09-19 15:42:21 -0400
commit376cf5d3fd3c84855f1bf8d62aa05b7e4bcf1b8c (patch)
tree5e34c6012ed5c2f1ef1dae2e34858be23f46773c /drivers
parent12e62d6f7ec475e546b40bece2045da15d6c21ef (diff)
parent045c745f8ccdb584ccc97f068c7c10c1090fbcf9 (diff)
Merge branch 'for-linville' of git://github.com/lucacoelho/wl12xx
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/wl12xx/Kconfig10
-rw-r--r--drivers/net/wireless/wl12xx/acx.c40
-rw-r--r--drivers/net/wireless/wl12xx/acx.h18
-rw-r--r--drivers/net/wireless/wl12xx/cmd.c17
-rw-r--r--drivers/net/wireless/wl12xx/cmd.h7
-rw-r--r--drivers/net/wireless/wl12xx/conf.h23
-rw-r--r--drivers/net/wireless/wl12xx/debugfs.c88
-rw-r--r--drivers/net/wireless/wl12xx/event.c37
-rw-r--r--drivers/net/wireless/wl12xx/init.c5
-rw-r--r--drivers/net/wireless/wl12xx/main.c96
-rw-r--r--drivers/net/wireless/wl12xx/ps.c8
-rw-r--r--drivers/net/wireless/wl12xx/rx.c9
-rw-r--r--drivers/net/wireless/wl12xx/scan.c124
-rw-r--r--drivers/net/wireless/wl12xx/tx.c22
-rw-r--r--drivers/net/wireless/wl12xx/tx.h4
-rw-r--r--drivers/net/wireless/wl12xx/wl12xx.h8
16 files changed, 358 insertions, 158 deletions
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig
index 07bcb1548d8..3fe388b87c2 100644
--- a/drivers/net/wireless/wl12xx/Kconfig
+++ b/drivers/net/wireless/wl12xx/Kconfig
@@ -19,16 +19,6 @@ config WL12XX
If you choose to build a module, it will be called wl12xx. Say N if
unsure.
-config WL12XX_HT
- bool "TI wl12xx 802.11 HT support (EXPERIMENTAL)"
- depends on WL12XX && EXPERIMENTAL
- default n
- ---help---
- This will enable 802.11 HT support in the wl12xx module.
-
- That configuration is temporary due to the code incomplete and
- still in testing process.
-
config WL12XX_SPI
tristate "TI wl12xx SPI support"
depends on WL12XX && SPI_MASTER
diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c
index e047594794a..9809e41f06e 100644
--- a/drivers/net/wireless/wl12xx/acx.c
+++ b/drivers/net/wireless/wl12xx/acx.c
@@ -1691,3 +1691,43 @@ out:
kfree(acx);
return ret;
}
+
+int wl12xx_acx_config_hangover(struct wl1271 *wl)
+{
+ struct wl12xx_acx_config_hangover *acx;
+ struct conf_hangover_settings *conf = &wl->conf.hangover;
+ int ret;
+
+ wl1271_debug(DEBUG_ACX, "acx config hangover");
+
+ acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+ if (!acx) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ acx->recover_time = cpu_to_le32(conf->recover_time);
+ acx->hangover_period = conf->hangover_period;
+ acx->dynamic_mode = conf->dynamic_mode;
+ acx->early_termination_mode = conf->early_termination_mode;
+ acx->max_period = conf->max_period;
+ acx->min_period = conf->min_period;
+ acx->increase_delta = conf->increase_delta;
+ acx->decrease_delta = conf->decrease_delta;
+ acx->quiet_time = conf->quiet_time;
+ acx->increase_time = conf->increase_time;
+ acx->window_size = acx->window_size;
+
+ ret = wl1271_cmd_configure(wl, ACX_CONFIG_HANGOVER, acx,
+ sizeof(*acx));
+
+ if (ret < 0) {
+ wl1271_warning("acx config hangover 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 758c596f62f..556ee4e282d 100644
--- a/drivers/net/wireless/wl12xx/acx.h
+++ b/drivers/net/wireless/wl12xx/acx.h
@@ -1144,6 +1144,23 @@ struct wl12xx_acx_set_rate_mgmt_params {
u8 padding2[2];
} __packed;
+struct wl12xx_acx_config_hangover {
+ struct acx_header header;
+
+ __le32 recover_time;
+ u8 hangover_period;
+ u8 dynamic_mode;
+ u8 early_termination_mode;
+ u8 max_period;
+ u8 min_period;
+ u8 increase_delta;
+ u8 decrease_delta;
+ u8 quiet_time;
+ u8 increase_time;
+ u8 window_size;
+ u8 padding[2];
+} __packed;
+
enum {
ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003,
@@ -1281,5 +1298,6 @@ int wl1271_acx_config_ps(struct wl1271 *wl);
int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
int wl1271_acx_fm_coex(struct wl1271 *wl);
int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
+int wl12xx_acx_config_hangover(struct wl1271 *wl);
#endif /* __WL1271_ACX_H__ */
diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c
index 817bc183bc8..084262f169b 100644
--- a/drivers/net/wireless/wl12xx/cmd.c
+++ b/drivers/net/wireless/wl12xx/cmd.c
@@ -895,7 +895,7 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
struct acx_header *acx = buf;
int ret;
- wl1271_debug(DEBUG_CMD, "cmd configure");
+ wl1271_debug(DEBUG_CMD, "cmd configure (%d)", id);
acx->id = cpu_to_le16(id);
@@ -1413,7 +1413,7 @@ out:
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
{
struct wl12xx_cmd_add_peer *cmd;
- int ret;
+ int i, ret;
u32 sta_rates;
wl1271_debug(DEBUG_CMD, "cmd add peer %d", (int)hlid);
@@ -1424,15 +1424,19 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
goto out;
}
- /* currently we don't support UAPSD */
- 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;
+ cmd->sp_len = sta->max_sp;
cmd->wmm = sta->wme ? 1 : 0;
+ for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
+ if (sta->wme && (sta->uapsd_queues & BIT(i)))
+ cmd->psd_type[i] = WL1271_PSD_UPSD_TRIGGER;
+ else
+ cmd->psd_type[i] = WL1271_PSD_LEGACY;
+
sta_rates = sta->supp_rates[wl->band];
if (sta->ht_cap.ht_supported)
sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET;
@@ -1440,7 +1444,8 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid)
cmd->supported_rates =
cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates));
- wl1271_debug(DEBUG_CMD, "new peer rates: 0x%x", cmd->supported_rates);
+ wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x",
+ cmd->supported_rates, sta->uapsd_queues);
ret = wl1271_cmd_send(wl, CMD_ADD_PEER, cmd, sizeof(*cmd), 0);
if (ret < 0) {
diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h
index 22c2f373dd0..8e4d11ec0c5 100644
--- a/drivers/net/wireless/wl12xx/cmd.h
+++ b/drivers/net/wireless/wl12xx/cmd.h
@@ -591,6 +591,13 @@ enum wl12xx_ssid_type {
WL12XX_SSID_TYPE_ANY = 2,
};
+enum wl1271_psd_type {
+ WL1271_PSD_LEGACY = 0,
+ WL1271_PSD_UPSD_TRIGGER = 1,
+ WL1271_PSD_LEGACY_PSPOLL = 2,
+ WL1271_PSD_SAPSD = 3
+};
+
struct wl12xx_cmd_add_peer {
struct wl1271_cmd_header header;
diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h
index 82f205c4334..45428a21f9e 100644
--- a/drivers/net/wireless/wl12xx/conf.h
+++ b/drivers/net/wireless/wl12xx/conf.h
@@ -916,14 +916,6 @@ struct conf_conn_settings {
u8 psm_entry_nullfunc_retries;
/*
- * Specifies the time to linger in active mode after successfully
- * transmitting the PSM entry null-func frame.
- *
- * Range 0 - 255 TU's
- */
- u8 psm_entry_hangover_period;
-
- /*
*
* Specifies the interval of the connection keep-alive null-func
* frame in ms.
@@ -1236,6 +1228,20 @@ struct conf_rate_policy_settings {
u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES];
};
+struct conf_hangover_settings {
+ u32 recover_time;
+ u8 hangover_period;
+ u8 dynamic_mode;
+ u8 early_termination_mode;
+ u8 max_period;
+ u8 min_period;
+ u8 increase_delta;
+ u8 decrease_delta;
+ u8 quiet_time;
+ u8 increase_time;
+ u8 window_size;
+};
+
struct conf_drv_settings {
struct conf_sg_settings sg;
struct conf_rx_settings rx;
@@ -1254,6 +1260,7 @@ struct conf_drv_settings {
struct conf_rx_streaming_settings rx_streaming;
struct conf_fwlog fwlog;
struct conf_rate_policy_settings rate;
+ struct conf_hangover_settings hangover;
u8 hci_io_ds;
};
diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c
index d59354f5370..3999fd52830 100644
--- a/drivers/net/wireless/wl12xx/debugfs.c
+++ b/drivers/net/wireless/wl12xx/debugfs.c
@@ -265,18 +265,10 @@ static ssize_t gpio_power_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
- char buf[10];
- size_t len;
unsigned long value;
int ret;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len)) {
- return -EFAULT;
- }
- buf[len] = '\0';
-
- ret = kstrtoul(buf, 0, &value);
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
if (ret < 0) {
wl1271_warning("illegal value in gpio_power");
return -EINVAL;
@@ -427,17 +419,10 @@ static ssize_t dtim_interval_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
- char buf[10];
- size_t len;
unsigned long value;
int ret;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
-
- ret = kstrtoul(buf, 0, &value);
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
if (ret < 0) {
wl1271_warning("illegal value for dtim_interval");
return -EINVAL;
@@ -492,17 +477,10 @@ static ssize_t beacon_interval_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
- char buf[10];
- size_t len;
unsigned long value;
int ret;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
-
- ret = kstrtoul(buf, 0, &value);
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
if (ret < 0) {
wl1271_warning("illegal value for beacon_interval");
return -EINVAL;
@@ -542,17 +520,10 @@ static ssize_t rx_streaming_interval_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
- char buf[10];
- size_t len;
unsigned long value;
int ret;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
-
- ret = kstrtoul(buf, 0, &value);
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
if (ret < 0) {
wl1271_warning("illegal value in rx_streaming_interval!");
return -EINVAL;
@@ -601,17 +572,10 @@ static ssize_t rx_streaming_always_write(struct file *file,
size_t count, loff_t *ppos)
{
struct wl1271 *wl = file->private_data;
- char buf[10];
- size_t len;
unsigned long value;
int ret;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
-
- ret = kstrtoul(buf, 0, &value);
+ ret = kstrtoul_from_user(user_buf, count, 10, &value);
if (ret < 0) {
wl1271_warning("illegal value in rx_streaming_write!");
return -EINVAL;
@@ -655,6 +619,47 @@ static const struct file_operations rx_streaming_always_ops = {
.llseek = default_llseek,
};
+static ssize_t beacon_filtering_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct wl1271 *wl = file->private_data;
+ char buf[10];
+ size_t len;
+ unsigned long value;
+ int ret;
+
+ len = min(count, sizeof(buf) - 1);
+ if (copy_from_user(buf, user_buf, len))
+ return -EFAULT;
+ buf[len] = '\0';
+
+ ret = kstrtoul(buf, 0, &value);
+ if (ret < 0) {
+ wl1271_warning("illegal value for beacon_filtering!");
+ return -EINVAL;
+ }
+
+ mutex_lock(&wl->mutex);
+
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0)
+ goto out;
+
+ ret = wl1271_acx_beacon_filter_opt(wl, !!value);
+
+ wl1271_ps_elp_sleep(wl);
+out:
+ mutex_unlock(&wl->mutex);
+ return count;
+}
+
+static const struct file_operations beacon_filtering_ops = {
+ .write = beacon_filtering_write,
+ .open = wl1271_open_file_generic,
+ .llseek = default_llseek,
+};
+
static int wl1271_debugfs_add_files(struct wl1271 *wl,
struct dentry *rootdir)
{
@@ -767,6 +772,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl,
DEBUGFS_ADD(driver_state, rootdir);
DEBUGFS_ADD(dtim_interval, rootdir);
DEBUGFS_ADD(beacon_interval, rootdir);
+ DEBUGFS_ADD(beacon_filtering, rootdir);
streaming = debugfs_create_dir("rx_streaming", rootdir);
if (!streaming || IS_ERR(streaming))
diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c
index 0bd7b020a42..c73fe4c6b61 100644
--- a/drivers/net/wireless/wl12xx/event.c
+++ b/drivers/net/wireless/wl12xx/event.c
@@ -171,19 +171,26 @@ static void wl1271_event_rssi_trigger(struct wl1271 *wl,
wl->last_rssi_event = event;
}
-static void wl1271_stop_ba_event(struct wl1271 *wl, u8 ba_allowed)
+static void wl1271_stop_ba_event(struct wl1271 *wl)
{
- /* Convert the value to bool */
- wl->ba_allowed = !!ba_allowed;
-
- /*
- * Return in case:
- * there are not BA open or the event indication is to allowed BA
- */
- if ((!wl->ba_rx_bitmap) || (wl->ba_allowed))
- return;
+ if (wl->bss_type != BSS_TYPE_AP_BSS) {
+ if (!wl->ba_rx_bitmap)
+ return;
+ ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap,
+ wl->bssid);
+ } else {
+ int i;
+ struct wl1271_link *lnk;
+ for (i = WL1271_AP_STA_HLID_START; i < WL12XX_MAX_LINKS; i++) {
+ lnk = &wl->links[i];
+ if (!wl1271_is_active_sta(wl, i) || !lnk->ba_bitmap)
+ continue;
- ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, wl->bssid);
+ ieee80211_stop_rx_ba_session(wl->vif,
+ lnk->ba_bitmap,
+ lnk->addr);
+ }
+ }
}
static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl,
@@ -283,12 +290,14 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
wl1271_event_rssi_trigger(wl, mbox);
}
- if ((vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) && !is_ap) {
+ if ((vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)) {
wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. "
"ba_allowed = 0x%x", mbox->rx_ba_allowed);
- if (wl->vif)
- wl1271_stop_ba_event(wl, mbox->rx_ba_allowed);
+ wl->ba_allowed = !!mbox->rx_ba_allowed;
+
+ if (wl->vif && !wl->ba_allowed)
+ wl1271_stop_ba_event(wl);
}
if ((vector & DUMMY_PACKET_EVENT_ID)) {
diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c
index b13bebea95e..09515f5e5e1 100644
--- a/drivers/net/wireless/wl12xx/init.c
+++ b/drivers/net/wireless/wl12xx/init.c
@@ -707,6 +707,11 @@ int wl1271_hw_init(struct wl1271 *wl)
if (ret < 0)
goto out_free_memmap;
+ /* configure hangover */
+ ret = wl12xx_acx_config_hangover(wl);
+ if (ret < 0)
+ goto out_free_memmap;
+
return 0;
out_free_memmap:
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c
index bde84027ab7..680f5582618 100644
--- a/drivers/net/wireless/wl12xx/main.c
+++ b/drivers/net/wireless/wl12xx/main.c
@@ -239,7 +239,6 @@ static struct conf_drv_settings default_conf = {
.psm_entry_retries = 8,
.psm_exit_retries = 16,
.psm_entry_nullfunc_retries = 3,
- .psm_entry_hangover_period = 1,
.keep_alive_interval = 55000,
.max_listen_interval = 20,
},
@@ -267,8 +266,8 @@ static struct conf_drv_settings default_conf = {
},
.sched_scan = {
/* sched_scan requires dwell times in TU instead of TU/1000 */
- .min_dwell_time_active = 8,
- .max_dwell_time_active = 30,
+ .min_dwell_time_active = 30,
+ .max_dwell_time_active = 60,
.dwell_time_passive = 100,
.dwell_time_dfs = 150,
.num_probe_reqs = 2,
@@ -359,9 +358,23 @@ static struct conf_drv_settings default_conf = {
0x00, 0x00, 0x00,
},
},
+ .hangover = {
+ .recover_time = 0,
+ .hangover_period = 20,
+ .dynamic_mode = 1,
+ .early_termination_mode = 1,
+ .max_period = 20,
+ .min_period = 1,
+ .increase_delta = 1,
+ .decrease_delta = 2,
+ .quiet_time = 4,
+ .increase_time = 1,
+ .window_size = 16,
+ },
};
static char *fwlog_param;
+static bool bug_on_recovery;
static void __wl1271_op_remove_interface(struct wl1271 *wl,
bool reset_tx_queues);
@@ -757,13 +770,14 @@ static int wl1271_plt_init(struct wl1271 *wl)
static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts)
{
- bool fw_ps;
+ bool fw_ps, single_sta;
/* only regulate station links */
if (hlid < WL1271_AP_STA_HLID_START)
return;
fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+ single_sta = (wl->active_sta_count == 1);
/*
* Wake up from high level PS if the STA is asleep with too little
@@ -772,8 +786,12 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts)
if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS)
wl1271_ps_link_end(wl, hlid);
- /* Start high-level PS if the STA is asleep with enough blocks in FW */
- else if (fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+ /*
+ * Start high-level PS if the STA is asleep with enough blocks in FW.
+ * Make an exception if this is the only connected station. In this
+ * case FW-memory congestion is not a problem.
+ */
+ else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
wl1271_ps_link_start(wl, hlid, true);
}
@@ -1213,6 +1231,8 @@ static void wl1271_recovery_work(struct work_struct *work)
wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x",
wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4));
+ BUG_ON(bug_on_recovery);
+
/*
* Advance security sequence number to overcome potential progress
* in the firmware during recovery. This doens't hurt if the network is
@@ -1222,9 +1242,6 @@ static void wl1271_recovery_work(struct work_struct *work)
test_bit(WL1271_FLAG_AP_STARTED, &wl->flags))
wl->tx_security_seq += WL1271_TX_SQN_POST_RECOVERY_PADDING;
- if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
- ieee80211_connection_loss(wl->vif);
-
/* Prevent spurious TX during FW restart */
ieee80211_stop_queues(wl->hw);
@@ -1528,7 +1545,13 @@ out:
int wl1271_tx_dummy_packet(struct wl1271 *wl)
{
unsigned long flags;
- int q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
+ int q;
+
+ /* no need to queue a new dummy packet if one is already pending */
+ if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags))
+ return 0;
+
+ q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet));
spin_lock_irqsave(&wl->wl_lock, flags);
set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
@@ -1802,10 +1825,16 @@ static u8 wl12xx_get_role_type(struct wl1271 *wl)
{
switch (wl->bss_type) {
case BSS_TYPE_AP_BSS:
- return WL1271_ROLE_AP;
+ if (wl->p2p)
+ return WL1271_ROLE_P2P_GO;
+ else
+ return WL1271_ROLE_AP;
case BSS_TYPE_STA_BSS:
- return WL1271_ROLE_STA;
+ if (wl->p2p)
+ return WL1271_ROLE_P2P_CL;
+ else
+ return WL1271_ROLE_STA;
case BSS_TYPE_IBSS:
return WL1271_ROLE_IBSS;
@@ -1827,7 +1856,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
bool booted = false;
wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
- vif->type, vif->addr);
+ ieee80211_vif_type_p2p(vif), vif->addr);
mutex_lock(&wl->mutex);
if (wl->vif) {
@@ -1847,7 +1876,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
goto out;
}
- switch (vif->type) {
+ switch (ieee80211_vif_type_p2p(vif)) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ wl->p2p = 1;
+ /* fall-through */
case NL80211_IFTYPE_STATION:
wl->bss_type = BSS_TYPE_STA_BSS;
wl->set_bss_type = BSS_TYPE_STA_BSS;
@@ -1856,6 +1888,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_P2P_GO:
+ wl->p2p = 1;
+ /* fall-through */
case NL80211_IFTYPE_AP:
wl->bss_type = BSS_TYPE_AP_BSS;
break;
@@ -2051,6 +2086,7 @@ deinit:
wl->ssid_len = 0;
wl->bss_type = MAX_BSS_TYPE;
wl->set_bss_type = MAX_BSS_TYPE;
+ wl->p2p = 0;
wl->band = IEEE80211_BAND_2GHZ;
wl->rx_counter = 0;
@@ -2075,6 +2111,7 @@ deinit:
memset(wl->roles_map, 0, sizeof(wl->roles_map));
memset(wl->links_map, 0, sizeof(wl->links_map));
memset(wl->roc_map, 0, sizeof(wl->roc_map));
+ wl->active_sta_count = 0;
/* The system link is always allocated */
__set_bit(WL12XX_SYSTEM_HLID, wl->links_map);
@@ -3729,14 +3766,18 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
wl_sta->hlid = WL1271_AP_STA_HLID_START + id;
*hlid = wl_sta->hlid;
memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN);
+ wl->active_sta_count++;
return 0;
}
-static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
+void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
{
int id = hlid - WL1271_AP_STA_HLID_START;
- if (WARN_ON(!test_bit(id, wl->ap_hlid_map)))
+ if (hlid < WL1271_AP_STA_HLID_START)
+ return;
+
+ if (!test_bit(id, wl->ap_hlid_map))
return;
clear_bit(id, wl->ap_hlid_map);
@@ -3745,6 +3786,7 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid)
wl1271_tx_reset_link_queues(wl, hlid);
__clear_bit(hlid, &wl->ap_ps_map);
__clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
+ wl->active_sta_count--;
}
static int wl1271_op_sta_add(struct ieee80211_hw *hw,
@@ -4066,7 +4108,6 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
/* 11n STA capabilities */
#define HW_RX_HIGHEST_RATE 72
-#ifdef CONFIG_WL12XX_HT
#define WL12XX_HT_CAP { \
.cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \
(1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
@@ -4079,11 +4120,6 @@ static const u8 wl1271_rate_to_idx_2ghz[] = {
.tx_params = IEEE80211_HT_MCS_TX_DEFINED, \
}, \
}
-#else
-#define WL12XX_HT_CAP { \
- .ht_supported = false, \
-}
-#endif
/* can't be const, mac80211 writes to this */
static struct ieee80211_supported_band wl1271_band_2ghz = {
@@ -4483,15 +4519,19 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
IEEE80211_HW_SUPPORTS_CQM_RSSI |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SPECTRUM_MGMT |
- IEEE80211_HW_AP_LINK_PS;
+ IEEE80211_HW_AP_LINK_PS |
+ IEEE80211_HW_AMPDU_AGGREGATION |
+ IEEE80211_HW_TX_AMPDU_SETUP_IN_HW;
wl->hw->wiphy->cipher_suites = cipher_suites;
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_AP);
+ BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO);
wl->hw->wiphy->max_scan_ssids = 1;
- wl->hw->wiphy->max_sched_scan_ssids = 8;
+ wl->hw->wiphy->max_sched_scan_ssids = 16;
+ wl->hw->wiphy->max_match_sets = 16;
/*
* Maximum length of elements in scanning probe request templates
* should be the maximum length possible for a template, without
@@ -4500,6 +4540,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE -
sizeof(struct ieee80211_header);
+ wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+
/* make sure all our channels fit in the scanned_ch bitmask */
BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
ARRAY_SIZE(wl1271_channels_5ghz) >
@@ -4625,6 +4667,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->session_counter = 0;
wl->ap_bcast_hlid = WL12XX_INVALID_LINK_ID;
wl->ap_global_hlid = WL12XX_INVALID_LINK_ID;
+ wl->active_sta_count = 0;
setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer,
(unsigned long) wl);
wl->fwlog_size = 0;
@@ -4776,6 +4819,9 @@ module_param_named(fwlog, fwlog_param, charp, 0);
MODULE_PARM_DESC(keymap,
"FW logger options: continuous, ondemand, dbgpins or disable");
+module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c
index 4b720b1b9f6..c15ebf2efd4 100644
--- a/drivers/net/wireless/wl12xx/ps.c
+++ b/drivers/net/wireless/wl12xx/ps.c
@@ -199,15 +199,19 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid)
unsigned long flags;
int filtered[NUM_TX_QUEUES];
- /* filter all frames currently the low level queus for this hlid */
+ /* filter all frames currently in the low level queues for this hlid */
for (i = 0; i < NUM_TX_QUEUES; i++) {
filtered[i] = 0;
while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) {
+ filtered[i]++;
+
+ if (WARN_ON(wl12xx_is_dummy_packet(wl, skb)))
+ continue;
+
info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
info->status.rates[0].idx = -1;
ieee80211_tx_status_ni(wl->hw, skb);
- filtered[i]++;
}
}
diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c
index 78d8410da1f..dee4cfe9ccc 100644
--- a/drivers/net/wireless/wl12xx/rx.c
+++ b/drivers/net/wireless/wl12xx/rx.c
@@ -66,11 +66,9 @@ static void wl1271_rx_status(struct wl1271 *wl,
status->rate_idx = wl1271_rate_to_idx(desc->rate, status->band);
-#ifdef CONFIG_WL12XX_HT
/* 11n support */
if (desc->rate <= CONF_HW_RXTX_RATE_MCS0)
status->flag |= RX_FLAG_HT;
-#endif
status->signal = desc->rssi;
@@ -107,6 +105,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
u8 beacon = 0;
u8 is_data = 0;
u8 reserved = unaligned ? NET_IP_ALIGN : 0;
+ u16 seq_num;
/*
* In PLT mode we seem to get frames and mac80211 warns about them,
@@ -169,9 +168,11 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
- wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb,
+ seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
+ wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d", skb,
skb->len - desc->pad_len,
- beacon ? "beacon" : "");
+ beacon ? "beacon" : "",
+ seq_num);
skb_trim(skb, skb->len - desc->pad_len);
diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c
index af4ad2353f5..eeccc9f095b 100644
--- a/drivers/net/wireless/wl12xx/scan.c
+++ b/drivers/net/wireless/wl12xx/scan.c
@@ -65,8 +65,9 @@ void wl1271_scan_complete_work(struct work_struct *work)
/* return to ROC if needed */
is_sta = (wl->bss_type == BSS_TYPE_STA_BSS);
is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
- if ((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ||
- (is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) {
+ if (((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ||
+ (is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) &&
+ !test_bit(wl->dev_role_id, wl->roc_map)) {
/* restore remain on channel */
wl12xx_cmd_role_start_dev(wl);
wl12xx_roc(wl, wl->dev_role_id);
@@ -164,9 +165,6 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
goto out;
}
- /* We always use high priority scans */
- scan_options = WL1271_SCAN_OPT_PRIORITY_HIGH;
-
/* No SSIDs means that we have a forced passive scan */
if (passive || wl->scan.req->n_ssids == 0)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
@@ -473,34 +471,86 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl,
cfg->passive[2] || cfg->active[2];
}
-/* Returns 0 if no wildcard is used, 1 if wildcard is used or a
- * negative value on error */
+/* Returns the scan type to be used or a negative value on error */
static int
wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
struct cfg80211_sched_scan_request *req)
{
struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL;
- struct cfg80211_ssid *ssid = req->ssids;
- int ret, wildcard = 0;
+ struct cfg80211_match_set *sets = req->match_sets;
+ struct cfg80211_ssid *ssids = req->ssids;
+ int ret = 0, type, i, j, n_match_ssids = 0;
wl1271_debug(DEBUG_CMD, "cmd sched scan ssid list");
+ /* count the match sets that contain SSIDs */
+ for (i = 0; i < req->n_match_sets; i++)
+ if (sets[i].ssid.ssid_len > 0)
+ n_match_ssids++;
+
+ /* No filter, no ssids or only bcast ssid */
+ if (!n_match_ssids &&
+ (!req->n_ssids ||
+ (req->n_ssids == 1 && req->ssids[0].ssid_len == 0))) {
+ type = SCAN_SSID_FILTER_ANY;
+ goto out;
+ }
+
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
+ if (!cmd) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (!n_match_ssids) {
+ /* No filter, with ssids */
+ type = SCAN_SSID_FILTER_DISABLED;
+
+ for (i = 0; i < req->n_ssids; i++) {
+ cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ?
+ SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC;
+ cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len;
+ memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid,
+ ssids[i].ssid_len);
+ cmd->n_ssids++;
+ }
+ } else {
+ type = SCAN_SSID_FILTER_LIST;
+
+ /* Add all SSIDs from the filters */
+ for (i = 0; i < req->n_match_sets; i++) {
+ /* ignore sets without SSIDs */
+ if (!sets[i].ssid.ssid_len)
+ continue;
- while ((cmd->n_ssids < req->n_ssids) && ssid) {
- if (ssid->ssid_len == 0) {
- wildcard = 1;
cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC;
- } else {
- cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_HIDDEN;
+ cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len;
+ memcpy(cmd->ssids[cmd->n_ssids].ssid,
+ sets[i].ssid.ssid, sets[i].ssid.ssid_len);
+ cmd->n_ssids++;
+ }
+ if ((req->n_ssids > 1) ||
+ (req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) {
+ /*
+ * Mark all the SSIDs passed in the SSID list as HIDDEN,
+ * so they're used in probe requests.
+ */
+ for (i = 0; i < req->n_ssids; i++) {
+ for (j = 0; j < cmd->n_ssids; j++)
+ if (!memcmp(req->ssids[i].ssid,
+ cmd->ssids[j].ssid,
+ req->ssids[i].ssid_len)) {
+ cmd->ssids[j].type =
+ SCAN_SSID_TYPE_HIDDEN;
+ break;
+ }
+ /* Fail if SSID isn't present in the filters */
+ if (j == req->n_ssids) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+ }
}
- cmd->ssids[cmd->n_ssids].len = ssid->ssid_len;
- memcpy(cmd->ssids[cmd->n_ssids].ssid, ssid->ssid,
- ssid->ssid_len);
- ssid++;
- cmd->n_ssids++;
}
wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd));
@@ -509,13 +559,15 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("cmd sched scan ssid list failed");
- goto out;
+ goto out_free;
}
- ret = wildcard;
-out:
+out_free:
kfree(cmd);
- return ret;
+out:
+ if (ret < 0)
+ return ret;
+ return type;
}
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
@@ -550,21 +602,13 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
cfg->intervals[i] = cpu_to_le32(req->interval);
cfg->ssid_len = 0;
- if (req->n_ssids == 0) {
- wl1271_debug(DEBUG_SCAN, "using SCAN_SSID_FILTER_ANY");
- cfg->filter_type = SCAN_SSID_FILTER_ANY;
- } else {
- ret = wl12xx_scan_sched_scan_ssid_list(wl, req);
- if (ret < 0)
- goto out;
- if (ret) {
- wl1271_debug(DEBUG_SCAN, "using SCAN_SSID_FILTER_DISABLED");
- cfg->filter_type = SCAN_SSID_FILTER_DISABLED;
- } else {
- wl1271_debug(DEBUG_SCAN, "using SCAN_SSID_FILTER_LIST");
- cfg->filter_type = SCAN_SSID_FILTER_LIST;
- }
- }
+ ret = wl12xx_scan_sched_scan_ssid_list(wl, req);
+ if (ret < 0)
+ goto out;
+
+ cfg->filter_type = ret;
+
+ wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
wl1271_error("scan channel list is empty");
diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c
index 08227e69616..9d4157ce095 100644
--- a/drivers/net/wireless/wl12xx/tx.c
+++ b/drivers/net/wireless/wl12xx/tx.c
@@ -30,6 +30,7 @@
#include "reg.h"
#include "ps.h"
#include "tx.h"
+#include "event.h"
static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id)
{
@@ -125,25 +126,31 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid)
{
- bool fw_ps;
+ bool fw_ps, single_sta;
u8 tx_pkts;
/* only regulate station links */
if (hlid < WL1271_AP_STA_HLID_START)
return;
+ if (WARN_ON(!wl1271_is_active_sta(wl, hlid)))
+ return;
+
fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map);
tx_pkts = wl->links[hlid].allocated_pkts;
+ single_sta = (wl->active_sta_count == 1);
/*
* if in FW PS and there is enough data in FW we can put the link
* into high-level PS and clean out its TX queues.
+ * Make an exception if this is the only connected station. In this
+ * case FW-memory congestion is not a problem.
*/
- if (fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
+ if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
wl1271_ps_link_start(wl, hlid, true);
}
-static bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
+bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
{
return wl->dummy_packet == skb;
}
@@ -453,7 +460,6 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
rate_set >>= 1;
}
-#ifdef CONFIG_WL12XX_HT
/* MCS rates indication are on bits 16 - 23 */
rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates;
@@ -462,7 +468,6 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set)
enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit);
rate_set >>= 1;
}
-#endif
return enabled_rates;
}
@@ -886,6 +891,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
/* TX failure */
if (wl->bss_type == BSS_TYPE_AP_BSS) {
for (i = 0; i < AP_MAX_LINKS; i++) {
+ wl1271_free_sta(wl, i);
wl1271_tx_reset_link_queues(wl, i);
wl->links[i].allocated_pkts = 0;
wl->links[i].prev_freed_pkts = 0;
@@ -905,10 +911,14 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
ieee80211_tx_status_ni(wl->hw, skb);
}
}
- wl->tx_queue_count[i] = 0;
}
+
+ wl->ba_rx_bitmap = 0;
}
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ wl->tx_queue_count[i] = 0;
+
wl->stopped_queues_map = 0;
/*
diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h
index 6519be4b2c3..d6fdbf904a0 100644
--- a/drivers/net/wireless/wl12xx/tx.h
+++ b/drivers/net/wireless/wl12xx/tx.h
@@ -214,5 +214,9 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl);
u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb);
void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid);
void wl1271_handle_tx_low_watermark(struct wl1271 *wl);
+bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb);
+
+/* from main.c */
+void wl1271_free_sta(struct wl1271 *wl, u8 hlid);
#endif
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h
index fb2753c4630..3ceb20c170b 100644
--- a/drivers/net/wireless/wl12xx/wl12xx.h
+++ b/drivers/net/wireless/wl12xx/wl12xx.h
@@ -234,14 +234,14 @@ struct wl1271_stats {
#define NUM_TX_QUEUES 4
#define NUM_RX_PKT_DESC 8
-#define AP_MAX_STATIONS 5
+#define AP_MAX_STATIONS 8
/* Broadcast and Global links + system link + links to stations */
/*
* TODO: when WL1271_AP_STA_HLID_START is no longer constant, change all
* the places that use this.
*/
-#define AP_MAX_LINKS (AP_MAX_STATIONS + 3)
+#define AP_MAX_LINKS (AP_MAX_STATIONS + WL1271_AP_STA_HLID_START)
/* FW status registers */
struct wl12xx_fw_status {
@@ -402,6 +402,7 @@ struct wl1271 {
u8 mac_addr[ETH_ALEN];
u8 bss_type;
u8 set_bss_type;
+ u8 p2p; /* we are using p2p role */
u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
u8 ssid_len;
int channel;
@@ -626,6 +627,9 @@ struct wl1271 {
/* number of currently active RX BA sessions */
int ba_rx_session_count;
+
+ /* AP-mode - number of currently connected stations */
+ int active_sta_count;
};
struct wl1271_station {