diff options
| author | John W. Linville <linville@tuxdriver.com> | 2013-02-08 13:16:17 -0500 | 
|---|---|---|
| committer | John W. Linville <linville@tuxdriver.com> | 2013-02-08 13:16:17 -0500 | 
| commit | f5237f278f30a92401539a54f87ee0c717b6f818 (patch) | |
| tree | 209d4fd6fb00e660c76ca8ac5d4caed59dbb9957 /drivers/net/wireless/ti/wl12xx | |
| parent | b285109dde7b873b5dc671ef1b3ae3090f4bc72f (diff) | |
| parent | b26f5f09ebdeb85ab152344cc1d6d484a3ce967d (diff) | |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Diffstat (limited to 'drivers/net/wireless/ti/wl12xx')
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/Makefile | 2 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/cmd.c | 37 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/cmd.h | 20 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/event.c | 116 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/event.h | 111 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/main.c | 192 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/scan.c | 501 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/scan.h | 140 | ||||
| -rw-r--r-- | drivers/net/wireless/ti/wl12xx/wl12xx.h | 40 | 
9 files changed, 1096 insertions, 63 deletions
| diff --git a/drivers/net/wireless/ti/wl12xx/Makefile b/drivers/net/wireless/ti/wl12xx/Makefile index da509aa7d009..e6a24056b3c8 100644 --- a/drivers/net/wireless/ti/wl12xx/Makefile +++ b/drivers/net/wireless/ti/wl12xx/Makefile @@ -1,3 +1,3 @@ -wl12xx-objs	= main.o cmd.o acx.o debugfs.o +wl12xx-objs	= main.o cmd.o acx.o debugfs.o scan.o event.o  obj-$(CONFIG_WL12XX)		+= wl12xx.o diff --git a/drivers/net/wireless/ti/wl12xx/cmd.c b/drivers/net/wireless/ti/wl12xx/cmd.c index 622206241e83..7dc9f965037d 100644 --- a/drivers/net/wireless/ti/wl12xx/cmd.c +++ b/drivers/net/wireless/ti/wl12xx/cmd.c @@ -284,3 +284,40 @@ int wl128x_cmd_radio_parms(struct wl1271 *wl)  	kfree(radio_parms);  	return ret;  } + +int wl12xx_cmd_channel_switch(struct wl1271 *wl, +			      struct wl12xx_vif *wlvif, +			      struct ieee80211_channel_switch *ch_switch) +{ +	struct wl12xx_cmd_channel_switch *cmd; +	int ret; + +	wl1271_debug(DEBUG_ACX, "cmd channel switch"); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (!cmd) { +		ret = -ENOMEM; +		goto out; +	} + +	cmd->role_id = wlvif->role_id; +	cmd->channel = ch_switch->channel->hw_value; +	cmd->switch_time = ch_switch->count; +	cmd->stop_tx = ch_switch->block_tx; + +	/* FIXME: control from mac80211 in the future */ +	/* Enable TX on the target channel */ +	cmd->post_switch_tx_disable = 0; + +	ret = wl1271_cmd_send(wl, CMD_CHANNEL_SWITCH, cmd, sizeof(*cmd), 0); +	if (ret < 0) { +		wl1271_error("failed to send channel switch command"); +		goto out_free; +	} + +out_free: +	kfree(cmd); + +out: +	return ret; +} diff --git a/drivers/net/wireless/ti/wl12xx/cmd.h b/drivers/net/wireless/ti/wl12xx/cmd.h index 140a0e8829d5..32cbad54e993 100644 --- a/drivers/net/wireless/ti/wl12xx/cmd.h +++ b/drivers/net/wireless/ti/wl12xx/cmd.h @@ -103,10 +103,30 @@ struct wl1271_ext_radio_parms_cmd {  	u8 padding[3];  } __packed; +struct wl12xx_cmd_channel_switch { +	struct wl1271_cmd_header header; + +	u8 role_id; + +	/* The new serving channel */ +	u8 channel; +	/* Relative time of the serving channel switch in TBTT units */ +	u8 switch_time; +	/* Stop the role TX, should expect it after radar detection */ +	u8 stop_tx; +	/* The target channel tx status 1-stopped 0-open*/ +	u8 post_switch_tx_disable; + +	u8 padding[3]; +} __packed; +  int wl1271_cmd_general_parms(struct wl1271 *wl);  int wl128x_cmd_general_parms(struct wl1271 *wl);  int wl1271_cmd_radio_parms(struct wl1271 *wl);  int wl128x_cmd_radio_parms(struct wl1271 *wl);  int wl1271_cmd_ext_radio_parms(struct wl1271 *wl); +int wl12xx_cmd_channel_switch(struct wl1271 *wl, +			      struct wl12xx_vif *wlvif, +			      struct ieee80211_channel_switch *ch_switch);  #endif /* __WL12XX_CMD_H__ */ diff --git a/drivers/net/wireless/ti/wl12xx/event.c b/drivers/net/wireless/ti/wl12xx/event.c new file mode 100644 index 000000000000..6ac0ed751da8 --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/event.c @@ -0,0 +1,116 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "event.h" +#include "scan.h" +#include "../wlcore/cmd.h" +#include "../wlcore/debug.h" + +int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, +			  bool *timeout) +{ +	u32 local_event; + +	switch (event) { +	case WLCORE_EVENT_ROLE_STOP_COMPLETE: +		local_event = ROLE_STOP_COMPLETE_EVENT_ID; +		break; + +	case WLCORE_EVENT_PEER_REMOVE_COMPLETE: +		local_event = PEER_REMOVE_COMPLETE_EVENT_ID; +		break; + +	default: +		/* event not implemented */ +		return 0; +	} +	return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout); +} + +int wl12xx_process_mailbox_events(struct wl1271 *wl) +{ +	struct wl12xx_event_mailbox *mbox = wl->mbox; +	u32 vector; + + +	vector = le32_to_cpu(mbox->events_vector); +	vector &= ~(le32_to_cpu(mbox->events_mask)); + +	wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector); + +	if (vector & SCAN_COMPLETE_EVENT_ID) { +		wl1271_debug(DEBUG_EVENT, "status: 0x%x", +			     mbox->scheduled_scan_status); + +		if (wl->scan_wlvif) +			wl12xx_scan_completed(wl, wl->scan_wlvif); +	} + +	if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { +		wl1271_debug(DEBUG_EVENT, +			     "PERIODIC_SCAN_REPORT_EVENT (status 0x%0x)", +			     mbox->scheduled_scan_status); + +		wlcore_scan_sched_scan_results(wl); +	} + +	if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) +		wlcore_event_sched_scan_completed(wl, +						  mbox->scheduled_scan_status); +	if (vector & SOFT_GEMINI_SENSE_EVENT_ID) +		wlcore_event_soft_gemini_sense(wl, +					       mbox->soft_gemini_sense_info); + +	if (vector & BSS_LOSE_EVENT_ID) +		wlcore_event_beacon_loss(wl, 0xff); + +	if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) +		wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric); + +	if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) +		wlcore_event_ba_rx_constraint(wl, +					      BIT(mbox->role_id), +					      mbox->rx_ba_allowed); + +	if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) +		wlcore_event_channel_switch(wl, 0xff, +					    mbox->channel_switch_status); + +	if (vector & DUMMY_PACKET_EVENT_ID) +		wlcore_event_dummy_packet(wl); + +	/* +	 * "TX retries exceeded" has a different meaning according to mode. +	 * In AP mode the offending station is disconnected. +	 */ +	if (vector & MAX_TX_RETRY_EVENT_ID) +		wlcore_event_max_tx_failure(wl, +				le16_to_cpu(mbox->sta_tx_retry_exceeded)); + +	if (vector & INACTIVE_STA_EVENT_ID) +		wlcore_event_inactive_sta(wl, +					  le16_to_cpu(mbox->sta_aging_status)); + +	if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) +		wlcore_event_roc_complete(wl); + +	return 0; +} diff --git a/drivers/net/wireless/ti/wl12xx/event.h b/drivers/net/wireless/ti/wl12xx/event.h new file mode 100644 index 000000000000..a5cc3fcd9eea --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/event.h @@ -0,0 +1,111 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL12XX_EVENT_H__ +#define __WL12XX_EVENT_H__ + +#include "../wlcore/wlcore.h" + +enum { +	MEASUREMENT_START_EVENT_ID		 = BIT(8), +	MEASUREMENT_COMPLETE_EVENT_ID		 = BIT(9), +	SCAN_COMPLETE_EVENT_ID			 = BIT(10), +	WFD_DISCOVERY_COMPLETE_EVENT_ID		 = BIT(11), +	AP_DISCOVERY_COMPLETE_EVENT_ID		 = BIT(12), +	RESERVED1			         = BIT(13), +	PSPOLL_DELIVERY_FAILURE_EVENT_ID	 = BIT(14), +	ROLE_STOP_COMPLETE_EVENT_ID		 = BIT(15), +	RADAR_DETECTED_EVENT_ID                  = BIT(16), +	CHANNEL_SWITCH_COMPLETE_EVENT_ID	 = BIT(17), +	BSS_LOSE_EVENT_ID			 = BIT(18), +	REGAINED_BSS_EVENT_ID			 = BIT(19), +	MAX_TX_RETRY_EVENT_ID			 = BIT(20), +	DUMMY_PACKET_EVENT_ID			 = BIT(21), +	SOFT_GEMINI_SENSE_EVENT_ID		 = BIT(22), +	CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID	 = BIT(23), +	SOFT_GEMINI_AVALANCHE_EVENT_ID		 = BIT(24), +	PLT_RX_CALIBRATION_COMPLETE_EVENT_ID	 = BIT(25), +	INACTIVE_STA_EVENT_ID			 = BIT(26), +	PEER_REMOVE_COMPLETE_EVENT_ID		 = BIT(27), +	PERIODIC_SCAN_COMPLETE_EVENT_ID		 = BIT(28), +	PERIODIC_SCAN_REPORT_EVENT_ID		 = BIT(29), +	BA_SESSION_RX_CONSTRAINT_EVENT_ID	 = BIT(30), +	REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID	 = BIT(31), +}; + +struct wl12xx_event_mailbox { +	__le32 events_vector; +	__le32 events_mask; +	__le32 reserved_1; +	__le32 reserved_2; + +	u8 number_of_scan_results; +	u8 scan_tag; +	u8 completed_scan_status; +	u8 reserved_3; + +	u8 soft_gemini_sense_info; +	u8 soft_gemini_protective_info; +	s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; +	u8 change_auto_mode_timeout; +	u8 scheduled_scan_status; +	u8 reserved4; +	/* tuned channel (roc) */ +	u8 roc_channel; + +	__le16 hlid_removed_bitmap; + +	/* bitmap of aged stations (by HLID) */ +	__le16 sta_aging_status; + +	/* bitmap of stations (by HLID) which exceeded max tx retries */ +	__le16 sta_tx_retry_exceeded; + +	/* discovery completed results */ +	u8 discovery_tag; +	u8 number_of_preq_results; +	u8 number_of_prsp_results; +	u8 reserved_5; + +	/* rx ba constraint */ +	u8 role_id; /* 0xFF means any role. */ +	u8 rx_ba_allowed; +	u8 reserved_6[2]; + +	/* Channel switch results */ + +	u8 channel_switch_role_id; +	u8 channel_switch_status; +	u8 reserved_7[2]; + +	u8 ps_poll_delivery_failure_role_ids; +	u8 stopped_role_ids; +	u8 started_role_ids; + +	u8 reserved_8[9]; +} __packed; + +int wl12xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, +			  bool *timeout); +int wl12xx_process_mailbox_events(struct wl1271 *wl); + +#endif + diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index e5f5f8f39144..3254bfc81a2a 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -38,6 +38,8 @@  #include "reg.h"  #include "cmd.h"  #include "acx.h" +#include "scan.h" +#include "event.h"  #include "debugfs.h"  static char *fref_param; @@ -208,6 +210,8 @@ static struct wlcore_conf wl12xx_conf = {  		.tmpl_short_retry_limit      = 10,  		.tmpl_long_retry_limit       = 10,  		.tx_watchdog_timeout         = 5000, +		.slow_link_thold             = 3, +		.fast_link_thold             = 10,  	},  	.conn = {  		.wake_up_event               = CONF_WAKE_UP_EVENT_DTIM, @@ -265,8 +269,10 @@ static struct wlcore_conf wl12xx_conf = {  	.scan = {  		.min_dwell_time_active        = 7500,  		.max_dwell_time_active        = 30000, -		.min_dwell_time_passive       = 100000, -		.max_dwell_time_passive       = 100000, +		.min_dwell_time_active_long   = 25000, +		.max_dwell_time_active_long   = 50000, +		.dwell_time_passive           = 100000, +		.dwell_time_dfs               = 150000,  		.num_probe_reqs               = 2,  		.split_scan_timeout           = 50000,  	}, @@ -368,6 +374,10 @@ static struct wlcore_conf wl12xx_conf = {  		.increase_time              = 1,  		.window_size                = 16,  	}, +	.recovery = { +		.bug_on_recovery	    = 0, +		.no_recovery		    = 0, +	},  };  static struct wl12xx_priv_conf wl12xx_default_priv_conf = { @@ -601,9 +611,9 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)  {  	int ret; -	if (wl->chip.id != CHIP_ID_1283_PG20) { +	if (wl->chip.id != CHIP_ID_128X_PG20) {  		struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; -		struct wl127x_rx_mem_pool_addr rx_mem_addr; +		struct wl12xx_priv *priv = wl->priv;  		/*  		 * Choose the block we want to read @@ -612,13 +622,13 @@ static int wl127x_prepare_read(struct wl1271 *wl, u32 rx_desc, u32 len)  		 */  		u32 mem_block = rx_desc & RX_MEM_BLOCK_MASK; -		rx_mem_addr.addr = (mem_block << 8) + +		priv->rx_mem_addr->addr = (mem_block << 8) +  			le32_to_cpu(wl_mem_map->packet_memory_pool_start); -		rx_mem_addr.addr_extra = rx_mem_addr.addr + 4; +		priv->rx_mem_addr->addr_extra = priv->rx_mem_addr->addr + 4; -		ret = wlcore_write(wl, WL1271_SLV_REG_DATA, &rx_mem_addr, -				   sizeof(rx_mem_addr), false); +		ret = wlcore_write(wl, WL1271_SLV_REG_DATA, priv->rx_mem_addr, +				   sizeof(*priv->rx_mem_addr), false);  		if (ret < 0)  			return ret;  	} @@ -631,13 +641,15 @@ static int wl12xx_identify_chip(struct wl1271 *wl)  	int ret = 0;  	switch (wl->chip.id) { -	case CHIP_ID_1271_PG10: +	case CHIP_ID_127X_PG10:  		wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete",  			       wl->chip.id);  		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |  			      WLCORE_QUIRK_DUAL_PROBE_TMPL | -			      WLCORE_QUIRK_TKIP_HEADER_SPACE; +			      WLCORE_QUIRK_TKIP_HEADER_SPACE | +			      WLCORE_QUIRK_START_STA_FAILS | +			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;  		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;  		wl->mr_fw_name = WL127X_FW_NAME_MULTI;  		memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x, @@ -646,18 +658,22 @@ static int wl12xx_identify_chip(struct wl1271 *wl)  		/* read data preparation is only needed by wl127x */  		wl->ops->prepare_read = wl127x_prepare_read; -		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, -				      WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, -				      WL127X_MINOR_VER); +		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, +			      WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER, +			      WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER, +			      WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER, +			      WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);  		break; -	case CHIP_ID_1271_PG20: +	case CHIP_ID_127X_PG20:  		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)",  			     wl->chip.id);  		wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |  			      WLCORE_QUIRK_DUAL_PROBE_TMPL | -			      WLCORE_QUIRK_TKIP_HEADER_SPACE; +			      WLCORE_QUIRK_TKIP_HEADER_SPACE | +			      WLCORE_QUIRK_START_STA_FAILS | +			      WLCORE_QUIRK_AP_ZERO_SESSION_ID;  		wl->plt_fw_name = WL127X_PLT_FW_NAME;  		wl->sr_fw_name = WL127X_FW_NAME_SINGLE;  		wl->mr_fw_name = WL127X_FW_NAME_MULTI; @@ -667,12 +683,14 @@ static int wl12xx_identify_chip(struct wl1271 *wl)  		/* read data preparation is only needed by wl127x */  		wl->ops->prepare_read = wl127x_prepare_read; -		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, WL127X_IFTYPE_VER, -				      WL127X_MAJOR_VER, WL127X_SUBTYPE_VER, -				      WL127X_MINOR_VER); +		wlcore_set_min_fw_ver(wl, WL127X_CHIP_VER, +			      WL127X_IFTYPE_SR_VER,  WL127X_MAJOR_SR_VER, +			      WL127X_SUBTYPE_SR_VER, WL127X_MINOR_SR_VER, +			      WL127X_IFTYPE_MR_VER,  WL127X_MAJOR_MR_VER, +			      WL127X_SUBTYPE_MR_VER, WL127X_MINOR_MR_VER);  		break; -	case CHIP_ID_1283_PG20: +	case CHIP_ID_128X_PG20:  		wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",  			     wl->chip.id);  		wl->plt_fw_name = WL128X_PLT_FW_NAME; @@ -682,19 +700,29 @@ static int wl12xx_identify_chip(struct wl1271 *wl)  		/* wl128x requires TX blocksize alignment */  		wl->quirks |= WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN |  			      WLCORE_QUIRK_DUAL_PROBE_TMPL | -			      WLCORE_QUIRK_TKIP_HEADER_SPACE; - -		wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, WL128X_IFTYPE_VER, -				      WL128X_MAJOR_VER, WL128X_SUBTYPE_VER, -				      WL128X_MINOR_VER); +			      WLCORE_QUIRK_TKIP_HEADER_SPACE | +			      WLCORE_QUIRK_START_STA_FAILS | +			      WLCORE_QUIRK_AP_ZERO_SESSION_ID; + +		wlcore_set_min_fw_ver(wl, WL128X_CHIP_VER, +			      WL128X_IFTYPE_SR_VER,  WL128X_MAJOR_SR_VER, +			      WL128X_SUBTYPE_SR_VER, WL128X_MINOR_SR_VER, +			      WL128X_IFTYPE_MR_VER,  WL128X_MAJOR_MR_VER, +			      WL128X_SUBTYPE_MR_VER, WL128X_MINOR_MR_VER);  		break; -	case CHIP_ID_1283_PG10: +	case CHIP_ID_128X_PG10:  	default:  		wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);  		ret = -ENODEV;  		goto out;  	} +	/* common settings */ +	wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY; +	wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY; +	wl->sched_scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; +	wl->sched_scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; +	wl->max_channels_5 = WL12XX_MAX_CHANNELS_5GHZ;  out:  	return ret;  } @@ -1067,7 +1095,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)  	u32 clk;  	int selected_clock = -1; -	if (wl->chip.id == CHIP_ID_1283_PG20) { +	if (wl->chip.id == CHIP_ID_128X_PG20) {  		ret = wl128x_boot_clk(wl, &selected_clock);  		if (ret < 0)  			goto out; @@ -1098,7 +1126,7 @@ static int wl12xx_pre_boot(struct wl1271 *wl)  	wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); -	if (wl->chip.id == CHIP_ID_1283_PG20) +	if (wl->chip.id == CHIP_ID_128X_PG20)  		clk |= ((selected_clock & 0x3) << 1) << 4;  	else  		clk |= (priv->ref_clock << 1) << 4; @@ -1152,7 +1180,7 @@ static int wl12xx_pre_upload(struct wl1271 *wl)  	/* WL1271: The reference driver skips steps 7 to 10 (jumps directly  	 * to upload_fw) */ -	if (wl->chip.id == CHIP_ID_1283_PG20) { +	if (wl->chip.id == CHIP_ID_128X_PG20) {  		ret = wl12xx_top_reg_write(wl, SDIO_IO_DS, HCI_IO_DS_6MA);  		if (ret < 0)  			goto out; @@ -1219,6 +1247,23 @@ static int wl12xx_boot(struct wl1271 *wl)  	if (ret < 0)  		goto out; +	wl->event_mask = BSS_LOSE_EVENT_ID | +		REGAINED_BSS_EVENT_ID | +		SCAN_COMPLETE_EVENT_ID | +		ROLE_STOP_COMPLETE_EVENT_ID | +		RSSI_SNR_TRIGGER_0_EVENT_ID | +		PSPOLL_DELIVERY_FAILURE_EVENT_ID | +		SOFT_GEMINI_SENSE_EVENT_ID | +		PERIODIC_SCAN_REPORT_EVENT_ID | +		PERIODIC_SCAN_COMPLETE_EVENT_ID | +		DUMMY_PACKET_EVENT_ID | +		PEER_REMOVE_COMPLETE_EVENT_ID | +		BA_SESSION_RX_CONSTRAINT_EVENT_ID | +		REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | +		INACTIVE_STA_EVENT_ID | +		MAX_TX_RETRY_EVENT_ID | +		CHANNEL_SWITCH_COMPLETE_EVENT_ID; +  	ret = wlcore_boot_run_firmware(wl);  	if (ret < 0)  		goto out; @@ -1261,7 +1306,7 @@ static void  wl12xx_set_tx_desc_blocks(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,  			  u32 blks, u32 spare_blks)  { -	if (wl->chip.id == CHIP_ID_1283_PG20) { +	if (wl->chip.id == CHIP_ID_128X_PG20) {  		desc->wl128x_mem.total_mem_blocks = blks;  	} else {  		desc->wl127x_mem.extra_blocks = spare_blks; @@ -1275,7 +1320,7 @@ wl12xx_set_tx_desc_data_len(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc,  {  	u32 aligned_len = wlcore_calc_packet_alignment(wl, skb->len); -	if (wl->chip.id == CHIP_ID_1283_PG20) { +	if (wl->chip.id == CHIP_ID_128X_PG20) {  		desc->wl128x_mem.extra_bytes = aligned_len - skb->len;  		desc->length = cpu_to_le16(aligned_len >> 2); @@ -1339,7 +1384,7 @@ static int wl12xx_hw_init(struct wl1271 *wl)  {  	int ret; -	if (wl->chip.id == CHIP_ID_1283_PG20) { +	if (wl->chip.id == CHIP_ID_128X_PG20) {  		u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE;  		ret = wl128x_cmd_general_parms(wl); @@ -1394,22 +1439,6 @@ static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,  	return wlvif->rate_set;  } -static int wl12xx_identify_fw(struct wl1271 *wl) -{ -	unsigned int *fw_ver = wl->chip.fw_ver; - -	/* Only new station firmwares support routing fw logs to the host */ -	if ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) && -	    (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_FWLOG_STA_MIN)) -		wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; - -	/* This feature is not yet supported for AP mode */ -	if (fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) -		wl->quirks |= WLCORE_QUIRK_FWLOG_NOT_IMPLEMENTED; - -	return 0; -} -  static void wl12xx_conf_init(struct wl1271 *wl)  {  	struct wl12xx_priv *priv = wl->priv; @@ -1426,7 +1455,7 @@ static bool wl12xx_mac_in_fuse(struct wl1271 *wl)  	bool supported = false;  	u8 major, minor; -	if (wl->chip.id == CHIP_ID_1283_PG20) { +	if (wl->chip.id == CHIP_ID_128X_PG20) {  		major = WL128X_PG_GET_MAJOR(wl->hw_pg_ver);  		minor = WL128X_PG_GET_MINOR(wl->hw_pg_ver); @@ -1482,7 +1511,7 @@ static int wl12xx_get_pg_ver(struct wl1271 *wl, s8 *ver)  	u16 die_info;  	int ret; -	if (wl->chip.id == CHIP_ID_1283_PG20) +	if (wl->chip.id == CHIP_ID_128X_PG20)  		ret = wl12xx_top_reg_read(wl, WL128X_REG_FUSE_DATA_2_1,  					  &die_info);  	else @@ -1589,16 +1618,46 @@ static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,  	return wlcore_set_key(wl, cmd, vif, sta, key_conf);  } +static int wl12xx_set_peer_cap(struct wl1271 *wl, +			       struct ieee80211_sta_ht_cap *ht_cap, +			       bool allow_ht_operation, +			       u32 rate_set, u8 hlid) +{ +	return wl1271_acx_set_ht_capabilities(wl, ht_cap, allow_ht_operation, +					      hlid); +} + +static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, +				 struct wl1271_link *lnk) +{ +	u8 thold; + +	if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) +		thold = wl->conf.tx.fast_link_thold; +	else +		thold = wl->conf.tx.slow_link_thold; + +	return lnk->allocated_pkts < thold; +} + +static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, +				struct wl1271_link *lnk) +{ +	/* any link is good for low priority */ +	return true; +} +  static int wl12xx_setup(struct wl1271 *wl);  static struct wlcore_ops wl12xx_ops = {  	.setup			= wl12xx_setup,  	.identify_chip		= wl12xx_identify_chip, -	.identify_fw		= wl12xx_identify_fw,  	.boot			= wl12xx_boot,  	.plt_init		= wl12xx_plt_init,  	.trigger_cmd		= wl12xx_trigger_cmd,  	.ack_event		= wl12xx_ack_event, +	.wait_for_event		= wl12xx_wait_for_event, +	.process_mailbox_events	= wl12xx_process_mailbox_events,  	.calc_tx_blocks		= wl12xx_calc_tx_blocks,  	.set_tx_desc_blocks	= wl12xx_set_tx_desc_blocks,  	.set_tx_desc_data_len	= wl12xx_set_tx_desc_data_len, @@ -1615,9 +1674,17 @@ static struct wlcore_ops wl12xx_ops = {  	.set_rx_csum		= NULL,  	.ap_get_mimo_wide_rate_mask = NULL,  	.debugfs_init		= wl12xx_debugfs_add_files, +	.scan_start		= wl12xx_scan_start, +	.scan_stop		= wl12xx_scan_stop, +	.sched_scan_start	= wl12xx_sched_scan_start, +	.sched_scan_stop	= wl12xx_scan_sched_scan_stop,  	.get_spare_blocks	= wl12xx_get_spare_blocks,  	.set_key		= wl12xx_set_key, +	.channel_switch		= wl12xx_cmd_channel_switch,  	.pre_pkt_send		= NULL, +	.set_peer_cap		= wl12xx_set_peer_cap, +	.lnk_high_prio		= wl12xx_lnk_high_prio, +	.lnk_low_prio		= wl12xx_lnk_low_prio,  };  static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { @@ -1641,6 +1708,7 @@ static int wl12xx_setup(struct wl1271 *wl)  	wl->rtable = wl12xx_rtable;  	wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;  	wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS; +	wl->num_channels = 1;  	wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;  	wl->band_rate_to_idx = wl12xx_band_rate_to_idx;  	wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; @@ -1693,6 +1761,10 @@ static int wl12xx_setup(struct wl1271 *wl)  			wl1271_error("Invalid tcxo parameter %s", tcxo_param);  	} +	priv->rx_mem_addr = kmalloc(sizeof(*priv->rx_mem_addr), GFP_KERNEL); +	if (!priv->rx_mem_addr) +		return -ENOMEM; +  	return 0;  } @@ -1703,7 +1775,8 @@ static int wl12xx_probe(struct platform_device *pdev)  	int ret;  	hw = wlcore_alloc_hw(sizeof(struct wl12xx_priv), -			     WL12XX_AGGR_BUFFER_SIZE); +			     WL12XX_AGGR_BUFFER_SIZE, +			     sizeof(struct wl12xx_event_mailbox));  	if (IS_ERR(hw)) {  		wl1271_error("can't allocate hw");  		ret = PTR_ERR(hw); @@ -1725,6 +1798,21 @@ out:  	return ret;  } +static int wl12xx_remove(struct platform_device *pdev) +{ +	struct wl1271 *wl = platform_get_drvdata(pdev); +	struct wl12xx_priv *priv; + +	if (!wl) +		goto out; +	priv = wl->priv; + +	kfree(priv->rx_mem_addr); + +out: +	return wlcore_remove(pdev); +} +  static const struct platform_device_id wl12xx_id_table[] = {  	{ "wl12xx", 0 },  	{  } /* Terminating Entry */ @@ -1733,7 +1821,7 @@ MODULE_DEVICE_TABLE(platform, wl12xx_id_table);  static struct platform_driver wl12xx_driver = {  	.probe		= wl12xx_probe, -	.remove		= wlcore_remove, +	.remove		= wl12xx_remove,  	.id_table	= wl12xx_id_table,  	.driver = {  		.name	= "wl12xx_driver", diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c new file mode 100644 index 000000000000..affdb3ec6225 --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/scan.c @@ -0,0 +1,501 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/ieee80211.h> +#include "scan.h" +#include "../wlcore/debug.h" +#include "../wlcore/tx.h" + +static int wl1271_get_scan_channels(struct wl1271 *wl, +				    struct cfg80211_scan_request *req, +				    struct basic_scan_channel_params *channels, +				    enum ieee80211_band band, bool passive) +{ +	struct conf_scan_settings *c = &wl->conf.scan; +	int i, j; +	u32 flags; + +	for (i = 0, j = 0; +	     i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; +	     i++) { +		flags = req->channels[i]->flags; + +		if (!test_bit(i, wl->scan.scanned_ch) && +		    !(flags & IEEE80211_CHAN_DISABLED) && +		    (req->channels[i]->band == band) && +		    /* +		     * In passive scans, we scan all remaining +		     * channels, even if not marked as such. +		     * In active scans, we only scan channels not +		     * marked as passive. +		     */ +		    (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) { +			wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", +				     req->channels[i]->band, +				     req->channels[i]->center_freq); +			wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", +				     req->channels[i]->hw_value, +				     req->channels[i]->flags); +			wl1271_debug(DEBUG_SCAN, +				     "max_antenna_gain %d, max_power %d", +				     req->channels[i]->max_antenna_gain, +				     req->channels[i]->max_power); +			wl1271_debug(DEBUG_SCAN, "beacon_found %d", +				     req->channels[i]->beacon_found); + +			if (!passive) { +				channels[j].min_duration = +					cpu_to_le32(c->min_dwell_time_active); +				channels[j].max_duration = +					cpu_to_le32(c->max_dwell_time_active); +			} else { +				channels[j].min_duration = +					cpu_to_le32(c->dwell_time_passive); +				channels[j].max_duration = +					cpu_to_le32(c->dwell_time_passive); +			} +			channels[j].early_termination = 0; +			channels[j].tx_power_att = req->channels[i]->max_power; +			channels[j].channel = req->channels[i]->hw_value; + +			memset(&channels[j].bssid_lsb, 0xff, 4); +			memset(&channels[j].bssid_msb, 0xff, 2); + +			/* Mark the channels we already used */ +			set_bit(i, wl->scan.scanned_ch); + +			j++; +		} +	} + +	return j; +} + +#define WL1271_NOTHING_TO_SCAN 1 + +static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, +			    enum ieee80211_band band, +			    bool passive, u32 basic_rate) +{ +	struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); +	struct wl1271_cmd_scan *cmd; +	struct wl1271_cmd_trigger_scan_to *trigger; +	int ret; +	u16 scan_options = 0; + +	/* skip active scans if we don't have SSIDs */ +	if (!passive && wl->scan.req->n_ssids == 0) +		return WL1271_NOTHING_TO_SCAN; + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); +	if (!cmd || !trigger) { +		ret = -ENOMEM; +		goto out; +	} + +	if (wl->conf.scan.split_scan_timeout) +		scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; + +	if (passive) +		scan_options |= WL1271_SCAN_OPT_PASSIVE; + +	cmd->params.role_id = wlvif->role_id; + +	if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { +		ret = -EINVAL; +		goto out; +	} + +	cmd->params.scan_options = cpu_to_le16(scan_options); + +	cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, +						    cmd->channels, +						    band, passive); +	if (cmd->params.n_ch == 0) { +		ret = WL1271_NOTHING_TO_SCAN; +		goto out; +	} + +	cmd->params.tx_rate = cpu_to_le32(basic_rate); +	cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; +	cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; +	cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; + +	if (band == IEEE80211_BAND_2GHZ) +		cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; +	else +		cmd->params.band = WL1271_SCAN_BAND_5_GHZ; + +	if (wl->scan.ssid_len && wl->scan.ssid) { +		cmd->params.ssid_len = wl->scan.ssid_len; +		memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); +	} + +	memcpy(cmd->addr, vif->addr, ETH_ALEN); + +	ret = wl12xx_cmd_build_probe_req(wl, wlvif, +					 cmd->params.role_id, band, +					 wl->scan.ssid, wl->scan.ssid_len, +					 wl->scan.req->ie, +					 wl->scan.req->ie_len, false); +	if (ret < 0) { +		wl1271_error("PROBE request template failed"); +		goto out; +	} + +	trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); +	ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, +			      sizeof(*trigger), 0); +	if (ret < 0) { +		wl1271_error("trigger scan to failed for hw scan"); +		goto out; +	} + +	wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); + +	ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); +	if (ret < 0) { +		wl1271_error("SCAN failed"); +		goto out; +	} + +out: +	kfree(cmd); +	kfree(trigger); +	return ret; +} + +int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ +	struct wl1271_cmd_header *cmd = NULL; +	int ret = 0; + +	if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) +		return -EINVAL; + +	wl1271_debug(DEBUG_CMD, "cmd scan stop"); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (!cmd) { +		ret = -ENOMEM; +		goto out; +	} + +	ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, +			      sizeof(*cmd), 0); +	if (ret < 0) { +		wl1271_error("cmd stop_scan failed"); +		goto out; +	} +out: +	kfree(cmd); +	return ret; +} + +void wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ +	int ret = 0; +	enum ieee80211_band band; +	u32 rate, mask; + +	switch (wl->scan.state) { +	case WL1271_SCAN_STATE_IDLE: +		break; + +	case WL1271_SCAN_STATE_2GHZ_ACTIVE: +		band = IEEE80211_BAND_2GHZ; +		mask = wlvif->bitrate_masks[band]; +		if (wl->scan.req->no_cck) { +			mask &= ~CONF_TX_CCK_RATES; +			if (!mask) +				mask = CONF_TX_RATE_MASK_BASIC_P2P; +		} +		rate = wl1271_tx_min_rate_get(wl, mask); +		ret = wl1271_scan_send(wl, wlvif, band, false, rate); +		if (ret == WL1271_NOTHING_TO_SCAN) { +			wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; +			wl1271_scan_stm(wl, wlvif); +		} + +		break; + +	case WL1271_SCAN_STATE_2GHZ_PASSIVE: +		band = IEEE80211_BAND_2GHZ; +		mask = wlvif->bitrate_masks[band]; +		if (wl->scan.req->no_cck) { +			mask &= ~CONF_TX_CCK_RATES; +			if (!mask) +				mask = CONF_TX_RATE_MASK_BASIC_P2P; +		} +		rate = wl1271_tx_min_rate_get(wl, mask); +		ret = wl1271_scan_send(wl, wlvif, band, true, rate); +		if (ret == WL1271_NOTHING_TO_SCAN) { +			if (wl->enable_11a) +				wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; +			else +				wl->scan.state = WL1271_SCAN_STATE_DONE; +			wl1271_scan_stm(wl, wlvif); +		} + +		break; + +	case WL1271_SCAN_STATE_5GHZ_ACTIVE: +		band = IEEE80211_BAND_5GHZ; +		rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); +		ret = wl1271_scan_send(wl, wlvif, band, false, rate); +		if (ret == WL1271_NOTHING_TO_SCAN) { +			wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; +			wl1271_scan_stm(wl, wlvif); +		} + +		break; + +	case WL1271_SCAN_STATE_5GHZ_PASSIVE: +		band = IEEE80211_BAND_5GHZ; +		rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); +		ret = wl1271_scan_send(wl, wlvif, band, true, rate); +		if (ret == WL1271_NOTHING_TO_SCAN) { +			wl->scan.state = WL1271_SCAN_STATE_DONE; +			wl1271_scan_stm(wl, wlvif); +		} + +		break; + +	case WL1271_SCAN_STATE_DONE: +		wl->scan.failed = false; +		cancel_delayed_work(&wl->scan_complete_work); +		ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, +					     msecs_to_jiffies(0)); +		break; + +	default: +		wl1271_error("invalid scan state"); +		break; +	} + +	if (ret < 0) { +		cancel_delayed_work(&wl->scan_complete_work); +		ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, +					     msecs_to_jiffies(0)); +	} +} + +static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd, +				   struct wlcore_scan_channels *cmd_channels) +{ +	memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive)); +	memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active)); +	cmd->dfs = cmd_channels->dfs; +	cmd->n_pactive_ch = cmd_channels->passive_active; + +	memcpy(cmd->channels_2, cmd_channels->channels_2, +	       sizeof(cmd->channels_2)); +	memcpy(cmd->channels_5, cmd_channels->channels_5, +	       sizeof(cmd->channels_2)); +	/* channels_4 are not supported, so no need to copy them */ +} + +int wl1271_scan_sched_scan_config(struct wl1271 *wl, +				  struct wl12xx_vif *wlvif, +				  struct cfg80211_sched_scan_request *req, +				  struct ieee80211_sched_scan_ies *ies) +{ +	struct wl1271_cmd_sched_scan_config *cfg = NULL; +	struct wlcore_scan_channels *cfg_channels = NULL; +	struct conf_sched_scan_settings *c = &wl->conf.sched_scan; +	int i, ret; +	bool force_passive = !req->n_ssids; + +	wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); + +	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); +	if (!cfg) +		return -ENOMEM; + +	cfg->role_id = wlvif->role_id; +	cfg->rssi_threshold = c->rssi_threshold; +	cfg->snr_threshold  = c->snr_threshold; +	cfg->n_probe_reqs = c->num_probe_reqs; +	/* cycles set to 0 it means infinite (until manually stopped) */ +	cfg->cycles = 0; +	/* report APs when at least 1 is found */ +	cfg->report_after = 1; +	/* don't stop scanning automatically when something is found */ +	cfg->terminate = 0; +	cfg->tag = WL1271_SCAN_DEFAULT_TAG; +	/* don't filter on BSS type */ +	cfg->bss_type = SCAN_BSS_TYPE_ANY; +	/* currently NL80211 supports only a single interval */ +	for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) +		cfg->intervals[i] = cpu_to_le32(req->interval); + +	cfg->ssid_len = 0; +	ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); +	if (ret < 0) +		goto out; + +	cfg->filter_type = ret; + +	wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); + +	cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL); +	if (!cfg_channels) { +		ret = -ENOMEM; +		goto out; +	} + +	if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels, +					 req->n_channels, req->n_ssids, +					 SCAN_TYPE_PERIODIC)) { +		wl1271_error("scan channel list is empty"); +		ret = -EINVAL; +		goto out; +	} +	wl12xx_adjust_channels(cfg, cfg_channels); + +	if (!force_passive && cfg->active[0]) { +		u8 band = IEEE80211_BAND_2GHZ; +		ret = wl12xx_cmd_build_probe_req(wl, wlvif, +						 wlvif->role_id, band, +						 req->ssids[0].ssid, +						 req->ssids[0].ssid_len, +						 ies->ie[band], +						 ies->len[band], true); +		if (ret < 0) { +			wl1271_error("2.4GHz PROBE request template failed"); +			goto out; +		} +	} + +	if (!force_passive && cfg->active[1]) { +		u8 band = IEEE80211_BAND_5GHZ; +		ret = wl12xx_cmd_build_probe_req(wl, wlvif, +						 wlvif->role_id, band, +						 req->ssids[0].ssid, +						 req->ssids[0].ssid_len, +						 ies->ie[band], +						 ies->len[band], true); +		if (ret < 0) { +			wl1271_error("5GHz PROBE request template failed"); +			goto out; +		} +	} + +	wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); + +	ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, +			      sizeof(*cfg), 0); +	if (ret < 0) { +		wl1271_error("SCAN configuration failed"); +		goto out; +	} +out: +	kfree(cfg_channels); +	kfree(cfg); +	return ret; +} + +int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ +	struct wl1271_cmd_sched_scan_start *start; +	int ret = 0; + +	wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); + +	if (wlvif->bss_type != BSS_TYPE_STA_BSS) +		return -EOPNOTSUPP; + +	if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) && +	    test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) +		return -EBUSY; + +	start = kzalloc(sizeof(*start), GFP_KERNEL); +	if (!start) +		return -ENOMEM; + +	start->role_id = wlvif->role_id; +	start->tag = WL1271_SCAN_DEFAULT_TAG; + +	ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, +			      sizeof(*start), 0); +	if (ret < 0) { +		wl1271_error("failed to send scan start command"); +		goto out_free; +	} + +out_free: +	kfree(start); +	return ret; +} + +int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif, +			    struct cfg80211_sched_scan_request *req, +			    struct ieee80211_sched_scan_ies *ies) +{ +	int ret; + +	ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies); +	if (ret < 0) +		return ret; + +	return wl1271_scan_sched_scan_start(wl, wlvif); +} + +void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif) +{ +	struct wl1271_cmd_sched_scan_stop *stop; +	int ret = 0; + +	wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); + +	/* FIXME: what to do if alloc'ing to stop fails? */ +	stop = kzalloc(sizeof(*stop), GFP_KERNEL); +	if (!stop) { +		wl1271_error("failed to alloc memory to send sched scan stop"); +		return; +	} + +	stop->role_id = wlvif->role_id; +	stop->tag = WL1271_SCAN_DEFAULT_TAG; + +	ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, +			      sizeof(*stop), 0); +	if (ret < 0) { +		wl1271_error("failed to send sched scan stop command"); +		goto out_free; +	} + +out_free: +	kfree(stop); +} + +int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, +		      struct cfg80211_scan_request *req) +{ +	wl1271_scan_stm(wl, wlvif); +	return 0; +} + +void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ +	wl1271_scan_stm(wl, wlvif); +} diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h new file mode 100644 index 000000000000..264af7ac2785 --- /dev/null +++ b/drivers/net/wireless/ti/wl12xx/scan.h @@ -0,0 +1,140 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2012 Texas Instruments. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL12XX_SCAN_H__ +#define __WL12XX_SCAN_H__ + +#include "../wlcore/wlcore.h" +#include "../wlcore/cmd.h" +#include "../wlcore/scan.h" + +#define WL12XX_MAX_CHANNELS_5GHZ 23 + +struct basic_scan_params { +	/* Scan option flags (WL1271_SCAN_OPT_*) */ +	__le16 scan_options; +	u8 role_id; +	/* Number of scan channels in the list (maximum 30) */ +	u8 n_ch; +	/* This field indicates the number of probe requests to send +	   per channel for an active scan */ +	u8 n_probe_reqs; +	u8 tid_trigger; +	u8 ssid_len; +	u8 use_ssid_list; + +	/* Rate bit field for sending the probes */ +	__le32 tx_rate; + +	u8 ssid[IEEE80211_MAX_SSID_LEN]; +	/* Band to scan */ +	u8 band; + +	u8 scan_tag; +	u8 padding2[2]; +} __packed; + +struct basic_scan_channel_params { +	/* Duration in TU to wait for frames on a channel for active scan */ +	__le32 min_duration; +	__le32 max_duration; +	__le32 bssid_lsb; +	__le16 bssid_msb; +	u8 early_termination; +	u8 tx_power_att; +	u8 channel; +	/* FW internal use only! */ +	u8 dfs_candidate; +	u8 activity_detected; +	u8 pad; +} __packed; + +struct wl1271_cmd_scan { +	struct wl1271_cmd_header header; + +	struct basic_scan_params params; +	struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS]; + +	/* src mac address */ +	u8 addr[ETH_ALEN]; +	u8 padding[2]; +} __packed; + +struct wl1271_cmd_sched_scan_config { +	struct wl1271_cmd_header header; + +	__le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; + +	s8 rssi_threshold; /* for filtering (in dBm) */ +	s8 snr_threshold;  /* for filtering (in dB) */ + +	u8 cycles;       /* maximum number of scan cycles */ +	u8 report_after; /* report when this number of results are received */ +	u8 terminate;    /* stop scanning after reporting */ + +	u8 tag; +	u8 bss_type; /* for filtering */ +	u8 filter_type; + +	u8 ssid_len;     /* For SCAN_SSID_FILTER_SPECIFIC */ +	u8 ssid[IEEE80211_MAX_SSID_LEN]; + +	u8 n_probe_reqs; /* Number of probes requests per channel */ + +	u8 passive[SCAN_MAX_BANDS]; +	u8 active[SCAN_MAX_BANDS]; + +	u8 dfs; + +	u8 n_pactive_ch; /* number of pactive (passive until fw detects energy) +			    channels in BG band */ +	u8 role_id; +	u8 padding[1]; +	struct conn_scan_ch_params channels_2[MAX_CHANNELS_2GHZ]; +	struct conn_scan_ch_params channels_5[WL12XX_MAX_CHANNELS_5GHZ]; +	struct conn_scan_ch_params channels_4[MAX_CHANNELS_4GHZ]; +} __packed; + +struct wl1271_cmd_sched_scan_start { +	struct wl1271_cmd_header header; + +	u8 tag; +	u8 role_id; +	u8 padding[2]; +} __packed; + +struct wl1271_cmd_sched_scan_stop { +	struct wl1271_cmd_header header; + +	u8 tag; +	u8 role_id; +	u8 padding[2]; +} __packed; + +int wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, +		      struct cfg80211_scan_request *req); +int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif); +void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif); +int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif  *wlvif, +			    struct cfg80211_sched_scan_request *req, +			    struct ieee80211_sched_scan_ies *ies); +void wl12xx_scan_sched_scan_stop(struct wl1271 *wl,  struct wl12xx_vif *wlvif); +#endif diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 7182bbf6625d..d4552857480c 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -24,19 +24,37 @@  #include "conf.h" -/* minimum FW required for driver for wl127x */ +/* WiLink 6/7 chip IDs */ +#define CHIP_ID_127X_PG10              (0x04030101) +#define CHIP_ID_127X_PG20              (0x04030111) +#define CHIP_ID_128X_PG10              (0x05030101) +#define CHIP_ID_128X_PG20              (0x05030111) + +/* FW chip version for wl127x */  #define WL127X_CHIP_VER		6 -#define WL127X_IFTYPE_VER	3 -#define WL127X_MAJOR_VER	10 -#define WL127X_SUBTYPE_VER	2 -#define WL127X_MINOR_VER	115 +/* minimum single-role FW version for wl127x */ +#define WL127X_IFTYPE_SR_VER	3 +#define WL127X_MAJOR_SR_VER	10 +#define WL127X_SUBTYPE_SR_VER	WLCORE_FW_VER_IGNORE +#define WL127X_MINOR_SR_VER	115 +/* minimum multi-role FW version for wl127x */ +#define WL127X_IFTYPE_MR_VER	5 +#define WL127X_MAJOR_MR_VER	7 +#define WL127X_SUBTYPE_MR_VER	WLCORE_FW_VER_IGNORE +#define WL127X_MINOR_MR_VER	115 -/* minimum FW required for driver for wl128x */ +/* FW chip version for wl128x */  #define WL128X_CHIP_VER		7 -#define WL128X_IFTYPE_VER	3 -#define WL128X_MAJOR_VER	10 -#define WL128X_SUBTYPE_VER	2 -#define WL128X_MINOR_VER	115 +/* minimum single-role FW version for wl128x */ +#define WL128X_IFTYPE_SR_VER	3 +#define WL128X_MAJOR_SR_VER	10 +#define WL128X_SUBTYPE_SR_VER	WLCORE_FW_VER_IGNORE +#define WL128X_MINOR_SR_VER	115 +/* minimum multi-role FW version for wl128x */ +#define WL128X_IFTYPE_MR_VER	5 +#define WL128X_MAJOR_MR_VER	7 +#define WL128X_SUBTYPE_MR_VER	WLCORE_FW_VER_IGNORE +#define WL128X_MINOR_MR_VER	42  #define WL12XX_AGGR_BUFFER_SIZE	(4 * PAGE_SIZE) @@ -55,6 +73,8 @@ struct wl12xx_priv {  	int ref_clock;  	int tcxo_clock; + +	struct wl127x_rx_mem_pool_addr *rx_mem_addr;  };  #endif /* __WL12XX_PRIV_H__ */ | 
