Browse Source

Merge branch 'for-linville' of git://github.com/lucacoelho/wl12xx

John W. Linville 13 years ago
parent
commit
376cf5d3fd

+ 0 - 10
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

+ 40 - 0
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;
+
+}

+ 18 - 0
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__ */

+ 11 - 6
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) {

+ 7 - 0
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;
 

+ 15 - 8
drivers/net/wireless/wl12xx/conf.h

@@ -915,14 +915,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
@@ -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;
 };
 

+ 47 - 41
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))

+ 23 - 14
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)) {

+ 5 - 0
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:

+ 71 - 25
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>");

+ 6 - 2
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]++;
 		}
 	}
 

+ 5 - 4
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);
 

+ 84 - 40
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");

+ 16 - 6
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;
 
 	/*

+ 4 - 0
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

+ 6 - 2
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 {