diff options
Diffstat (limited to 'drivers/net/wireless/mwl8k.c')
| -rw-r--r-- | drivers/net/wireless/mwl8k.c | 2280 | 
1 files changed, 2019 insertions, 261 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 9ecf8407cb1..3c0a0a86ba1 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -9,7 +9,7 @@   * warranty of any kind, whether express or implied.   */ -#include <linux/init.h> +#include <linux/interrupt.h>  #include <linux/module.h>  #include <linux/kernel.h>  #include <linux/sched.h> @@ -27,10 +27,10 @@  #define MWL8K_DESC	"Marvell TOPDOG(R) 802.11 Wireless Network Driver"  #define MWL8K_NAME	KBUILD_MODNAME -#define MWL8K_VERSION	"0.12" +#define MWL8K_VERSION	"0.13"  /* Module parameters */ -static unsigned ap_mode_default; +static bool ap_mode_default;  module_param(ap_mode_default, bool, 0);  MODULE_PARM_DESC(ap_mode_default,  		 "Set to 1 to make ap mode the default instead of sta mode"); @@ -63,6 +63,7 @@ MODULE_PARM_DESC(ap_mode_default,  #define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL	0x00000c38  #define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK	0x00000c3c  #define  MWL8K_A2H_INT_DUMMY			 (1 << 20) +#define  MWL8K_A2H_INT_BA_WATCHDOG		 (1 << 14)  #define  MWL8K_A2H_INT_CHNL_SWITCHED		 (1 << 11)  #define  MWL8K_A2H_INT_QUEUE_EMPTY		 (1 << 10)  #define  MWL8K_A2H_INT_RADAR_DETECT		 (1 << 7) @@ -73,6 +74,17 @@ MODULE_PARM_DESC(ap_mode_default,  #define  MWL8K_A2H_INT_RX_READY			 (1 << 1)  #define  MWL8K_A2H_INT_TX_DONE			 (1 << 0) +/* HW micro second timer register + * located at offset 0xA600. This + * will be used to timestamp tx + * packets. + */ + +#define	MWL8K_HW_TIMER_REGISTER			0x0000a600 +#define BBU_RXRDY_CNT_REG			0x0000a860 +#define NOK_CCA_CNT_REG				0x0000a6a0 +#define BBU_AVG_NOISE_VAL			0x67 +  #define MWL8K_A2H_EVENTS	(MWL8K_A2H_INT_DUMMY | \  				 MWL8K_A2H_INT_CHNL_SWITCHED | \  				 MWL8K_A2H_INT_QUEUE_EMPTY | \ @@ -82,10 +94,28 @@ MODULE_PARM_DESC(ap_mode_default,  				 MWL8K_A2H_INT_MAC_EVENT | \  				 MWL8K_A2H_INT_OPC_DONE | \  				 MWL8K_A2H_INT_RX_READY | \ -				 MWL8K_A2H_INT_TX_DONE) +				 MWL8K_A2H_INT_TX_DONE | \ +				 MWL8K_A2H_INT_BA_WATCHDOG)  #define MWL8K_RX_QUEUES		1 -#define MWL8K_TX_QUEUES		4 +#define MWL8K_TX_WMM_QUEUES	4 +#define MWL8K_MAX_AMPDU_QUEUES	8 +#define MWL8K_MAX_TX_QUEUES	(MWL8K_TX_WMM_QUEUES + MWL8K_MAX_AMPDU_QUEUES) +#define mwl8k_tx_queues(priv)	(MWL8K_TX_WMM_QUEUES + (priv)->num_ampdu_queues) + +/* txpriorities are mapped with hw queues. + * Each hw queue has a txpriority. + */ +#define TOTAL_HW_TX_QUEUES	8 + +/* Each HW queue can have one AMPDU stream. + * But, because one of the hw queue is reserved, + * maximum AMPDU queues that can be created are + * one short of total tx queues. + */ +#define MWL8K_NUM_AMPDU_STREAMS	(TOTAL_HW_TX_QUEUES - 1) + +#define MWL8K_NUM_CHANS 18  struct rxd_ops {  	int rxd_size; @@ -134,9 +164,24 @@ struct mwl8k_tx_queue {  	struct sk_buff **skb;  }; +enum { +	AMPDU_NO_STREAM, +	AMPDU_STREAM_NEW, +	AMPDU_STREAM_IN_PROGRESS, +	AMPDU_STREAM_ACTIVE, +}; + +struct mwl8k_ampdu_stream { +	struct ieee80211_sta *sta; +	u8 tid; +	u8 state; +	u8 idx; +}; +  struct mwl8k_priv {  	struct ieee80211_hw *hw;  	struct pci_dev *pdev; +	int irq;  	struct mwl8k_device_info *device_info; @@ -152,19 +197,28 @@ struct mwl8k_priv {  	struct rxd_ops *rxd_ops;  	struct ieee80211_supported_band band_24;  	struct ieee80211_channel channels_24[14]; -	struct ieee80211_rate rates_24[14]; +	struct ieee80211_rate rates_24[13];  	struct ieee80211_supported_band band_50;  	struct ieee80211_channel channels_50[4]; -	struct ieee80211_rate rates_50[9]; +	struct ieee80211_rate rates_50[8];  	u32 ap_macids_supported;  	u32 sta_macids_supported; +	/* Ampdu stream information */ +	u8 num_ampdu_queues; +	spinlock_t stream_lock; +	struct mwl8k_ampdu_stream ampdu[MWL8K_MAX_AMPDU_QUEUES]; +	struct work_struct watchdog_ba_handle; +  	/* firmware access */  	struct mutex fw_mutex;  	struct task_struct *fw_mutex_owner; +	struct task_struct *hw_restart_owner;  	int fw_mutex_depth;  	struct completion *hostcmd_wait; +	atomic_t watchdog_event_pending; +  	/* lock held over TX and TX reap */  	spinlock_t tx_lock; @@ -182,6 +236,7 @@ struct mwl8k_priv {  	u16 num_mcaddrs;  	u8 hw_rev;  	u32 fw_rev; +	u32 caps;  	/*  	 * Running count of TX packets in flight, to avoid @@ -190,7 +245,8 @@ struct mwl8k_priv {  	int pending_tx_pkts;  	struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; -	struct mwl8k_tx_queue txq[MWL8K_TX_QUEUES]; +	struct mwl8k_tx_queue txq[MWL8K_MAX_TX_QUEUES]; +	u32 txq_offset[MWL8K_MAX_TX_QUEUES];  	bool radio_on;  	bool radio_short_preamble; @@ -223,15 +279,32 @@ struct mwl8k_priv {  	 * preserve the queue configurations so they can be restored if/when  	 * the firmware image is swapped.  	 */ -	struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_QUEUES]; +	struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_WMM_QUEUES]; + +	/* To perform the task of reloading the firmware */ +	struct work_struct fw_reload; +	bool hw_restart_in_progress;  	/* async firmware loading state */  	unsigned fw_state;  	char *fw_pref;  	char *fw_alt; +	bool is_8764;  	struct completion firmware_loading_complete; + +	/* bitmap of running BSSes */ +	u32 running_bsses; + +	/* ACS related */ +	bool sw_scan_start; +	struct ieee80211_channel *acs_chan; +	unsigned long channel_time; +	struct survey_info survey[MWL8K_NUM_CHANS];  }; +#define MAX_WEP_KEY_LEN         13 +#define NUM_WEP_KEYS            4 +  /* Per interface specific private data */  struct mwl8k_vif {  	struct list_head list; @@ -242,30 +315,51 @@ struct mwl8k_vif {  	/* Non AMPDU sequence number assigned by driver.  */  	u16 seqno; + +	/* Saved WEP keys */ +	struct { +		u8 enabled; +		u8 key[sizeof(struct ieee80211_key_conf) + MAX_WEP_KEY_LEN]; +	} wep_key_conf[NUM_WEP_KEYS]; + +	/* BSSID */ +	u8 bssid[ETH_ALEN]; + +	/* A flag to indicate is HW crypto is enabled for this bssid */ +	bool is_hw_crypto_enabled;  };  #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) +#define IEEE80211_KEY_CONF(_u8) ((struct ieee80211_key_conf *)(_u8)) +struct tx_traffic_info { +	u32 start_time; +	u32 pkts; +}; + +#define MWL8K_MAX_TID 8  struct mwl8k_sta {  	/* Index into station database. Returned by UPDATE_STADB.  */  	u8 peer_id; +	u8 is_ampdu_allowed; +	struct tx_traffic_info tx_stats[MWL8K_MAX_TID];  };  #define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv))  static const struct ieee80211_channel mwl8k_channels_24[] = { -	{ .center_freq = 2412, .hw_value = 1, }, -	{ .center_freq = 2417, .hw_value = 2, }, -	{ .center_freq = 2422, .hw_value = 3, }, -	{ .center_freq = 2427, .hw_value = 4, }, -	{ .center_freq = 2432, .hw_value = 5, }, -	{ .center_freq = 2437, .hw_value = 6, }, -	{ .center_freq = 2442, .hw_value = 7, }, -	{ .center_freq = 2447, .hw_value = 8, }, -	{ .center_freq = 2452, .hw_value = 9, }, -	{ .center_freq = 2457, .hw_value = 10, }, -	{ .center_freq = 2462, .hw_value = 11, }, -	{ .center_freq = 2467, .hw_value = 12, }, -	{ .center_freq = 2472, .hw_value = 13, }, -	{ .center_freq = 2484, .hw_value = 14, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2412, .hw_value = 1, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2417, .hw_value = 2, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2422, .hw_value = 3, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2427, .hw_value = 4, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2432, .hw_value = 5, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2437, .hw_value = 6, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2442, .hw_value = 7, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2447, .hw_value = 8, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2452, .hw_value = 9, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2457, .hw_value = 10, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2462, .hw_value = 11, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2467, .hw_value = 12, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2472, .hw_value = 13, }, +	{ .band = IEEE80211_BAND_2GHZ, .center_freq = 2484, .hw_value = 14, },  };  static const struct ieee80211_rate mwl8k_rates_24[] = { @@ -282,14 +376,13 @@ static const struct ieee80211_rate mwl8k_rates_24[] = {  	{ .bitrate = 360, .hw_value = 72, },  	{ .bitrate = 480, .hw_value = 96, },  	{ .bitrate = 540, .hw_value = 108, }, -	{ .bitrate = 720, .hw_value = 144, },  };  static const struct ieee80211_channel mwl8k_channels_50[] = { -	{ .center_freq = 5180, .hw_value = 36, }, -	{ .center_freq = 5200, .hw_value = 40, }, -	{ .center_freq = 5220, .hw_value = 44, }, -	{ .center_freq = 5240, .hw_value = 48, }, +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5180, .hw_value = 36, }, +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5200, .hw_value = 40, }, +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5220, .hw_value = 44, }, +	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5240, .hw_value = 48, },  };  static const struct ieee80211_rate mwl8k_rates_50[] = { @@ -301,7 +394,6 @@ static const struct ieee80211_rate mwl8k_rates_50[] = {  	{ .bitrate = 360, .hw_value = 72, },  	{ .bitrate = 480, .hw_value = 96, },  	{ .bitrate = 540, .hw_value = 108, }, -	{ .bitrate = 720, .hw_value = 144, },  };  /* Set or get info from Firmware */ @@ -315,6 +407,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = {  #define MWL8K_CMD_SET_HW_SPEC		0x0004  #define MWL8K_CMD_MAC_MULTICAST_ADR	0x0010  #define MWL8K_CMD_GET_STAT		0x0014 +#define MWL8K_CMD_BBP_REG_ACCESS	0x001a  #define MWL8K_CMD_RADIO_CONTROL		0x001c  #define MWL8K_CMD_RF_TX_POWER		0x001e  #define MWL8K_CMD_TX_POWER		0x001f @@ -335,9 +428,13 @@ static const struct ieee80211_rate mwl8k_rates_50[] = {  #define MWL8K_CMD_ENABLE_SNIFFER	0x0150  #define MWL8K_CMD_SET_MAC_ADDR		0x0202		/* per-vif */  #define MWL8K_CMD_SET_RATEADAPT_MODE	0x0203 +#define MWL8K_CMD_GET_WATCHDOG_BITMAP	0x0205 +#define MWL8K_CMD_DEL_MAC_ADDR		0x0206		/* per-vif */  #define MWL8K_CMD_BSS_START		0x1100		/* per-vif */  #define MWL8K_CMD_SET_NEW_STN		0x1111		/* per-vif */ +#define MWL8K_CMD_UPDATE_ENCRYPTION	0x1122		/* per-vif */  #define MWL8K_CMD_UPDATE_STADB		0x1123 +#define MWL8K_CMD_BASTREAM		0x1125  static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)  { @@ -375,7 +472,10 @@ static const char *mwl8k_cmd_name(__le16 cmd, char *buf, int bufsize)  		MWL8K_CMDNAME(SET_RATEADAPT_MODE);  		MWL8K_CMDNAME(BSS_START);  		MWL8K_CMDNAME(SET_NEW_STN); +		MWL8K_CMDNAME(UPDATE_ENCRYPTION);  		MWL8K_CMDNAME(UPDATE_STADB); +		MWL8K_CMDNAME(BASTREAM); +		MWL8K_CMDNAME(GET_WATCHDOG_BITMAP);  	default:  		snprintf(buf, bufsize, "0x%x", cmd);  	} @@ -511,13 +611,18 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length)  	loops = 1000;  	do {  		u32 int_code; - -		int_code = ioread32(regs + MWL8K_HIU_INT_CODE); -		if (int_code == MWL8K_INT_CODE_CMD_FINISHED) { -			iowrite32(0, regs + MWL8K_HIU_INT_CODE); -			break; +		if (priv->is_8764) { +			int_code = ioread32(regs + +					    MWL8K_HIU_H2A_INTERRUPT_STATUS); +			if (int_code == 0) +				break; +		} else { +			int_code = ioread32(regs + MWL8K_HIU_INT_CODE); +			if (int_code == MWL8K_INT_CODE_CMD_FINISHED) { +				iowrite32(0, regs + MWL8K_HIU_INT_CODE); +				break; +			}  		} -  		cond_resched();  		udelay(1);  	} while (--loops); @@ -635,7 +740,7 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw)  	int rc;  	int loops; -	if (!memcmp(fw->data, "\x01\x00\x00\x00", 4)) { +	if (!memcmp(fw->data, "\x01\x00\x00\x00", 4) && !priv->is_8764) {  		const struct firmware *helper = priv->fw_helper;  		if (helper == NULL) { @@ -650,11 +755,14 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw)  			       "helper image\n", pci_name(priv->pdev));  			return rc;  		} -		msleep(5); +		msleep(20);  		rc = mwl8k_feed_fw_image(priv, fw->data, fw->size);  	} else { -		rc = mwl8k_load_fw_image(priv, fw->data, fw->size); +		if (priv->is_8764) +			rc = mwl8k_feed_fw_image(priv, fw->data, fw->size); +		else +			rc = mwl8k_load_fw_image(priv, fw->data, fw->size);  	}  	if (rc) { @@ -671,10 +779,10 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw)  		ready_code = ioread32(priv->regs + MWL8K_HIU_INT_CODE);  		if (ready_code == MWL8K_FWAP_READY) { -			priv->ap_fw = 1; +			priv->ap_fw = true;  			break;  		} else if (ready_code == MWL8K_FWSTA_READY) { -			priv->ap_fw = 0; +			priv->ap_fw = false;  			break;  		} @@ -715,10 +823,15 @@ static inline void mwl8k_remove_dma_header(struct sk_buff *skb, __le16 qos)  		skb_pull(skb, sizeof(*tr) - hdrlen);  } -static inline void mwl8k_add_dma_header(struct sk_buff *skb) +#define REDUCED_TX_HEADROOM	8 + +static void +mwl8k_add_dma_header(struct mwl8k_priv *priv, struct sk_buff *skb, +						int head_pad, int tail_pad)  {  	struct ieee80211_hdr *wh;  	int hdrlen; +	int reqd_hdrlen;  	struct mwl8k_dma_data *tr;  	/* @@ -730,11 +843,29 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb)  	wh = (struct ieee80211_hdr *)skb->data;  	hdrlen = ieee80211_hdrlen(wh->frame_control); -	if (hdrlen != sizeof(*tr)) -		skb_push(skb, sizeof(*tr) - hdrlen); + +	/* +	 * Check if skb_resize is required because of +	 * tx_headroom adjustment. +	 */ +	if (priv->ap_fw && (hdrlen < (sizeof(struct ieee80211_cts) +						+ REDUCED_TX_HEADROOM))) { +		if (pskb_expand_head(skb, REDUCED_TX_HEADROOM, 0, GFP_ATOMIC)) { + +			wiphy_err(priv->hw->wiphy, +					"Failed to reallocate TX buffer\n"); +			return; +		} +		skb->truesize += REDUCED_TX_HEADROOM; +	} + +	reqd_hdrlen = sizeof(*tr) + head_pad; + +	if (hdrlen != reqd_hdrlen) +		skb_push(skb, reqd_hdrlen - hdrlen);  	if (ieee80211_is_data_qos(wh->frame_control)) -		hdrlen -= 2; +		hdrlen -= IEEE80211_QOS_CTL_LEN;  	tr = (struct mwl8k_dma_data *)skb->data;  	if (wh != &tr->wh) @@ -747,14 +878,58 @@ static inline void mwl8k_add_dma_header(struct sk_buff *skb)  	 * payload".  That is, everything except for the 802.11 header.  	 * This includes all crypto material including the MIC.  	 */ -	tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr)); +	tr->fwlen = cpu_to_le16(skb->len - sizeof(*tr) + tail_pad);  } +static void mwl8k_encapsulate_tx_frame(struct mwl8k_priv *priv, +		struct sk_buff *skb) +{ +	struct ieee80211_hdr *wh; +	struct ieee80211_tx_info *tx_info; +	struct ieee80211_key_conf *key_conf; +	int data_pad; +	int head_pad = 0; + +	wh = (struct ieee80211_hdr *)skb->data; + +	tx_info = IEEE80211_SKB_CB(skb); + +	key_conf = NULL; +	if (ieee80211_is_data(wh->frame_control)) +		key_conf = tx_info->control.hw_key; + +	/* +	 * Make sure the packet header is in the DMA header format (4-address +	 * without QoS), and add head & tail padding when HW crypto is enabled. +	 * +	 * We have the following trailer padding requirements: +	 * - WEP: 4 trailer bytes (ICV) +	 * - TKIP: 12 trailer bytes (8 MIC + 4 ICV) +	 * - CCMP: 8 trailer bytes (MIC) +	 */ +	data_pad = 0; +	if (key_conf != NULL) { +		head_pad = key_conf->iv_len; +		switch (key_conf->cipher) { +		case WLAN_CIPHER_SUITE_WEP40: +		case WLAN_CIPHER_SUITE_WEP104: +			data_pad = 4; +			break; +		case WLAN_CIPHER_SUITE_TKIP: +			data_pad = 12; +			break; +		case WLAN_CIPHER_SUITE_CCMP: +			data_pad = 8; +			break; +		} +	} +	mwl8k_add_dma_header(priv, skb, head_pad, data_pad); +}  /* - * Packet reception for 88w8366 AP firmware. + * Packet reception for 88w8366/88w8764 AP firmware.   */ -struct mwl8k_rxd_8366_ap { +struct mwl8k_rxd_ap {  	__le16 pkt_len;  	__u8 sq2;  	__u8 rate; @@ -772,23 +947,30 @@ struct mwl8k_rxd_8366_ap {  	__u8 rx_ctrl;  } __packed; -#define MWL8K_8366_AP_RATE_INFO_MCS_FORMAT	0x80 -#define MWL8K_8366_AP_RATE_INFO_40MHZ		0x40 -#define MWL8K_8366_AP_RATE_INFO_RATEID(x)	((x) & 0x3f) +#define MWL8K_AP_RATE_INFO_MCS_FORMAT		0x80 +#define MWL8K_AP_RATE_INFO_40MHZ		0x40 +#define MWL8K_AP_RATE_INFO_RATEID(x)		((x) & 0x3f) -#define MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST	0x80 +#define MWL8K_AP_RX_CTRL_OWNED_BY_HOST		0x80 -static void mwl8k_rxd_8366_ap_init(void *_rxd, dma_addr_t next_dma_addr) +/* 8366/8764 AP rx_status bits */ +#define MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK		0x80 +#define MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR		0xFF +#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR		0x02 +#define MWL8K_AP_RXSTAT_WEP_DECRYPT_ICV_ERR		0x04 +#define MWL8K_AP_RXSTAT_TKIP_DECRYPT_ICV_ERR		0x08 + +static void mwl8k_rxd_ap_init(void *_rxd, dma_addr_t next_dma_addr)  { -	struct mwl8k_rxd_8366_ap *rxd = _rxd; +	struct mwl8k_rxd_ap *rxd = _rxd;  	rxd->next_rxd_phys_addr = cpu_to_le32(next_dma_addr); -	rxd->rx_ctrl = MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST; +	rxd->rx_ctrl = MWL8K_AP_RX_CTRL_OWNED_BY_HOST;  } -static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len) +static void mwl8k_rxd_ap_refill(void *_rxd, dma_addr_t addr, int len)  { -	struct mwl8k_rxd_8366_ap *rxd = _rxd; +	struct mwl8k_rxd_ap *rxd = _rxd;  	rxd->pkt_len = cpu_to_le16(len);  	rxd->pkt_phys_addr = cpu_to_le32(addr); @@ -797,12 +979,12 @@ static void mwl8k_rxd_8366_ap_refill(void *_rxd, dma_addr_t addr, int len)  }  static int -mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, -			  __le16 *qos, s8 *noise) +mwl8k_rxd_ap_process(void *_rxd, struct ieee80211_rx_status *status, +		     __le16 *qos, s8 *noise)  { -	struct mwl8k_rxd_8366_ap *rxd = _rxd; +	struct mwl8k_rxd_ap *rxd = _rxd; -	if (!(rxd->rx_ctrl & MWL8K_8366_AP_RX_CTRL_OWNED_BY_HOST)) +	if (!(rxd->rx_ctrl & MWL8K_AP_RX_CTRL_OWNED_BY_HOST))  		return -1;  	rmb(); @@ -811,11 +993,11 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,  	status->signal = -rxd->rssi;  	*noise = -rxd->noise_floor; -	if (rxd->rate & MWL8K_8366_AP_RATE_INFO_MCS_FORMAT) { +	if (rxd->rate & MWL8K_AP_RATE_INFO_MCS_FORMAT) {  		status->flag |= RX_FLAG_HT; -		if (rxd->rate & MWL8K_8366_AP_RATE_INFO_40MHZ) +		if (rxd->rate & MWL8K_AP_RATE_INFO_40MHZ)  			status->flag |= RX_FLAG_40MHZ; -		status->rate_idx = MWL8K_8366_AP_RATE_INFO_RATEID(rxd->rate); +		status->rate_idx = MWL8K_AP_RATE_INFO_RATEID(rxd->rate);  	} else {  		int i; @@ -834,18 +1016,24 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status,  	} else {  		status->band = IEEE80211_BAND_2GHZ;  	} -	status->freq = ieee80211_channel_to_frequency(rxd->channel); +	status->freq = ieee80211_channel_to_frequency(rxd->channel, +						      status->band);  	*qos = rxd->qos_control; +	if ((rxd->rx_status != MWL8K_AP_RXSTAT_GENERAL_DECRYPT_ERR) && +	    (rxd->rx_status & MWL8K_AP_RXSTAT_DECRYPT_ERR_MASK) && +	    (rxd->rx_status & MWL8K_AP_RXSTAT_TKIP_DECRYPT_MIC_ERR)) +		status->flag |= RX_FLAG_MMIC_ERROR; +  	return le16_to_cpu(rxd->pkt_len);  } -static struct rxd_ops rxd_8366_ap_ops = { -	.rxd_size	= sizeof(struct mwl8k_rxd_8366_ap), -	.rxd_init	= mwl8k_rxd_8366_ap_init, -	.rxd_refill	= mwl8k_rxd_8366_ap_refill, -	.rxd_process	= mwl8k_rxd_8366_ap_process, +static struct rxd_ops rxd_ap_ops = { +	.rxd_size	= sizeof(struct mwl8k_rxd_ap), +	.rxd_init	= mwl8k_rxd_ap_init, +	.rxd_refill	= mwl8k_rxd_ap_refill, +	.rxd_process	= mwl8k_rxd_ap_process,  };  /* @@ -876,6 +1064,11 @@ struct mwl8k_rxd_sta {  #define MWL8K_STA_RATE_INFO_MCS_FORMAT		0x0001  #define MWL8K_STA_RX_CTRL_OWNED_BY_HOST		0x02 +#define MWL8K_STA_RX_CTRL_DECRYPT_ERROR		0x04 +/* ICV=0 or MIC=1 */ +#define MWL8K_STA_RX_CTRL_DEC_ERR_TYPE		0x08 +/* Key is uploaded only in failure case */ +#define MWL8K_STA_RX_CTRL_KEY_INDEX			0x30  static void mwl8k_rxd_sta_init(void *_rxd, dma_addr_t next_dma_addr)  { @@ -931,9 +1124,13 @@ mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status,  	} else {  		status->band = IEEE80211_BAND_2GHZ;  	} -	status->freq = ieee80211_channel_to_frequency(rxd->channel); +	status->freq = ieee80211_channel_to_frequency(rxd->channel, +						      status->band);  	*qos = rxd->qos_control; +	if ((rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DECRYPT_ERROR) && +	    (rxd->rx_ctrl & MWL8K_STA_RX_CTRL_DEC_ERR_TYPE)) +		status->flag |= RX_FLAG_MMIC_ERROR;  	return le16_to_cpu(rxd->pkt_len);  } @@ -969,13 +1166,11 @@ static int mwl8k_rxq_init(struct ieee80211_hw *hw, int index)  	}  	memset(rxq->rxd, 0, size); -	rxq->buf = kmalloc(MWL8K_RX_DESCS * sizeof(*rxq->buf), GFP_KERNEL); +	rxq->buf = kcalloc(MWL8K_RX_DESCS, sizeof(*rxq->buf), GFP_KERNEL);  	if (rxq->buf == NULL) { -		wiphy_err(hw->wiphy, "failed to alloc RX skbuff list\n");  		pci_free_consistent(priv->pdev, size, rxq->rxd, rxq->rxd_dma);  		return -ENOMEM;  	} -	memset(rxq->buf, 0, MWL8K_RX_DESCS * sizeof(*rxq->buf));  	for (i = 0; i < MWL8K_RX_DESCS; i++) {  		int desc_size; @@ -1040,6 +1235,9 @@ static void mwl8k_rxq_deinit(struct ieee80211_hw *hw, int index)  	struct mwl8k_rx_queue *rxq = priv->rxq + index;  	int i; +	if (rxq->rxd == NULL) +		return; +  	for (i = 0; i < MWL8K_RX_DESCS; i++) {  		if (rxq->buf[i].skb != NULL) {  			pci_unmap_single(priv->pdev, @@ -1071,7 +1269,7 @@ mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)  {  	return priv->capture_beacon &&  		ieee80211_is_beacon(wh->frame_control) && -		!compare_ether_addr(wh->addr3, priv->capture_bssid); +		ether_addr_equal_64bits(wh->addr3, priv->capture_bssid);  }  static inline void mwl8k_save_beacon(struct ieee80211_hw *hw, @@ -1092,9 +1290,25 @@ static inline void mwl8k_save_beacon(struct ieee80211_hw *hw,  		ieee80211_queue_work(hw, &priv->finalize_join_worker);  } +static inline struct mwl8k_vif *mwl8k_find_vif_bss(struct list_head *vif_list, +						   u8 *bssid) +{ +	struct mwl8k_vif *mwl8k_vif; + +	list_for_each_entry(mwl8k_vif, +			    vif_list, list) { +		if (memcmp(bssid, mwl8k_vif->bssid, +			   ETH_ALEN) == 0) +			return mwl8k_vif; +	} + +	return NULL; +} +  static int rxq_process(struct ieee80211_hw *hw, int index, int limit)  {  	struct mwl8k_priv *priv = hw->priv; +	struct mwl8k_vif *mwl8k_vif = NULL;  	struct mwl8k_rx_queue *rxq = priv->rxq + index;  	int processed; @@ -1104,6 +1318,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)  		void *rxd;  		int pkt_len;  		struct ieee80211_rx_status status; +		struct ieee80211_hdr *wh;  		__le16 qos;  		skb = rxq->buf[rxq->head].skb; @@ -1130,8 +1345,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)  		rxq->rxd_count--; -		skb_put(skb, pkt_len); -		mwl8k_remove_dma_header(skb, qos); +		wh = &((struct mwl8k_dma_data *)skb->data)->wh;  		/*  		 * Check for a pending join operation.  Save a @@ -1141,6 +1355,46 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit)  		if (mwl8k_capture_bssid(priv, (void *)skb->data))  			mwl8k_save_beacon(hw, skb); +		if (ieee80211_has_protected(wh->frame_control)) { + +			/* Check if hw crypto has been enabled for +			 * this bss. If yes, set the status flags +			 * accordingly +			 */ +			mwl8k_vif = mwl8k_find_vif_bss(&priv->vif_list, +								wh->addr1); + +			if (mwl8k_vif != NULL && +			    mwl8k_vif->is_hw_crypto_enabled) { +				/* +				 * When MMIC ERROR is encountered +				 * by the firmware, payload is +				 * dropped and only 32 bytes of +				 * mwl8k Firmware header is sent +				 * to the host. +				 * +				 * We need to add four bytes of +				 * key information.  In it +				 * MAC80211 expects keyidx set to +				 * 0 for triggering Counter +				 * Measure of MMIC failure. +				 */ +				if (status.flag & RX_FLAG_MMIC_ERROR) { +					struct mwl8k_dma_data *tr; +					tr = (struct mwl8k_dma_data *)skb->data; +					memset((void *)&(tr->data), 0, 4); +					pkt_len += 4; +				} + +				if (!ieee80211_is_auth(wh->frame_control)) +					status.flag |= RX_FLAG_IV_STRIPPED | +						       RX_FLAG_DECRYPTED | +						       RX_FLAG_MMIC_STRIPPED; +			} +		} + +		skb_put(skb, pkt_len); +		mwl8k_remove_dma_header(skb, qos);  		memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));  		ieee80211_rx_irqsafe(hw, skb); @@ -1176,7 +1430,7 @@ struct mwl8k_tx_desc {  	__le16 pkt_len;  	__u8 dest_MAC_addr[ETH_ALEN];  	__le32 next_txd_phys_addr; -	__le32 reserved; +	__le32 timestamp;  	__le16 rate_info;  	__u8 peer_id;  	__u8 tx_frag_cnt; @@ -1204,13 +1458,11 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index)  	}  	memset(txq->txd, 0, size); -	txq->skb = kmalloc(MWL8K_TX_DESCS * sizeof(*txq->skb), GFP_KERNEL); +	txq->skb = kcalloc(MWL8K_TX_DESCS, sizeof(*txq->skb), GFP_KERNEL);  	if (txq->skb == NULL) { -		wiphy_err(hw->wiphy, "failed to alloc TX skbuff list\n");  		pci_free_consistent(priv->pdev, size, txq->txd, txq->txd_dma);  		return -ENOMEM;  	} -	memset(txq->skb, 0, MWL8K_TX_DESCS * sizeof(*txq->skb));  	for (i = 0; i < MWL8K_TX_DESCS; i++) {  		struct mwl8k_tx_desc *tx_desc; @@ -1241,7 +1493,7 @@ static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw)  	struct mwl8k_priv *priv = hw->priv;  	int i; -	for (i = 0; i < MWL8K_TX_QUEUES; i++) { +	for (i = 0; i < mwl8k_tx_queues(priv); i++) {  		struct mwl8k_tx_queue *txq = priv->txq + i;  		int fw_owned = 0;  		int drv_owned = 0; @@ -1285,6 +1537,21 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)  	might_sleep(); +	/* Since fw restart is in progress, allow only the firmware +	 * commands from the restart code and block the other +	 * commands since they are going to fail in any case since +	 * the firmware has crashed +	 */ +	if (priv->hw_restart_in_progress) { +		if (priv->hw_restart_owner == current) +			return 0; +		else +			return -EBUSY; +	} + +	if (atomic_read(&priv->watchdog_event_pending)) +		return 0; +  	/*  	 * The TX queues are stopped at this point, so this test  	 * doesn't need to take ->tx_lock. @@ -1292,7 +1559,7 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)  	if (!priv->pending_tx_pkts)  		return 0; -	retry = 0; +	retry = 1;  	rc = 0;  	spin_lock_bh(&priv->tx_lock); @@ -1306,16 +1573,29 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)  		spin_unlock_bh(&priv->tx_lock);  		timeout = wait_for_completion_timeout(&tx_wait,  			    msecs_to_jiffies(MWL8K_TX_WAIT_TIMEOUT_MS)); + +		if (atomic_read(&priv->watchdog_event_pending)) { +			spin_lock_bh(&priv->tx_lock); +			priv->tx_wait = NULL; +			spin_unlock_bh(&priv->tx_lock); +			return 0; +		} +  		spin_lock_bh(&priv->tx_lock); -		if (timeout) { +		if (timeout || !priv->pending_tx_pkts) {  			WARN_ON(priv->pending_tx_pkts); -			if (retry) { +			if (retry)  				wiphy_notice(hw->wiphy, "tx rings drained\n"); -			}  			break;  		} +		if (retry) { +			mwl8k_tx_start(priv); +			retry = 0; +			continue; +		} +  		if (priv->pending_tx_pkts < oldcount) {  			wiphy_notice(hw->wiphy,  				     "waiting for tx rings to drain (%d -> %d pkts)\n", @@ -1329,9 +1609,12 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)  		wiphy_err(hw->wiphy, "tx rings stuck for %d ms\n",  			  MWL8K_TX_WAIT_TIMEOUT_MS);  		mwl8k_dump_tx_rings(hw); +		priv->hw_restart_in_progress = true; +		ieee80211_queue_work(hw, &priv->fw_reload);  		rc = -ETIMEDOUT;  	} +	priv->tx_wait = NULL;  	spin_unlock_bh(&priv->tx_lock);  	return rc; @@ -1342,6 +1625,41 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw)  		     MWL8K_TXD_STATUS_OK_RETRY |		\  		     MWL8K_TXD_STATUS_OK_MORE_RETRY)) +static int mwl8k_tid_queue_mapping(u8 tid) +{ +	BUG_ON(tid > 7); + +	switch (tid) { +	case 0: +	case 3: +		return IEEE80211_AC_BE; +		break; +	case 1: +	case 2: +		return IEEE80211_AC_BK; +		break; +	case 4: +	case 5: +		return IEEE80211_AC_VI; +		break; +	case 6: +	case 7: +		return IEEE80211_AC_VO; +		break; +	default: +		return -1; +		break; +	} +} + +/* The firmware will fill in the rate information + * for each packet that gets queued in the hardware + * and these macros will interpret that info. + */ + +#define RI_FORMAT(a)		  (a & 0x0001) +#define RI_RATE_ID_MCS(a)	 ((a & 0x01f8) >> 3) +  static int  mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)  { @@ -1358,6 +1676,10 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)  		struct sk_buff *skb;  		struct ieee80211_tx_info *info;  		u32 status; +		struct ieee80211_sta *sta; +		struct mwl8k_sta *sta_info = NULL; +		u16 rate_info; +		struct ieee80211_hdr *wh;  		tx = txq->head;  		tx_desc = txq->txd + tx; @@ -1386,12 +1708,44 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)  		mwl8k_remove_dma_header(skb, tx_desc->qos_control); +		wh = (struct ieee80211_hdr *) skb->data; +  		/* Mark descriptor as unused */  		tx_desc->pkt_phys_addr = 0;  		tx_desc->pkt_len = 0;  		info = IEEE80211_SKB_CB(skb); +		if (ieee80211_is_data(wh->frame_control)) { +			rcu_read_lock(); +			sta = ieee80211_find_sta_by_ifaddr(hw, wh->addr1, +							   wh->addr2); +			if (sta) { +				sta_info = MWL8K_STA(sta); +				BUG_ON(sta_info == NULL); +				rate_info = le16_to_cpu(tx_desc->rate_info); +				/* If rate is < 6.5 Mpbs for an ht station +				 * do not form an ampdu. If the station is a +				 * legacy station (format = 0), do not form an +				 * ampdu +				 */ +				if (RI_RATE_ID_MCS(rate_info) < 1 || +				    RI_FORMAT(rate_info) == 0) { +					sta_info->is_ampdu_allowed = false; +				} else { +					sta_info->is_ampdu_allowed = true; +				} +			} +			rcu_read_unlock(); +		} +  		ieee80211_tx_info_clear_status(info); + +		/* Rate control is happening in the firmware. +		 * Ensure no tx rate is being reported. +		 */ +		info->status.rates[0].idx = -1; +		info->status.rates[0].count = 1; +  		if (MWL8K_TXD_SUCCESS(status))  			info->flags |= IEEE80211_TX_STAT_ACK; @@ -1400,9 +1754,6 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force)  		processed++;  	} -	if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) -		ieee80211_wake_queue(hw, index); -  	return processed;  } @@ -1412,6 +1763,9 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)  	struct mwl8k_priv *priv = hw->priv;  	struct mwl8k_tx_queue *txq = priv->txq + index; +	if (txq->txd == NULL) +		return; +  	mwl8k_txq_reclaim(hw, index, INT_MAX, 1);  	kfree(txq->skb); @@ -1423,8 +1777,120 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index)  	txq->txd = NULL;  } +/* caller must hold priv->stream_lock when calling the stream functions */ +static struct mwl8k_ampdu_stream * +mwl8k_add_stream(struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 tid) +{ +	struct mwl8k_ampdu_stream *stream; +	struct mwl8k_priv *priv = hw->priv; +	int i; + +	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { +		stream = &priv->ampdu[i]; +		if (stream->state == AMPDU_NO_STREAM) { +			stream->sta = sta; +			stream->state = AMPDU_STREAM_NEW; +			stream->tid = tid; +			stream->idx = i; +			wiphy_debug(hw->wiphy, "Added a new stream for %pM %d", +				    sta->addr, tid); +			return stream; +		} +	} +	return NULL; +} +  static int -mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) +mwl8k_start_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) +{ +	int ret; + +	/* if the stream has already been started, don't start it again */ +	if (stream->state != AMPDU_STREAM_NEW) +		return 0; +	ret = ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0); +	if (ret) +		wiphy_debug(hw->wiphy, "Failed to start stream for %pM %d: " +			    "%d\n", stream->sta->addr, stream->tid, ret); +	else +		wiphy_debug(hw->wiphy, "Started stream for %pM %d\n", +			    stream->sta->addr, stream->tid); +	return ret; +} + +static void +mwl8k_remove_stream(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) +{ +	wiphy_debug(hw->wiphy, "Remove stream for %pM %d\n", stream->sta->addr, +		    stream->tid); +	memset(stream, 0, sizeof(*stream)); +} + +static struct mwl8k_ampdu_stream * +mwl8k_lookup_stream(struct ieee80211_hw *hw, u8 *addr, u8 tid) +{ +	struct mwl8k_priv *priv = hw->priv; +	int i; + +	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { +		struct mwl8k_ampdu_stream *stream; +		stream = &priv->ampdu[i]; +		if (stream->state == AMPDU_NO_STREAM) +			continue; +		if (!memcmp(stream->sta->addr, addr, ETH_ALEN) && +		    stream->tid == tid) +			return stream; +	} +	return NULL; +} + +#define MWL8K_AMPDU_PACKET_THRESHOLD 64 +static inline bool mwl8k_ampdu_allowed(struct ieee80211_sta *sta, u8 tid) +{ +	struct mwl8k_sta *sta_info = MWL8K_STA(sta); +	struct tx_traffic_info *tx_stats; + +	BUG_ON(tid >= MWL8K_MAX_TID); +	tx_stats = &sta_info->tx_stats[tid]; + +	return sta_info->is_ampdu_allowed && +		tx_stats->pkts > MWL8K_AMPDU_PACKET_THRESHOLD; +} + +static inline void mwl8k_tx_count_packet(struct ieee80211_sta *sta, u8 tid) +{ +	struct mwl8k_sta *sta_info = MWL8K_STA(sta); +	struct tx_traffic_info *tx_stats; + +	BUG_ON(tid >= MWL8K_MAX_TID); +	tx_stats = &sta_info->tx_stats[tid]; + +	if (tx_stats->start_time == 0) +		tx_stats->start_time = jiffies; + +	/* reset the packet count after each second elapses.  If the number of +	 * packets ever exceeds the ampdu_min_traffic threshold, we will allow +	 * an ampdu stream to be started. +	 */ +	if (jiffies - tx_stats->start_time > HZ) { +		tx_stats->pkts = 0; +		tx_stats->start_time = 0; +	} else +		tx_stats->pkts++; +} + +/* The hardware ampdu queues start from 5. + * txpriorities for ampdu queues are + * 5 6 7 0 1 2 3 4 ie., queue 5 is highest + * and queue 3 is lowest (queue 4 is reserved) + */ +#define BA_QUEUE		5 + +static void +mwl8k_txq_xmit(struct ieee80211_hw *hw, +	       int index, +	       struct ieee80211_sta *sta, +	       struct sk_buff *skb)  {  	struct mwl8k_priv *priv = hw->priv;  	struct ieee80211_tx_info *tx_info; @@ -1436,6 +1902,13 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)  	u32 txstatus;  	u8 txdatarate;  	u16 qos; +	int txpriority; +	u8 tid = 0; +	struct mwl8k_ampdu_stream *stream = NULL; +	bool start_ba_session = false; +	bool mgmtframe = false; +	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; +	bool eapol_frame = false;  	wh = (struct ieee80211_hdr *)skb->data;  	if (ieee80211_is_data_qos(wh->frame_control)) @@ -1443,7 +1916,17 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)  	else  		qos = 0; -	mwl8k_add_dma_header(skb); +	if (skb->protocol == cpu_to_be16(ETH_P_PAE)) +		eapol_frame = true; + +	if (ieee80211_is_mgmt(wh->frame_control)) +		mgmtframe = true; + +	if (priv->ap_fw) +		mwl8k_encapsulate_tx_frame(priv, skb); +	else +		mwl8k_add_dma_header(priv, skb, 0, 0); +  	wh = &((struct mwl8k_dma_data *)skb->data)->wh;  	tx_info = IEEE80211_SKB_CB(skb); @@ -1474,34 +1957,151 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)  			qos |= MWL8K_QOS_ACK_POLICY_NORMAL;  	} +	/* Queue ADDBA request in the respective data queue.  While setting up +	 * the ampdu stream, mac80211 queues further packets for that +	 * particular ra/tid pair.  However, packets piled up in the hardware +	 * for that ra/tid pair will still go out. ADDBA request and the +	 * related data packets going out from different queues asynchronously +	 * will cause a shift in the receiver window which might result in +	 * ampdu packets getting dropped at the receiver after the stream has +	 * been setup. +	 */ +	if (unlikely(ieee80211_is_action(wh->frame_control) && +	    mgmt->u.action.category == WLAN_CATEGORY_BACK && +	    mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ && +	    priv->ap_fw)) { +		u16 capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); +		tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; +		index = mwl8k_tid_queue_mapping(tid); +	} + +	txpriority = index; + +	if (priv->ap_fw && sta && sta->ht_cap.ht_supported && !eapol_frame && +	    ieee80211_is_data_qos(wh->frame_control)) { +		tid = qos & 0xf; +		mwl8k_tx_count_packet(sta, tid); +		spin_lock(&priv->stream_lock); +		stream = mwl8k_lookup_stream(hw, sta->addr, tid); +		if (stream != NULL) { +			if (stream->state == AMPDU_STREAM_ACTIVE) { +				WARN_ON(!(qos & MWL8K_QOS_ACK_POLICY_BLOCKACK)); +				txpriority = (BA_QUEUE + stream->idx) % +					     TOTAL_HW_TX_QUEUES; +				if (stream->idx <= 1) +					index = stream->idx + +						MWL8K_TX_WMM_QUEUES; + +			} else if (stream->state == AMPDU_STREAM_NEW) { +				/* We get here if the driver sends us packets +				 * after we've initiated a stream, but before +				 * our ampdu_action routine has been called +				 * with IEEE80211_AMPDU_TX_START to get the SSN +				 * for the ADDBA request.  So this packet can +				 * go out with no risk of sequence number +				 * mismatch.  No special handling is required. +				 */ +			} else { +				/* Drop packets that would go out after the +				 * ADDBA request was sent but before the ADDBA +				 * response is received.  If we don't do this, +				 * the recipient would probably receive it +				 * after the ADDBA request with SSN 0.  This +				 * will cause the recipient's BA receive window +				 * to shift, which would cause the subsequent +				 * packets in the BA stream to be discarded. +				 * mac80211 queues our packets for us in this +				 * case, so this is really just a safety check. +				 */ +				wiphy_warn(hw->wiphy, +					   "Cannot send packet while ADDBA " +					   "dialog is underway.\n"); +				spin_unlock(&priv->stream_lock); +				dev_kfree_skb(skb); +				return; +			} +		} else { +			/* Defer calling mwl8k_start_stream so that the current +			 * skb can go out before the ADDBA request.  This +			 * prevents sequence number mismatch at the recepient +			 * as described above. +			 */ +			if (mwl8k_ampdu_allowed(sta, tid)) { +				stream = mwl8k_add_stream(hw, sta, tid); +				if (stream != NULL) +					start_ba_session = true; +			} +		} +		spin_unlock(&priv->stream_lock); +	} else { +		qos &= ~MWL8K_QOS_ACK_POLICY_MASK; +		qos |= MWL8K_QOS_ACK_POLICY_NORMAL; +	} +  	dma = pci_map_single(priv->pdev, skb->data,  				skb->len, PCI_DMA_TODEVICE);  	if (pci_dma_mapping_error(priv->pdev, dma)) {  		wiphy_debug(hw->wiphy,  			    "failed to dma map skb, dropping TX frame.\n"); +		if (start_ba_session) { +			spin_lock(&priv->stream_lock); +			mwl8k_remove_stream(hw, stream); +			spin_unlock(&priv->stream_lock); +		}  		dev_kfree_skb(skb); -		return NETDEV_TX_OK; +		return;  	}  	spin_lock_bh(&priv->tx_lock);  	txq = priv->txq + index; +	/* Mgmt frames that go out frequently are probe +	 * responses. Other mgmt frames got out relatively +	 * infrequently. Hence reserve 2 buffers so that +	 * other mgmt frames do not get dropped due to an +	 * already queued probe response in one of the +	 * reserved buffers. +	 */ + +	if (txq->len >= MWL8K_TX_DESCS - 2) { +		if (!mgmtframe || txq->len == MWL8K_TX_DESCS) { +			if (start_ba_session) { +				spin_lock(&priv->stream_lock); +				mwl8k_remove_stream(hw, stream); +				spin_unlock(&priv->stream_lock); +			} +			mwl8k_tx_start(priv); +			spin_unlock_bh(&priv->tx_lock); +			pci_unmap_single(priv->pdev, dma, skb->len, +					 PCI_DMA_TODEVICE); +			dev_kfree_skb(skb); +			return; +		} +	} +  	BUG_ON(txq->skb[txq->tail] != NULL);  	txq->skb[txq->tail] = skb;  	tx = txq->txd + txq->tail;  	tx->data_rate = txdatarate; -	tx->tx_priority = index; +	tx->tx_priority = txpriority;  	tx->qos_control = cpu_to_le16(qos);  	tx->pkt_phys_addr = cpu_to_le32(dma);  	tx->pkt_len = cpu_to_le16(skb->len);  	tx->rate_info = 0; -	if (!priv->ap_fw && tx_info->control.sta != NULL) -		tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id; +	if (!priv->ap_fw && sta != NULL) +		tx->peer_id = MWL8K_STA(sta)->peer_id;  	else  		tx->peer_id = 0; + +	if (priv->ap_fw && ieee80211_is_data(wh->frame_control) && !eapol_frame) +		tx->timestamp = cpu_to_le32(ioread32(priv->regs + +						MWL8K_HW_TIMER_REGISTER)); +	else +		tx->timestamp = 0; +  	wmb();  	tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); @@ -1512,14 +2112,17 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb)  	if (txq->tail == MWL8K_TX_DESCS)  		txq->tail = 0; -	if (txq->head == txq->tail) -		ieee80211_stop_queue(hw, index); -  	mwl8k_tx_start(priv);  	spin_unlock_bh(&priv->tx_lock); -	return NETDEV_TX_OK; +	/* Initiate the ampdu session here */ +	if (start_ba_session) { +		spin_lock(&priv->stream_lock); +		if (mwl8k_start_stream(hw, stream)) +			mwl8k_remove_stream(hw, stream); +		spin_unlock(&priv->stream_lock); +	}  } @@ -1551,7 +2154,9 @@ static int mwl8k_fw_lock(struct ieee80211_hw *hw)  		rc = mwl8k_tx_wait_empty(hw);  		if (rc) { -			ieee80211_wake_queues(hw); +			if (!priv->hw_restart_in_progress) +				ieee80211_wake_queues(hw); +  			mutex_unlock(&priv->fw_mutex);  			return rc; @@ -1570,12 +2175,16 @@ static void mwl8k_fw_unlock(struct ieee80211_hw *hw)  	struct mwl8k_priv *priv = hw->priv;  	if (!--priv->fw_mutex_depth) { -		ieee80211_wake_queues(hw); +		if (!priv->hw_restart_in_progress) +			ieee80211_wake_queues(hw); +  		priv->fw_mutex_owner = NULL;  		mutex_unlock(&priv->fw_mutex);  	}  } +static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, +			       u32 bitmap);  /*   * Command processing. @@ -1594,6 +2203,34 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)  	int rc;  	unsigned long timeout = 0;  	u8 buf[32]; +	u32 bitmap = 0; + +	wiphy_dbg(hw->wiphy, "Posting %s [%d]\n", +		  mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), cmd->macid); + +	/* Before posting firmware commands that could change the hardware +	 * characteristics, make sure that all BSSes are stopped temporary. +	 * Enable these stopped BSSes after completion of the commands +	 */ + +	rc = mwl8k_fw_lock(hw); +	if (rc) +		return rc; + +	if (priv->ap_fw && priv->running_bsses) { +		switch (le16_to_cpu(cmd->code)) { +		case MWL8K_CMD_SET_RF_CHANNEL: +		case MWL8K_CMD_RADIO_CONTROL: +		case MWL8K_CMD_RF_TX_POWER: +		case MWL8K_CMD_TX_POWER: +		case MWL8K_CMD_RF_ANTENNA: +		case MWL8K_CMD_RTS_THRESHOLD: +		case MWL8K_CMD_MIMO_CONFIG: +			bitmap = priv->running_bsses; +			mwl8k_enable_bsses(hw, false, bitmap); +			break; +		} +	}  	cmd->result = (__force __le16) 0xffff;  	dma_size = le16_to_cpu(cmd->length); @@ -1602,13 +2239,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)  	if (pci_dma_mapping_error(priv->pdev, dma_addr))  		return -ENOMEM; -	rc = mwl8k_fw_lock(hw); -	if (rc) { -		pci_unmap_single(priv->pdev, dma_addr, dma_size, -						PCI_DMA_BIDIRECTIONAL); -		return rc; -	} -  	priv->hostcmd_wait = &cmd_wait;  	iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR);  	iowrite32(MWL8K_H2A_INT_DOORBELL, @@ -1621,7 +2251,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)  	priv->hostcmd_wait = NULL; -	mwl8k_fw_unlock(hw);  	pci_unmap_single(priv->pdev, dma_addr, dma_size,  					PCI_DMA_BIDIRECTIONAL); @@ -1648,6 +2277,11 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd)  				     ms);  	} +	if (bitmap) +		mwl8k_enable_bsses(hw, true, bitmap); + +	mwl8k_fw_unlock(hw); +  	return rc;  } @@ -1717,7 +2351,7 @@ struct mwl8k_cmd_get_hw_spec_sta {  	__u8 mcs_bitmap[16];  	__le32 rx_queue_ptr;  	__le32 num_tx_queues; -	__le32 tx_queue_ptrs[MWL8K_TX_QUEUES]; +	__le32 tx_queue_ptrs[MWL8K_TX_WMM_QUEUES];  	__le32 caps2;  	__le32 num_tx_desc_per_queue;  	__le32 total_rxd; @@ -1793,6 +2427,9 @@ mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)  {  	struct mwl8k_priv *priv = hw->priv; +	if (priv->caps) +		return; +  	if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) {  		mwl8k_setup_2ghz_band(hw);  		if (caps & MWL8K_CAP_MIMO) @@ -1804,6 +2441,8 @@ mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps)  		if (caps & MWL8K_CAP_MIMO)  			mwl8k_set_ht_caps(hw, &priv->band_50, caps);  	} + +	priv->caps = caps;  }  static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) @@ -1823,8 +2462,8 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw)  	memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr));  	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);  	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); -	cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma);  	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);  	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); @@ -1866,13 +2505,16 @@ struct mwl8k_cmd_get_hw_spec_ap {  	__le32 wcbbase2;  	__le32 wcbbase3;  	__le32 fw_api_version; +	__le32 caps; +	__le32 num_of_ampdu_queues; +	__le32 wcbbase_ampdu[MWL8K_MAX_AMPDU_QUEUES];  } __packed;  static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)  {  	struct mwl8k_priv *priv = hw->priv;  	struct mwl8k_cmd_get_hw_spec_ap *cmd; -	int rc; +	int rc, i;  	u32 api_version;  	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -1904,27 +2546,31 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw)  		priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs);  		priv->fw_rev = le32_to_cpu(cmd->fw_rev);  		priv->hw_rev = cmd->hw_rev; -		mwl8k_setup_2ghz_band(hw); +		mwl8k_set_caps(hw, le32_to_cpu(cmd->caps));  		priv->ap_macids_supported = 0x000000ff; -		priv->sta_macids_supported = 0x00000000; - -		off = le32_to_cpu(cmd->wcbbase0) & 0xffff; -		iowrite32(priv->txq[0].txd_dma, priv->sram + off); - +		priv->sta_macids_supported = 0x00000100; +		priv->num_ampdu_queues = le32_to_cpu(cmd->num_of_ampdu_queues); +		if (priv->num_ampdu_queues > MWL8K_MAX_AMPDU_QUEUES) { +			wiphy_warn(hw->wiphy, "fw reported %d ampdu queues" +				   " but we only support %d.\n", +				   priv->num_ampdu_queues, +				   MWL8K_MAX_AMPDU_QUEUES); +			priv->num_ampdu_queues = MWL8K_MAX_AMPDU_QUEUES; +		}  		off = le32_to_cpu(cmd->rxwrptr) & 0xffff;  		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off);  		off = le32_to_cpu(cmd->rxrdptr) & 0xffff;  		iowrite32(priv->rxq[0].rxd_dma, priv->sram + off); -		off = le32_to_cpu(cmd->wcbbase1) & 0xffff; -		iowrite32(priv->txq[1].txd_dma, priv->sram + off); - -		off = le32_to_cpu(cmd->wcbbase2) & 0xffff; -		iowrite32(priv->txq[2].txd_dma, priv->sram + off); +		priv->txq_offset[0] = le32_to_cpu(cmd->wcbbase0) & 0xffff; +		priv->txq_offset[1] = le32_to_cpu(cmd->wcbbase1) & 0xffff; +		priv->txq_offset[2] = le32_to_cpu(cmd->wcbbase2) & 0xffff; +		priv->txq_offset[3] = le32_to_cpu(cmd->wcbbase3) & 0xffff; -		off = le32_to_cpu(cmd->wcbbase3) & 0xffff; -		iowrite32(priv->txq[3].txd_dma, priv->sram + off); +		for (i = 0; i < priv->num_ampdu_queues; i++) +			priv->txq_offset[i + MWL8K_TX_WMM_QUEUES] = +				le32_to_cpu(cmd->wcbbase_ampdu[i]) & 0xffff;  	}  done: @@ -1947,12 +2593,21 @@ struct mwl8k_cmd_set_hw_spec {  	__le32 caps;  	__le32 rx_queue_ptr;  	__le32 num_tx_queues; -	__le32 tx_queue_ptrs[MWL8K_TX_QUEUES]; +	__le32 tx_queue_ptrs[MWL8K_MAX_TX_QUEUES];  	__le32 flags;  	__le32 num_tx_desc_per_queue;  	__le32 total_rxd;  } __packed; +/* If enabled, MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY will cause + * packets to expire 500 ms after the timestamp in the tx descriptor.  That is, + * the packets that are queued for more than 500ms, will be dropped in the + * hardware. This helps minimizing the issues caused due to head-of-line + * blocking where a slow client can hog the bandwidth and affect traffic to a + * faster client. + */ +#define MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY	0x00000400 +#define MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR	0x00000200  #define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT		0x00000080  #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP	0x00000020  #define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON		0x00000010 @@ -1973,12 +2628,24 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw)  	cmd->ps_cookie = cpu_to_le32(priv->cookie_dma);  	cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); -	cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); -	for (i = 0; i < MWL8K_TX_QUEUES; i++) -		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); +	cmd->num_tx_queues = cpu_to_le32(mwl8k_tx_queues(priv)); + +	/* +	 * Mac80211 stack has Q0 as highest priority and Q3 as lowest in +	 * that order. Firmware has Q3 as highest priority and Q0 as lowest +	 * in that order. Map Q3 of mac80211 to Q0 of firmware so that the +	 * priority is interpreted the right way in firmware. +	 */ +	for (i = 0; i < mwl8k_tx_queues(priv); i++) { +		int j = mwl8k_tx_queues(priv) - 1 - i; +		cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma); +	} +  	cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT |  				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | -				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON); +				 MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON | +				 MWL8K_SET_HW_SPEC_FLAG_ENABLE_LIFE_TIME_EXPIRY | +				 MWL8K_SET_HW_SPEC_FLAG_GENERATE_CCMP_HDR);  	cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS);  	cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); @@ -2195,13 +2862,15 @@ struct mwl8k_cmd_tx_power {  	__le16 bw;  	__le16 sub_ch;  	__le16 power_level_list[MWL8K_TX_POWER_LEVEL_TOTAL]; -} __attribute__((packed)); +} __packed;  static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw,  				     struct ieee80211_conf *conf,  				     unsigned short pwr)  { -	struct ieee80211_channel *channel = conf->channel; +	struct ieee80211_channel *channel = conf->chandef.chan; +	enum nl80211_channel_type channel_type = +		cfg80211_get_chandef_type(&conf->chandef);  	struct mwl8k_cmd_tx_power *cmd;  	int rc;  	int i; @@ -2219,16 +2888,16 @@ static int mwl8k_cmd_tx_power(struct ieee80211_hw *hw,  	else if (channel->band == IEEE80211_BAND_5GHZ)  		cmd->band = cpu_to_le16(0x4); -	cmd->channel = channel->hw_value; +	cmd->channel = cpu_to_le16(channel->hw_value); -	if (conf->channel_type == NL80211_CHAN_NO_HT || -	    conf->channel_type == NL80211_CHAN_HT20) { +	if (channel_type == NL80211_CHAN_NO_HT || +	    channel_type == NL80211_CHAN_HT20) {  		cmd->bw = cpu_to_le16(0x2);  	} else {  		cmd->bw = cpu_to_le16(0x4); -		if (conf->channel_type == NL80211_CHAN_HT40MINUS) +		if (channel_type == NL80211_CHAN_HT40MINUS)  			cmd->sub_ch = cpu_to_le16(0x3); -		else if (conf->channel_type == NL80211_CHAN_HT40PLUS) +		else if (channel_type == NL80211_CHAN_HT40PLUS)  			cmd->sub_ch = cpu_to_le16(0x1);  	} @@ -2330,6 +2999,47 @@ static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw)  }  /* + * CMD_BBP_REG_ACCESS. + */ +struct mwl8k_cmd_bbp_reg_access { +	struct mwl8k_cmd_pkt header; +	__le16 action; +	__le16 offset; +	u8 value; +	u8 rsrv[3]; +} __packed; + +static int +mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw, +			 u16 action, +			 u16 offset, +			 u8 *value) +{ +	struct mwl8k_cmd_bbp_reg_access *cmd; +	int rc; + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return -ENOMEM; + +	cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS); +	cmd->header.length = cpu_to_le16(sizeof(*cmd)); +	cmd->action = cpu_to_le16(action); +	cmd->offset = cpu_to_le16(offset); + +	rc = mwl8k_post_cmd(hw, &cmd->header); + +	if (!rc) +		*value = cmd->value; +	else +		*value = 0; + +	kfree(cmd); + +	return rc; +} + +/*   * CMD_SET_POST_SCAN.   */  struct mwl8k_cmd_set_post_scan { @@ -2359,6 +3069,64 @@ mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac)  	return rc;  } +static int freq_to_idx(struct mwl8k_priv *priv, int freq) +{ +	struct ieee80211_supported_band *sband; +	int band, ch, idx = 0; + +	for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { +		sband = priv->hw->wiphy->bands[band]; +		if (!sband) +			continue; + +		for (ch = 0; ch < sband->n_channels; ch++, idx++) +			if (sband->channels[ch].center_freq == freq) +				goto exit; +	} + +exit: +	return idx; +} + +static void mwl8k_update_survey(struct mwl8k_priv *priv, +				struct ieee80211_channel *channel) +{ +	u32 cca_cnt, rx_rdy; +	s8 nf = 0, idx; +	struct survey_info *survey; + +	idx = freq_to_idx(priv, priv->acs_chan->center_freq); +	if (idx >= MWL8K_NUM_CHANS) { +		wiphy_err(priv->hw->wiphy, "Failed to update survey\n"); +		return; +	} + +	survey = &priv->survey[idx]; + +	cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); +	cca_cnt /= 1000; /* uSecs to mSecs */ +	survey->channel_time_busy = (u64) cca_cnt; + +	rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); +	rx_rdy /= 1000; /* uSecs to mSecs */ +	survey->channel_time_rx = (u64) rx_rdy; + +	priv->channel_time = jiffies - priv->channel_time; +	survey->channel_time = jiffies_to_msecs(priv->channel_time); + +	survey->channel = channel; + +	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf); + +	/* Make sure sign is negative else ACS  at hostapd fails */ +	survey->noise = nf * -1; + +	survey->filled = SURVEY_INFO_NOISE_DBM | +			 SURVEY_INFO_CHANNEL_TIME | +			 SURVEY_INFO_CHANNEL_TIME_BUSY | +			 SURVEY_INFO_CHANNEL_TIME_RX; +} +  /*   * CMD_SET_RF_CHANNEL.   */ @@ -2372,8 +3140,11 @@ struct mwl8k_cmd_set_rf_channel {  static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,  				    struct ieee80211_conf *conf)  { -	struct ieee80211_channel *channel = conf->channel; +	struct ieee80211_channel *channel = conf->chandef.chan; +	enum nl80211_channel_type channel_type = +		cfg80211_get_chandef_type(&conf->chandef);  	struct mwl8k_cmd_set_rf_channel *cmd; +	struct mwl8k_priv *priv = hw->priv;  	int rc;  	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2390,13 +3161,29 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw,  	else if (channel->band == IEEE80211_BAND_5GHZ)  		cmd->channel_flags |= cpu_to_le32(0x00000004); -	if (conf->channel_type == NL80211_CHAN_NO_HT || -	    conf->channel_type == NL80211_CHAN_HT20) +	if (!priv->sw_scan_start) { +		if (channel_type == NL80211_CHAN_NO_HT || +		    channel_type == NL80211_CHAN_HT20) +			cmd->channel_flags |= cpu_to_le32(0x00000080); +		else if (channel_type == NL80211_CHAN_HT40MINUS) +			cmd->channel_flags |= cpu_to_le32(0x000001900); +		else if (channel_type == NL80211_CHAN_HT40PLUS) +			cmd->channel_flags |= cpu_to_le32(0x000000900); +	} else {  		cmd->channel_flags |= cpu_to_le32(0x00000080); -	else if (conf->channel_type == NL80211_CHAN_HT40MINUS) -		cmd->channel_flags |= cpu_to_le32(0x000001900); -	else if (conf->channel_type == NL80211_CHAN_HT40PLUS) -		cmd->channel_flags |= cpu_to_le32(0x000000900); +	} + +	if (priv->sw_scan_start) { +		/* Store current channel stats +		 * before switching to newer one. +		 * This will be processed only for AP fw. +		 */ +		if (priv->channel_time != 0) +			mwl8k_update_survey(priv, priv->acs_chan); + +		priv->channel_time = jiffies; +		priv->acs_chan =  channel; +	}  	rc = mwl8k_post_cmd(hw, &cmd->header);  	kfree(cmd); @@ -2428,11 +3215,11 @@ static void legacy_rate_mask_to_array(u8 *rates, u32 mask)  	int j;  	/* -	 * Clear nonstandard rates 4 and 13. +	 * Clear nonstandard rate 4.  	 */  	mask &= 0x1fef; -	for (i = 0, j = 0; i < 14; i++) { +	for (i = 0, j = 0; i < 13; i++) {  		if (mask & (1 << i))  			rates[j++] = mwl8k_rates_24[i].hw_value;  	} @@ -2872,10 +3659,7 @@ static int mwl8k_cmd_enable_sniffer(struct ieee80211_hw *hw, bool enable)  	return rc;  } -/* - * CMD_SET_MAC_ADDR. - */ -struct mwl8k_cmd_set_mac_addr { +struct mwl8k_cmd_update_mac_addr {  	struct mwl8k_cmd_pkt header;  	union {  		struct { @@ -2891,19 +3675,22 @@ struct mwl8k_cmd_set_mac_addr {  #define MWL8K_MAC_TYPE_PRIMARY_AP		2  #define MWL8K_MAC_TYPE_SECONDARY_AP		3 -static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, -				  struct ieee80211_vif *vif, u8 *mac) +static int mwl8k_cmd_update_mac_addr(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif, u8 *mac, bool set)  {  	struct mwl8k_priv *priv = hw->priv;  	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); -	struct mwl8k_cmd_set_mac_addr *cmd; +	struct mwl8k_cmd_update_mac_addr *cmd;  	int mac_type;  	int rc;  	mac_type = MWL8K_MAC_TYPE_PRIMARY_AP;  	if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) {  		if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) -			mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; +			if (priv->ap_fw) +				mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; +			else +				mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT;  		else  			mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT;  	} else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { @@ -2917,7 +3704,11 @@ static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,  	if (cmd == NULL)  		return -ENOMEM; -	cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); +	if (set) +		cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); +	else +		cmd->header.code = cpu_to_le16(MWL8K_CMD_DEL_MAC_ADDR); +  	cmd->header.length = cpu_to_le16(sizeof(*cmd));  	if (priv->ap_fw) {  		cmd->mbss.mac_type = cpu_to_le16(mac_type); @@ -2933,6 +3724,24 @@ static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw,  }  /* + * MWL8K_CMD_SET_MAC_ADDR. + */ +static inline int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif, u8 *mac) +{ +	return mwl8k_cmd_update_mac_addr(hw, vif, mac, true); +} + +/* + * MWL8K_CMD_DEL_MAC_ADDR. + */ +static inline int mwl8k_cmd_del_mac_addr(struct ieee80211_hw *hw, +				  struct ieee80211_vif *vif, u8 *mac) +{ +	return mwl8k_cmd_update_mac_addr(hw, vif, mac, false); +} + +/*   * CMD_SET_RATEADAPT_MODE.   */  struct mwl8k_cmd_set_rate_adapt_mode { @@ -2962,6 +3771,86 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode)  }  /* + * CMD_GET_WATCHDOG_BITMAP. + */ +struct mwl8k_cmd_get_watchdog_bitmap { +	struct mwl8k_cmd_pkt header; +	u8	bitmap; +} __packed; + +static int mwl8k_cmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap) +{ +	struct mwl8k_cmd_get_watchdog_bitmap *cmd; +	int rc; + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return -ENOMEM; + +	cmd->header.code = cpu_to_le16(MWL8K_CMD_GET_WATCHDOG_BITMAP); +	cmd->header.length = cpu_to_le16(sizeof(*cmd)); + +	rc = mwl8k_post_cmd(hw, &cmd->header); +	if (!rc) +		*bitmap = cmd->bitmap; + +	kfree(cmd); + +	return rc; +} + +#define MWL8K_WMM_QUEUE_NUMBER	3 + +static void mwl8k_destroy_ba(struct ieee80211_hw *hw, +			     u8 idx); + +static void mwl8k_watchdog_ba_events(struct work_struct *work) +{ +	int rc; +	u8 bitmap = 0, stream_index; +	struct mwl8k_ampdu_stream *streams; +	struct mwl8k_priv *priv = +		container_of(work, struct mwl8k_priv, watchdog_ba_handle); +	struct ieee80211_hw *hw = priv->hw; +	int i; +	u32 status = 0; + +	mwl8k_fw_lock(hw); + +	rc = mwl8k_cmd_get_watchdog_bitmap(priv->hw, &bitmap); +	if (rc) +		goto done; + +	spin_lock(&priv->stream_lock); + +	/* the bitmap is the hw queue number.  Map it to the ampdu queue. */ +	for (i = 0; i < TOTAL_HW_TX_QUEUES; i++) { +		if (bitmap & (1 << i)) { +			stream_index = (i + MWL8K_WMM_QUEUE_NUMBER) % +				       TOTAL_HW_TX_QUEUES; +			streams = &priv->ampdu[stream_index]; +			if (streams->state == AMPDU_STREAM_ACTIVE) { +				ieee80211_stop_tx_ba_session(streams->sta, +							     streams->tid); +				spin_unlock(&priv->stream_lock); +				mwl8k_destroy_ba(hw, stream_index); +				spin_lock(&priv->stream_lock); +			} +		} +	} + +	spin_unlock(&priv->stream_lock); +done: +	atomic_dec(&priv->watchdog_event_pending); +	status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); +	iowrite32((status | MWL8K_A2H_INT_BA_WATCHDOG), +		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); +	mwl8k_fw_unlock(hw); +	return; +} + + +/*   * CMD_BSS_START.   */  struct mwl8k_cmd_bss_start { @@ -2973,8 +3862,16 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,  			       struct ieee80211_vif *vif, int enable)  {  	struct mwl8k_cmd_bss_start *cmd; +	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); +	struct mwl8k_priv *priv = hw->priv;  	int rc; +	if (enable && (priv->running_bsses & (1 << mwl8k_vif->macid))) +		return 0; + +	if (!enable && !(priv->running_bsses & (1 << mwl8k_vif->macid))) +		return 0; +  	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);  	if (cmd == NULL)  		return -ENOMEM; @@ -2986,9 +3883,178 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw,  	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header);  	kfree(cmd); +	if (!rc) { +		if (enable) +			priv->running_bsses |= (1 << mwl8k_vif->macid); +		else +			priv->running_bsses &= ~(1 << mwl8k_vif->macid); +	}  	return rc;  } +static void mwl8k_enable_bsses(struct ieee80211_hw *hw, bool enable, u32 bitmap) +{ +	struct mwl8k_priv *priv = hw->priv; +	struct mwl8k_vif *mwl8k_vif, *tmp_vif; +	struct ieee80211_vif *vif; + +	list_for_each_entry_safe(mwl8k_vif, tmp_vif, &priv->vif_list, list) { +		vif = mwl8k_vif->vif; + +		if (!(bitmap & (1 << mwl8k_vif->macid))) +			continue; + +		if (vif->type == NL80211_IFTYPE_AP) +			mwl8k_cmd_bss_start(hw, vif, enable); +	} +} +/* + * CMD_BASTREAM. + */ + +/* + * UPSTREAM is tx direction + */ +#define BASTREAM_FLAG_DIRECTION_UPSTREAM	0x00 +#define BASTREAM_FLAG_IMMEDIATE_TYPE		0x01 + +enum ba_stream_action_type { +	MWL8K_BA_CREATE, +	MWL8K_BA_UPDATE, +	MWL8K_BA_DESTROY, +	MWL8K_BA_FLUSH, +	MWL8K_BA_CHECK, +}; + + +struct mwl8k_create_ba_stream { +	__le32	flags; +	__le32	idle_thrs; +	__le32	bar_thrs; +	__le32	window_size; +	u8	peer_mac_addr[6]; +	u8	dialog_token; +	u8	tid; +	u8	queue_id; +	u8	param_info; +	__le32	ba_context; +	u8	reset_seq_no_flag; +	__le16	curr_seq_no; +	u8	sta_src_mac_addr[6]; +} __packed; + +struct mwl8k_destroy_ba_stream { +	__le32	flags; +	__le32	ba_context; +} __packed; + +struct mwl8k_cmd_bastream { +	struct mwl8k_cmd_pkt	header; +	__le32	action; +	union { +		struct mwl8k_create_ba_stream	create_params; +		struct mwl8k_destroy_ba_stream	destroy_params; +	}; +} __packed; + +static int +mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, +	       struct ieee80211_vif *vif) +{ +	struct mwl8k_cmd_bastream *cmd; +	int rc; + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return -ENOMEM; + +	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); +	cmd->header.length = cpu_to_le16(sizeof(*cmd)); + +	cmd->action = cpu_to_le32(MWL8K_BA_CHECK); + +	cmd->create_params.queue_id = stream->idx; +	memcpy(&cmd->create_params.peer_mac_addr[0], stream->sta->addr, +	       ETH_ALEN); +	cmd->create_params.tid = stream->tid; + +	cmd->create_params.flags = +		cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) | +		cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM); + +	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + +	kfree(cmd); + +	return rc; +} + +static int +mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, +		u8 buf_size, struct ieee80211_vif *vif) +{ +	struct mwl8k_cmd_bastream *cmd; +	int rc; + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return -ENOMEM; + + +	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); +	cmd->header.length = cpu_to_le16(sizeof(*cmd)); + +	cmd->action = cpu_to_le32(MWL8K_BA_CREATE); + +	cmd->create_params.bar_thrs = cpu_to_le32((u32)buf_size); +	cmd->create_params.window_size = cpu_to_le32((u32)buf_size); +	cmd->create_params.queue_id = stream->idx; + +	memcpy(cmd->create_params.peer_mac_addr, stream->sta->addr, ETH_ALEN); +	cmd->create_params.tid = stream->tid; +	cmd->create_params.curr_seq_no = cpu_to_le16(0); +	cmd->create_params.reset_seq_no_flag = 1; + +	cmd->create_params.param_info = +		(stream->sta->ht_cap.ampdu_factor & +		 IEEE80211_HT_AMPDU_PARM_FACTOR) | +		((stream->sta->ht_cap.ampdu_density << 2) & +		 IEEE80211_HT_AMPDU_PARM_DENSITY); + +	cmd->create_params.flags = +		cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE | +					BASTREAM_FLAG_DIRECTION_UPSTREAM); + +	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + +	wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n", +		stream->sta->addr, stream->tid); +	kfree(cmd); + +	return rc; +} + +static void mwl8k_destroy_ba(struct ieee80211_hw *hw, +			     u8 idx) +{ +	struct mwl8k_cmd_bastream *cmd; + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return; + +	cmd->header.code = cpu_to_le16(MWL8K_CMD_BASTREAM); +	cmd->header.length = cpu_to_le16(sizeof(*cmd)); +	cmd->action = cpu_to_le32(MWL8K_BA_DESTROY); + +	cmd->destroy_params.ba_context = cpu_to_le32(idx); +	mwl8k_post_cmd(hw, &cmd->header); + +	wiphy_debug(hw->wiphy, "Deleted BA stream index %d\n", idx); + +	kfree(cmd); +} +  /*   * CMD_SET_NEW_STN.   */ @@ -3035,7 +4101,7 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw,  	memcpy(cmd->mac_addr, sta->addr, ETH_ALEN);  	cmd->stn_id = cpu_to_le16(sta->aid);  	cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); -	if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) +	if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)  		rates = sta->supp_rates[IEEE80211_BAND_2GHZ];  	else  		rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; @@ -3081,7 +4147,30 @@ static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,  				     struct ieee80211_vif *vif, u8 *addr)  {  	struct mwl8k_cmd_set_new_stn *cmd; -	int rc; +	struct mwl8k_priv *priv = hw->priv; +	int rc, i; +	u8 idx; + +	spin_lock(&priv->stream_lock); +	/* Destroy any active ampdu streams for this sta */ +	for (i = 0; i < MWL8K_NUM_AMPDU_STREAMS; i++) { +		struct mwl8k_ampdu_stream *s; +		s = &priv->ampdu[i]; +		if (s->state != AMPDU_NO_STREAM) { +			if (memcmp(s->sta->addr, addr, ETH_ALEN) == 0) { +				if (s->state == AMPDU_STREAM_ACTIVE) { +					idx = s->idx; +					spin_unlock(&priv->stream_lock); +					mwl8k_destroy_ba(hw, idx); +					spin_lock(&priv->stream_lock); +				} else if (s->state == AMPDU_STREAM_NEW) { +					mwl8k_remove_stream(hw, s); +				} +			} +		} +	} + +	spin_unlock(&priv->stream_lock);  	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);  	if (cmd == NULL) @@ -3099,6 +4188,271 @@ static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw,  }  /* + * CMD_UPDATE_ENCRYPTION. + */ + +#define MAX_ENCR_KEY_LENGTH	16 +#define MIC_KEY_LENGTH		8 + +struct mwl8k_cmd_update_encryption { +	struct mwl8k_cmd_pkt header; + +	__le32 action; +	__le32 reserved; +	__u8 mac_addr[6]; +	__u8 encr_type; + +} __packed; + +struct mwl8k_cmd_set_key { +	struct mwl8k_cmd_pkt header; + +	__le32 action; +	__le32 reserved; +	__le16 length; +	__le16 key_type_id; +	__le32 key_info; +	__le32 key_id; +	__le16 key_len; +	__u8 key_material[MAX_ENCR_KEY_LENGTH]; +	__u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; +	__u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; +	__le16 tkip_rsc_low; +	__le32 tkip_rsc_high; +	__le16 tkip_tsc_low; +	__le32 tkip_tsc_high; +	__u8 mac_addr[6]; +} __packed; + +enum { +	MWL8K_ENCR_ENABLE, +	MWL8K_ENCR_SET_KEY, +	MWL8K_ENCR_REMOVE_KEY, +	MWL8K_ENCR_SET_GROUP_KEY, +}; + +#define MWL8K_UPDATE_ENCRYPTION_TYPE_WEP	0 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_DISABLE	1 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_TKIP	4 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED	7 +#define MWL8K_UPDATE_ENCRYPTION_TYPE_AES	8 + +enum { +	MWL8K_ALG_WEP, +	MWL8K_ALG_TKIP, +	MWL8K_ALG_CCMP, +}; + +#define MWL8K_KEY_FLAG_TXGROUPKEY	0x00000004 +#define MWL8K_KEY_FLAG_PAIRWISE		0x00000008 +#define MWL8K_KEY_FLAG_TSC_VALID	0x00000040 +#define MWL8K_KEY_FLAG_WEP_TXKEY	0x01000000 +#define MWL8K_KEY_FLAG_MICKEY_VALID	0x02000000 + +static int mwl8k_cmd_update_encryption_enable(struct ieee80211_hw *hw, +					      struct ieee80211_vif *vif, +					      u8 *addr, +					      u8 encr_type) +{ +	struct mwl8k_cmd_update_encryption *cmd; +	int rc; + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return -ENOMEM; + +	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); +	cmd->header.length = cpu_to_le16(sizeof(*cmd)); +	cmd->action = cpu_to_le32(MWL8K_ENCR_ENABLE); +	memcpy(cmd->mac_addr, addr, ETH_ALEN); +	cmd->encr_type = encr_type; + +	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); +	kfree(cmd); + +	return rc; +} + +static int mwl8k_encryption_set_cmd_info(struct mwl8k_cmd_set_key *cmd, +						u8 *addr, +						struct ieee80211_key_conf *key) +{ +	cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_ENCRYPTION); +	cmd->header.length = cpu_to_le16(sizeof(*cmd)); +	cmd->length = cpu_to_le16(sizeof(*cmd) - +				offsetof(struct mwl8k_cmd_set_key, length)); +	cmd->key_id = cpu_to_le32(key->keyidx); +	cmd->key_len = cpu_to_le16(key->keylen); +	memcpy(cmd->mac_addr, addr, ETH_ALEN); + +	switch (key->cipher) { +	case WLAN_CIPHER_SUITE_WEP40: +	case WLAN_CIPHER_SUITE_WEP104: +		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_WEP); +		if (key->keyidx == 0) +			cmd->key_info =	cpu_to_le32(MWL8K_KEY_FLAG_WEP_TXKEY); + +		break; +	case WLAN_CIPHER_SUITE_TKIP: +		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_TKIP); +		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) +			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) +			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); +		cmd->key_info |= cpu_to_le32(MWL8K_KEY_FLAG_MICKEY_VALID +						| MWL8K_KEY_FLAG_TSC_VALID); +		break; +	case WLAN_CIPHER_SUITE_CCMP: +		cmd->key_type_id = cpu_to_le16(MWL8K_ALG_CCMP); +		cmd->key_info =	(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) +			? cpu_to_le32(MWL8K_KEY_FLAG_PAIRWISE) +			: cpu_to_le32(MWL8K_KEY_FLAG_TXGROUPKEY); +		break; +	default: +		return -ENOTSUPP; +	} + +	return 0; +} + +static int mwl8k_cmd_encryption_set_key(struct ieee80211_hw *hw, +						struct ieee80211_vif *vif, +						u8 *addr, +						struct ieee80211_key_conf *key) +{ +	struct mwl8k_cmd_set_key *cmd; +	int rc; +	int keymlen; +	u32 action; +	u8 idx; +	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return -ENOMEM; + +	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); +	if (rc < 0) +		goto done; + +	idx = key->keyidx; + +	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) +		action = MWL8K_ENCR_SET_KEY; +	else +		action = MWL8K_ENCR_SET_GROUP_KEY; + +	switch (key->cipher) { +	case WLAN_CIPHER_SUITE_WEP40: +	case WLAN_CIPHER_SUITE_WEP104: +		if (!mwl8k_vif->wep_key_conf[idx].enabled) { +			memcpy(mwl8k_vif->wep_key_conf[idx].key, key, +						sizeof(*key) + key->keylen); +			mwl8k_vif->wep_key_conf[idx].enabled = 1; +		} + +		keymlen = key->keylen; +		action = MWL8K_ENCR_SET_KEY; +		break; +	case WLAN_CIPHER_SUITE_TKIP: +		keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; +		break; +	case WLAN_CIPHER_SUITE_CCMP: +		keymlen = key->keylen; +		break; +	default: +		rc = -ENOTSUPP; +		goto done; +	} + +	memcpy(cmd->key_material, key->key, keymlen); +	cmd->action = cpu_to_le32(action); + +	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); +done: +	kfree(cmd); + +	return rc; +} + +static int mwl8k_cmd_encryption_remove_key(struct ieee80211_hw *hw, +						struct ieee80211_vif *vif, +						u8 *addr, +						struct ieee80211_key_conf *key) +{ +	struct mwl8k_cmd_set_key *cmd; +	int rc; +	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + +	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); +	if (cmd == NULL) +		return -ENOMEM; + +	rc = mwl8k_encryption_set_cmd_info(cmd, addr, key); +	if (rc < 0) +		goto done; + +	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 || +			key->cipher == WLAN_CIPHER_SUITE_WEP104) +		mwl8k_vif->wep_key_conf[key->keyidx].enabled = 0; + +	cmd->action = cpu_to_le32(MWL8K_ENCR_REMOVE_KEY); + +	rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); +done: +	kfree(cmd); + +	return rc; +} + +static int mwl8k_set_key(struct ieee80211_hw *hw, +			 enum set_key_cmd cmd_param, +			 struct ieee80211_vif *vif, +			 struct ieee80211_sta *sta, +			 struct ieee80211_key_conf *key) +{ +	int rc = 0; +	u8 encr_type; +	u8 *addr; +	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); +	struct mwl8k_priv *priv = hw->priv; + +	if (vif->type == NL80211_IFTYPE_STATION && !priv->ap_fw) +		return -EOPNOTSUPP; + +	if (sta == NULL) +		addr = vif->addr; +	else +		addr = sta->addr; + +	if (cmd_param == SET_KEY) { +		rc = mwl8k_cmd_encryption_set_key(hw, vif, addr, key); +		if (rc) +			goto out; + +		if ((key->cipher == WLAN_CIPHER_SUITE_WEP40) +				|| (key->cipher == WLAN_CIPHER_SUITE_WEP104)) +			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_WEP; +		else +			encr_type = MWL8K_UPDATE_ENCRYPTION_TYPE_MIXED; + +		rc = mwl8k_cmd_update_encryption_enable(hw, vif, addr, +								encr_type); +		if (rc) +			goto out; + +		mwl8k_vif->is_hw_crypto_enabled = true; + +	} else { +		rc = mwl8k_cmd_encryption_remove_key(hw, vif, addr, key); + +		if (rc) +			goto out; +	} +out: +	return rc; +} + +/*   * CMD_UPDATE_STADB.   */  struct ewc_ht_info { @@ -3182,7 +4536,7 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,  	p->ht_caps = cpu_to_le16(sta->ht_cap.cap);  	p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) |  		((sta->ht_cap.ampdu_density & 7) << 2); -	if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) +	if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)  		rates = sta->supp_rates[IEEE80211_BAND_2GHZ];  	else  		rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; @@ -3192,9 +4546,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,  	p->amsdu_enabled = 0;  	rc = mwl8k_post_cmd(hw, &cmd->header); +	if (!rc) +		rc = p->station_id;  	kfree(cmd); -	return rc ? rc : p->station_id; +	return rc;  }  static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, @@ -3242,6 +4598,15 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id)  		tasklet_schedule(&priv->poll_rx_task);  	} +	if (status & MWL8K_A2H_INT_BA_WATCHDOG) { +		iowrite32(~MWL8K_A2H_INT_BA_WATCHDOG, +			  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); + +		atomic_inc(&priv->watchdog_event_pending); +		status &= ~MWL8K_A2H_INT_BA_WATCHDOG; +		ieee80211_queue_work(hw, &priv->watchdog_ba_handle); +	} +  	if (status)  		iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); @@ -3270,7 +4635,7 @@ static void mwl8k_tx_poll(unsigned long data)  	spin_lock_bh(&priv->tx_lock); -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		limit -= mwl8k_txq_reclaim(hw, i, limit, 0);  	if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { @@ -3310,22 +4675,21 @@ static void mwl8k_rx_poll(unsigned long data)  /*   * Core driver operations.   */ -static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void mwl8k_tx(struct ieee80211_hw *hw, +		     struct ieee80211_tx_control *control, +		     struct sk_buff *skb)  {  	struct mwl8k_priv *priv = hw->priv;  	int index = skb_get_queue_mapping(skb); -	int rc;  	if (!priv->radio_on) {  		wiphy_debug(hw->wiphy,  			    "dropped TX frame since radio disabled\n");  		dev_kfree_skb(skb); -		return NETDEV_TX_OK; +		return;  	} -	rc = mwl8k_txq_xmit(hw, index, skb); - -	return rc; +	mwl8k_txq_xmit(hw, index, control->sta, skb);  }  static int mwl8k_start(struct ieee80211_hw *hw) @@ -3336,9 +4700,11 @@ static int mwl8k_start(struct ieee80211_hw *hw)  	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,  			 IRQF_SHARED, MWL8K_NAME, hw);  	if (rc) { +		priv->irq = -1;  		wiphy_err(hw->wiphy, "failed to register IRQ handler\n");  		return -EIO;  	} +	priv->irq = priv->pdev->irq;  	/* Enable TX reclaim and RX tasklets.  */  	tasklet_enable(&priv->poll_tx_task); @@ -3346,6 +4712,8 @@ static int mwl8k_start(struct ieee80211_hw *hw)  	/* Enable interrupts */  	iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); +	iowrite32(MWL8K_A2H_EVENTS, +		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);  	rc = mwl8k_fw_lock(hw);  	if (!rc) { @@ -3375,8 +4743,11 @@ static int mwl8k_start(struct ieee80211_hw *hw)  	if (rc) {  		iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);  		free_irq(priv->pdev->irq, hw); +		priv->irq = -1;  		tasklet_disable(&priv->poll_tx_task);  		tasklet_disable(&priv->poll_rx_task); +	} else { +		ieee80211_wake_queues(hw);  	}  	return rc; @@ -3387,16 +4758,21 @@ static void mwl8k_stop(struct ieee80211_hw *hw)  	struct mwl8k_priv *priv = hw->priv;  	int i; -	mwl8k_cmd_radio_disable(hw); +	if (!priv->hw_restart_in_progress) +		mwl8k_cmd_radio_disable(hw);  	ieee80211_stop_queues(hw);  	/* Disable interrupts */  	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); -	free_irq(priv->pdev->irq, hw); +	if (priv->irq != -1) { +		free_irq(priv->pdev->irq, hw); +		priv->irq = -1; +	}  	/* Stop finalize join worker */  	cancel_work_sync(&priv->finalize_join_worker); +	cancel_work_sync(&priv->watchdog_ba_handle);  	if (priv->beacon_skb != NULL)  		dev_kfree_skb(priv->beacon_skb); @@ -3405,7 +4781,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw)  	tasklet_disable(&priv->poll_rx_task);  	/* Return all skbs to mac80211 */ -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		mwl8k_txq_reclaim(hw, i, INT_MAX, 1);  } @@ -3446,12 +4822,18 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,  		break;  	case NL80211_IFTYPE_STATION:  		if (priv->ap_fw && di->fw_image_sta) { -			/* we must load the sta fw to meet this request */ -			if (!list_empty(&priv->vif_list)) -				return -EBUSY; -			rc = mwl8k_reload_firmware(hw, di->fw_image_sta); -			if (rc) -				return rc; +			if (!list_empty(&priv->vif_list)) { +				wiphy_warn(hw->wiphy, "AP interface is running.\n" +					   "Adding STA interface for WDS"); +			} else { +				/* we must load the sta fw to +				 * meet this request. +				 */ +				rc = mwl8k_reload_firmware(hw, +							   di->fw_image_sta); +				if (rc) +					return rc; +			}  		}  		macids_supported = priv->sta_macids_supported;  		break; @@ -3469,11 +4851,13 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,  	mwl8k_vif->vif = vif;  	mwl8k_vif->macid = macid;  	mwl8k_vif->seqno = 0; +	memcpy(mwl8k_vif->bssid, vif->addr, ETH_ALEN); +	mwl8k_vif->is_hw_crypto_enabled = false;  	/* Set the mac address.  */  	mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); -	if (priv->ap_fw) +	if (vif->type == NL80211_IFTYPE_AP)  		mwl8k_cmd_set_new_stn_add_self(hw, vif);  	priv->macids_used |= 1 << mwl8k_vif->macid; @@ -3482,19 +4866,75 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw,  	return 0;  } +static void mwl8k_remove_vif(struct mwl8k_priv *priv, struct mwl8k_vif *vif) +{ +	/* Has ieee80211_restart_hw re-added the removed interfaces? */ +	if (!priv->macids_used) +		return; + +	priv->macids_used &= ~(1 << vif->macid); +	list_del(&vif->list); +} +  static void mwl8k_remove_interface(struct ieee80211_hw *hw,  				   struct ieee80211_vif *vif)  {  	struct mwl8k_priv *priv = hw->priv;  	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); -	if (priv->ap_fw) +	if (vif->type == NL80211_IFTYPE_AP)  		mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); -	mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00"); +	mwl8k_cmd_del_mac_addr(hw, vif, vif->addr); -	priv->macids_used &= ~(1 << mwl8k_vif->macid); -	list_del(&mwl8k_vif->list); +	mwl8k_remove_vif(priv, mwl8k_vif); +} + +static void mwl8k_hw_restart_work(struct work_struct *work) +{ +	struct mwl8k_priv *priv = +		container_of(work, struct mwl8k_priv, fw_reload); +	struct ieee80211_hw *hw = priv->hw; +	struct mwl8k_device_info *di; +	int rc; + +	/* If some command is waiting for a response, clear it */ +	if (priv->hostcmd_wait != NULL) { +		complete(priv->hostcmd_wait); +		priv->hostcmd_wait = NULL; +	} + +	priv->hw_restart_owner = current; +	di = priv->device_info; +	mwl8k_fw_lock(hw); + +	if (priv->ap_fw) +		rc = mwl8k_reload_firmware(hw, di->fw_image_ap); +	else +		rc = mwl8k_reload_firmware(hw, di->fw_image_sta); + +	if (rc) +		goto fail; + +	priv->hw_restart_owner = NULL; +	priv->hw_restart_in_progress = false; + +	/* +	 * This unlock will wake up the queues and +	 * also opens the command path for other +	 * commands +	 */ +	mwl8k_fw_unlock(hw); + +	ieee80211_restart_hw(hw); + +	wiphy_err(hw->wiphy, "Firmware restarted successfully\n"); + +	return; +fail: +	mwl8k_fw_unlock(hw); + +	wiphy_err(hw->wiphy, "Firmware restart failed\n");  }  static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) @@ -3503,34 +4943,35 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed)  	struct mwl8k_priv *priv = hw->priv;  	int rc; -	if (conf->flags & IEEE80211_CONF_IDLE) { -		mwl8k_cmd_radio_disable(hw); -		return 0; -	} -  	rc = mwl8k_fw_lock(hw);  	if (rc)  		return rc; -	rc = mwl8k_cmd_radio_enable(hw); +	if (conf->flags & IEEE80211_CONF_IDLE) +		rc = mwl8k_cmd_radio_disable(hw); +	else +		rc = mwl8k_cmd_radio_enable(hw);  	if (rc)  		goto out; -	rc = mwl8k_cmd_set_rf_channel(hw, conf); -	if (rc) -		goto out; +	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { +		rc = mwl8k_cmd_set_rf_channel(hw, conf); +		if (rc) +			goto out; +	}  	if (conf->power_level > 18)  		conf->power_level = 18;  	if (priv->ap_fw) { -		rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level); -		if (rc) -			goto out; -		rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x7); -		if (!rc) -			rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); +		if (conf->flags & IEEE80211_CONF_CHANGE_POWER) { +			rc = mwl8k_cmd_tx_power(hw, conf, conf->power_level); +			if (rc) +				goto out; +		} + +  	} else {  		rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level);  		if (rc) @@ -3549,7 +4990,7 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  			   struct ieee80211_bss_conf *info, u32 changed)  {  	struct mwl8k_priv *priv = hw->priv; -	u32 ap_legacy_rates; +	u32 ap_legacy_rates = 0;  	u8 ap_mcs_rates[16];  	int rc; @@ -3576,7 +5017,7 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  			goto out;  		} -		if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) { +		if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) {  			ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ];  		} else {  			ap_legacy_rates = @@ -3587,7 +5028,8 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  		rcu_read_unlock();  	} -	if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { +	if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc && +	    !priv->ap_fw) {  		rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates);  		if (rc)  			goto out; @@ -3595,6 +5037,25 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  		rc = mwl8k_cmd_use_fixed_rate_sta(hw);  		if (rc)  			goto out; +	} else { +		if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc && +		    priv->ap_fw) { +			int idx; +			int rate; + +			/* Use AP firmware specific rate command. +			 */ +			idx = ffs(vif->bss_conf.basic_rates); +			if (idx) +				idx--; + +			if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) +				rate = mwl8k_rates_24[idx].hw_value; +			else +				rate = mwl8k_rates_50[idx].hw_value; + +			mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); +		}  	}  	if (changed & BSS_CHANGED_ERP_PREAMBLE) { @@ -3604,13 +5065,13 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  			goto out;  	} -	if (changed & BSS_CHANGED_ERP_SLOT) { +	if ((changed & BSS_CHANGED_ERP_SLOT) && !priv->ap_fw)  {  		rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot);  		if (rc)  			goto out;  	} -	if (vif->bss_conf.assoc && +	if (vif->bss_conf.assoc && !priv->ap_fw &&  	    (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT |  			BSS_CHANGED_HT))) {  		rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); @@ -3661,7 +5122,7 @@ mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  		if (idx)  			idx--; -		if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) +		if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)  			rate = mwl8k_rates_24[idx].hw_value;  		else  			rate = mwl8k_rates_50[idx].hw_value; @@ -3690,11 +5151,9 @@ static void  mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  		       struct ieee80211_bss_conf *info, u32 changed)  { -	struct mwl8k_priv *priv = hw->priv; - -	if (!priv->ap_fw) +	if (vif->type == NL80211_IFTYPE_STATION)  		mwl8k_bss_info_changed_sta(hw, vif, info, changed); -	else +	if (vif->type == NL80211_IFTYPE_AP)  		mwl8k_bss_info_changed_ap(hw, vif, info, changed);  } @@ -3866,21 +5325,33 @@ static int mwl8k_sta_add(struct ieee80211_hw *hw,  {  	struct mwl8k_priv *priv = hw->priv;  	int ret; +	int i; +	struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); +	struct ieee80211_key_conf *key;  	if (!priv->ap_fw) {  		ret = mwl8k_cmd_update_stadb_add(hw, vif, sta);  		if (ret >= 0) {  			MWL8K_STA(sta)->peer_id = ret; -			return 0; +			if (sta->ht_cap.ht_supported) +				MWL8K_STA(sta)->is_ampdu_allowed = true; +			ret = 0;  		} -		return ret; +	} else { +		ret = mwl8k_cmd_set_new_stn_add(hw, vif, sta);  	} -	return mwl8k_cmd_set_new_stn_add(hw, vif, sta); +	for (i = 0; i < NUM_WEP_KEYS; i++) { +		key = IEEE80211_KEY_CONF(mwl8k_vif->wep_key_conf[i].key); +		if (mwl8k_vif->wep_key_conf[i].enabled) +			mwl8k_set_key(hw, SET_KEY, vif, sta, key); +	} +	return ret;  } -static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, +static int mwl8k_conf_tx(struct ieee80211_hw *hw, +			 struct ieee80211_vif *vif, u16 queue,  			 const struct ieee80211_tx_queue_params *params)  {  	struct mwl8k_priv *priv = hw->priv; @@ -3888,18 +5359,20 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue,  	rc = mwl8k_fw_lock(hw);  	if (!rc) { -		BUG_ON(queue > MWL8K_TX_QUEUES - 1); +		BUG_ON(queue > MWL8K_TX_WMM_QUEUES - 1);  		memcpy(&priv->wmm_params[queue], params, sizeof(*params));  		if (!priv->wmm_enabled)  			rc = mwl8k_cmd_set_wmm_mode(hw, 1); -		if (!rc) -			rc = mwl8k_cmd_set_edca_params(hw, queue, +		if (!rc) { +			int q = MWL8K_TX_WMM_QUEUES - 1 - queue; +			rc = mwl8k_cmd_set_edca_params(hw, q,  						       params->cw_min,  						       params->cw_max,  						       params->aifs,  						       params->txop); +		}  		mwl8k_fw_unlock(hw);  	} @@ -3918,31 +5391,202 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,  {  	struct mwl8k_priv *priv = hw->priv;  	struct ieee80211_conf *conf = &hw->conf; +	struct ieee80211_supported_band *sband; + +	if (priv->ap_fw) { +		sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + +		if (sband && idx >= sband->n_channels) { +			idx -= sband->n_channels; +			sband = NULL; +		} + +		if (!sband) +			sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; + +		if (!sband || idx >= sband->n_channels) +			return -ENOENT; + +		memcpy(survey, &priv->survey[idx], sizeof(*survey)); +		survey->channel = &sband->channels[idx]; + +		return 0; +	}  	if (idx != 0)  		return -ENOENT; -	survey->channel = conf->channel; +	survey->channel = conf->chandef.chan;  	survey->filled = SURVEY_INFO_NOISE_DBM;  	survey->noise = priv->noise;  	return 0;  } +#define MAX_AMPDU_ATTEMPTS 5 +  static int  mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,  		   enum ieee80211_ampdu_mlme_action action, -		   struct ieee80211_sta *sta, u16 tid, u16 *ssn) +		   struct ieee80211_sta *sta, u16 tid, u16 *ssn, +		   u8 buf_size)  { + +	int i, rc = 0; +	struct mwl8k_priv *priv = hw->priv; +	struct mwl8k_ampdu_stream *stream; +	u8 *addr = sta->addr, idx; +	struct mwl8k_sta *sta_info = MWL8K_STA(sta); + +	if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) +		return -ENOTSUPP; + +	spin_lock(&priv->stream_lock); +	stream = mwl8k_lookup_stream(hw, addr, tid); +  	switch (action) {  	case IEEE80211_AMPDU_RX_START:  	case IEEE80211_AMPDU_RX_STOP: -		if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) -			return -ENOTSUPP; -		return 0; +		break; +	case IEEE80211_AMPDU_TX_START: +		/* By the time we get here the hw queues may contain outgoing +		 * packets for this RA/TID that are not part of this BA +		 * session.  The hw will assign sequence numbers to these +		 * packets as they go out.  So if we query the hw for its next +		 * sequence number and use that for the SSN here, it may end up +		 * being wrong, which will lead to sequence number mismatch at +		 * the recipient.  To avoid this, we reset the sequence number +		 * to O for the first MPDU in this BA stream. +		 */ +		*ssn = 0; +		if (stream == NULL) { +			/* This means that somebody outside this driver called +			 * ieee80211_start_tx_ba_session.  This is unexpected +			 * because we do our own rate control.  Just warn and +			 * move on. +			 */ +			wiphy_warn(hw->wiphy, "Unexpected call to %s.  " +				   "Proceeding anyway.\n", __func__); +			stream = mwl8k_add_stream(hw, sta, tid); +		} +		if (stream == NULL) { +			wiphy_debug(hw->wiphy, "no free AMPDU streams\n"); +			rc = -EBUSY; +			break; +		} +		stream->state = AMPDU_STREAM_IN_PROGRESS; + +		/* Release the lock before we do the time consuming stuff */ +		spin_unlock(&priv->stream_lock); +		for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { + +			/* Check if link is still valid */ +			if (!sta_info->is_ampdu_allowed) { +				spin_lock(&priv->stream_lock); +				mwl8k_remove_stream(hw, stream); +				spin_unlock(&priv->stream_lock); +				return -EBUSY; +			} + +			rc = mwl8k_check_ba(hw, stream, vif); + +			/* If HW restart is in progress mwl8k_post_cmd will +			 * return -EBUSY. Avoid retrying mwl8k_check_ba in +			 * such cases +			 */ +			if (!rc || rc == -EBUSY) +				break; +			/* +			 * HW queues take time to be flushed, give them +			 * sufficient time +			 */ + +			msleep(1000); +		} +		spin_lock(&priv->stream_lock); +		if (rc) { +			wiphy_err(hw->wiphy, "Stream for tid %d busy after %d" +				" attempts\n", tid, MAX_AMPDU_ATTEMPTS); +			mwl8k_remove_stream(hw, stream); +			rc = -EBUSY; +			break; +		} +		ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); +		break; +	case IEEE80211_AMPDU_TX_STOP_CONT: +	case IEEE80211_AMPDU_TX_STOP_FLUSH: +	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: +		if (stream) { +			if (stream->state == AMPDU_STREAM_ACTIVE) { +				idx = stream->idx; +				spin_unlock(&priv->stream_lock); +				mwl8k_destroy_ba(hw, idx); +				spin_lock(&priv->stream_lock); +			} +			mwl8k_remove_stream(hw, stream); +		} +		ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); +		break; +	case IEEE80211_AMPDU_TX_OPERATIONAL: +		BUG_ON(stream == NULL); +		BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); +		spin_unlock(&priv->stream_lock); +		rc = mwl8k_create_ba(hw, stream, buf_size, vif); +		spin_lock(&priv->stream_lock); +		if (!rc) +			stream->state = AMPDU_STREAM_ACTIVE; +		else { +			idx = stream->idx; +			spin_unlock(&priv->stream_lock); +			mwl8k_destroy_ba(hw, idx); +			spin_lock(&priv->stream_lock); +			wiphy_debug(hw->wiphy, +				"Failed adding stream for sta %pM tid %d\n", +				addr, tid); +			mwl8k_remove_stream(hw, stream); +		} +		break; +  	default: -		return -ENOTSUPP; +		rc = -ENOTSUPP;  	} + +	spin_unlock(&priv->stream_lock); +	return rc; +} + +static void mwl8k_sw_scan_start(struct ieee80211_hw *hw) +{ +	struct mwl8k_priv *priv = hw->priv; +	u8 tmp; + +	if (!priv->ap_fw) +		return; + +	/* clear all stats */ +	priv->channel_time = 0; +	ioread32(priv->regs + BBU_RXRDY_CNT_REG); +	ioread32(priv->regs + NOK_CCA_CNT_REG); +	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); + +	priv->sw_scan_start = true; +} + +static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw) +{ +	struct mwl8k_priv *priv = hw->priv; +	u8 tmp; + +	if (!priv->ap_fw) +		return; + +	priv->sw_scan_start = false; + +	/* clear all stats */ +	priv->channel_time = 0; +	ioread32(priv->regs + BBU_RXRDY_CNT_REG); +	ioread32(priv->regs + NOK_CCA_CNT_REG); +	mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp);  }  static const struct ieee80211_ops mwl8k_ops = { @@ -3955,6 +5599,7 @@ static const struct ieee80211_ops mwl8k_ops = {  	.bss_info_changed	= mwl8k_bss_info_changed,  	.prepare_multicast	= mwl8k_prepare_multicast,  	.configure_filter	= mwl8k_configure_filter, +	.set_key                = mwl8k_set_key,  	.set_rts_threshold	= mwl8k_set_rts_threshold,  	.sta_add		= mwl8k_sta_add,  	.sta_remove		= mwl8k_sta_remove, @@ -3962,6 +5607,8 @@ static const struct ieee80211_ops mwl8k_ops = {  	.get_stats		= mwl8k_get_stats,  	.get_survey		= mwl8k_get_survey,  	.ampdu_action		= mwl8k_ampdu_action, +	.sw_scan_start		= mwl8k_sw_scan_start, +	.sw_scan_complete	= mwl8k_sw_scan_complete,  };  static void mwl8k_finalize_join_worker(struct work_struct *work) @@ -3988,13 +5635,18 @@ enum {  	MWL8363 = 0,  	MWL8687,  	MWL8366, +	MWL8764,  }; -#define MWL8K_8366_AP_FW_API 1 +#define MWL8K_8366_AP_FW_API 3  #define _MWL8K_8366_AP_FW(api) "mwl8k/fmimage_8366_ap-" #api ".fw"  #define MWL8K_8366_AP_FW(api) _MWL8K_8366_AP_FW(api) -static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { +#define MWL8K_8764_AP_FW_API 1 +#define _MWL8K_8764_AP_FW(api) "mwl8k/fmimage_8764_ap-" #api ".fw" +#define MWL8K_8764_AP_FW(api) _MWL8K_8764_AP_FW(api) + +static struct mwl8k_device_info mwl8k_info_tbl[] = {  	[MWL8363] = {  		.part_name	= "88w8363",  		.helper_image	= "mwl8k/helper_8363.fw", @@ -4011,7 +5663,13 @@ static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = {  		.fw_image_sta	= "mwl8k/fmimage_8366.fw",  		.fw_image_ap	= MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API),  		.fw_api_ap	= MWL8K_8366_AP_FW_API, -		.ap_rxd_ops	= &rxd_8366_ap_ops, +		.ap_rxd_ops	= &rxd_ap_ops, +	}, +	[MWL8764] = { +		.part_name	= "88w8764", +		.fw_image_ap	= MWL8K_8764_AP_FW(MWL8K_8764_AP_FW_API), +		.fw_api_ap	= MWL8K_8764_AP_FW_API, +		.ap_rxd_ops	= &rxd_ap_ops,  	},  }; @@ -4030,7 +5688,10 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {  	{ PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, },  	{ PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, },  	{ PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, +	{ PCI_VDEVICE(MARVELL, 0x2a41), .driver_data = MWL8366, }, +	{ PCI_VDEVICE(MARVELL, 0x2a42), .driver_data = MWL8366, },  	{ PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, +	{ PCI_VDEVICE(MARVELL, 0x2b36), .driver_data = MWL8764, },  	{ },  };  MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); @@ -4126,12 +5787,15 @@ fail:  	mwl8k_release_firmware(priv);  } +#define MAX_RESTART_ATTEMPTS 1  static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,  			       bool nowait)  {  	struct mwl8k_priv *priv = hw->priv;  	int rc; +	int count = MAX_RESTART_ATTEMPTS; +retry:  	/* Reset firmware and hardware */  	mwl8k_hw_reset(priv); @@ -4153,6 +5817,33 @@ static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,  	/* Reclaim memory once firmware is successfully loaded */  	mwl8k_release_firmware(priv); +	if (rc && count) { +		/* FW did not start successfully; +		 * lets try one more time +		 */ +		count--; +		wiphy_err(hw->wiphy, "Trying to reload the firmware again\n"); +		msleep(20); +		goto retry; +	} + +	return rc; +} + +static int mwl8k_init_txqs(struct ieee80211_hw *hw) +{ +	struct mwl8k_priv *priv = hw->priv; +	int rc = 0; +	int i; + +	for (i = 0; i < mwl8k_tx_queues(priv); i++) { +		rc = mwl8k_txq_init(hw, i); +		if (rc) +			break; +		if (priv->ap_fw) +			iowrite32(priv->txq[i].txd_dma, +				  priv->sram + priv->txq_offset[i]); +	}  	return rc;  } @@ -4168,6 +5859,7 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)  		if (priv->rxd_ops == NULL) {  			wiphy_err(hw->wiphy,  				  "Driver does not have AP firmware image support for this hardware\n"); +			rc = -ENOENT;  			goto err_stop_firmware;  		}  	} else { @@ -4177,23 +5869,33 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)  	priv->sniffer_enabled = false;  	priv->wmm_enabled = false;  	priv->pending_tx_pkts = 0; +	atomic_set(&priv->watchdog_event_pending, 0);  	rc = mwl8k_rxq_init(hw, 0);  	if (rc)  		goto err_stop_firmware;  	rxq_refill(hw, 0, INT_MAX); -	for (i = 0; i < MWL8K_TX_QUEUES; i++) { -		rc = mwl8k_txq_init(hw, i); +	/* For the sta firmware, we need to know the dma addresses of tx queues +	 * before sending MWL8K_CMD_GET_HW_SPEC.  So we must initialize them +	 * prior to issuing this command.  But for the AP case, we learn the +	 * total number of queues from the result CMD_GET_HW_SPEC, so for this +	 * case we must initialize the tx queues after. +	 */ +	priv->num_ampdu_queues = 0; +	if (!priv->ap_fw) { +		rc = mwl8k_init_txqs(hw);  		if (rc)  			goto err_free_queues;  	}  	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS);  	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); -	iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY, +	iowrite32(MWL8K_A2H_INT_TX_DONE|MWL8K_A2H_INT_RX_READY| +		  MWL8K_A2H_INT_BA_WATCHDOG,  		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); -	iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); +	iowrite32(MWL8K_A2H_INT_OPC_DONE, +		  priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK);  	rc = request_irq(priv->pdev->irq, mwl8k_interrupt,  			 IRQF_SHARED, MWL8K_NAME, hw); @@ -4203,6 +5905,15 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)  	}  	/* +	 * When hw restart is requested, +	 * mac80211 will take care of clearing +	 * the ampdu streams, so do not clear +	 * the ampdu state here +	 */ +	if (!priv->hw_restart_in_progress) +		memset(priv->ampdu, 0, sizeof(priv->ampdu)); + +	/*  	 * Temporarily enable interrupts.  Initial firmware host  	 * commands use interrupts and avoid polling.  Disable  	 * interrupts when done. @@ -4213,6 +5924,8 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)  	if (priv->ap_fw) {  		rc = mwl8k_cmd_get_hw_spec_ap(hw);  		if (!rc) +			rc = mwl8k_init_txqs(hw); +		if (!rc)  			rc = mwl8k_cmd_set_hw_spec(hw);  	} else {  		rc = mwl8k_cmd_get_hw_spec_sta(hw); @@ -4236,6 +5949,15 @@ static int mwl8k_probe_hw(struct ieee80211_hw *hw)  		goto err_free_irq;  	} +	/* Configure Antennas */ +	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3); +	if (rc) +		wiphy_warn(hw->wiphy, "failed to set # of RX antennas"); +	rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); +	if (rc) +		wiphy_warn(hw->wiphy, "failed to set # of TX antennas"); + +  	/* Disable interrupts */  	iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);  	free_irq(priv->pdev->irq, hw); @@ -4254,7 +5976,7 @@ err_free_irq:  	free_irq(priv->pdev->irq, hw);  err_free_queues: -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		mwl8k_txq_deinit(hw, i);  	mwl8k_rxq_deinit(hw, 0); @@ -4272,11 +5994,21 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)  {  	int i, rc = 0;  	struct mwl8k_priv *priv = hw->priv; +	struct mwl8k_vif *vif, *tmp_vif;  	mwl8k_stop(hw);  	mwl8k_rxq_deinit(hw, 0); -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	/* +	 * All the existing interfaces are re-added by the ieee80211_reconfig; +	 * which means driver should remove existing interfaces before calling +	 * ieee80211_restart_hw +	 */ +	if (priv->hw_restart_in_progress) +		list_for_each_entry_safe(vif, tmp_vif, &priv->vif_list, list) +			mwl8k_remove_vif(priv, vif); + +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		mwl8k_txq_deinit(hw, i);  	rc = mwl8k_init_firmware(hw, fw_image, false); @@ -4287,6 +6019,9 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)  	if (rc)  		goto fail; +	if (priv->hw_restart_in_progress) +		return rc; +  	rc = mwl8k_start(hw);  	if (rc)  		goto fail; @@ -4295,8 +6030,8 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)  	if (rc)  		goto fail; -	for (i = 0; i < MWL8K_TX_QUEUES; i++) { -		rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]); +	for (i = 0; i < MWL8K_TX_WMM_QUEUES; i++) { +		rc = mwl8k_conf_tx(hw, NULL, i, &priv->wmm_params[i]);  		if (rc)  			goto fail;  	} @@ -4308,6 +6043,19 @@ fail:  	return rc;  } +static const struct ieee80211_iface_limit ap_if_limits[] = { +	{ .max = 8,	.types = BIT(NL80211_IFTYPE_AP) }, +	{ .max = 1,	.types = BIT(NL80211_IFTYPE_STATION) }, +}; + +static const struct ieee80211_iface_combination ap_if_comb = { +	.limits = ap_if_limits, +	.n_limits = ARRAY_SIZE(ap_if_limits), +	.max_interfaces = 8, +	.num_different_channels = 1, +}; + +  static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)  {  	struct ieee80211_hw *hw = priv->hw; @@ -4327,12 +6075,20 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)  	hw->extra_tx_headroom =  		sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts); -	hw->channel_change_time = 10; +	hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; -	hw->queues = MWL8K_TX_QUEUES; +	hw->queues = MWL8K_TX_WMM_QUEUES;  	/* Set rssi values to dBm */ -	hw->flags |= IEEE80211_HW_SIGNAL_DBM; +	hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_HAS_RATE_CONTROL; + +	/* +	 * Ask mac80211 to not to trigger PS mode +	 * based on PM bit of incoming frames. +	 */ +	if (priv->ap_fw) +		hw->flags |= IEEE80211_HW_AP_LINK_PS; +  	hw->vif_data_size = sizeof(struct mwl8k_vif);  	hw->sta_data_size = sizeof(struct mwl8k_sta); @@ -4340,11 +6096,15 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)  	INIT_LIST_HEAD(&priv->vif_list);  	/* Set default radio state and preamble */ -	priv->radio_on = 0; -	priv->radio_short_preamble = 0; +	priv->radio_on = false; +	priv->radio_short_preamble = false;  	/* Finalize join worker */  	INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); +	/* Handle watchdog ba events */ +	INIT_WORK(&priv->watchdog_ba_handle, mwl8k_watchdog_ba_events); +	/* To reload the firmware if it crashes */ +	INIT_WORK(&priv->fw_reload, mwl8k_hw_restart_work);  	/* TX reclaim and RX tasklets.  */  	tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); @@ -4364,6 +6124,8 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)  	spin_lock_init(&priv->tx_lock); +	spin_lock_init(&priv->stream_lock); +  	priv->tx_wait = NULL;  	rc = mwl8k_probe_hw(hw); @@ -4371,8 +6133,14 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)  		goto err_free_cookie;  	hw->wiphy->interface_modes = 0; -	if (priv->ap_macids_supported || priv->device_info->fw_image_ap) + +	if (priv->ap_macids_supported || priv->device_info->fw_image_ap) {  		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); +		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); +		hw->wiphy->iface_combinations = &ap_if_comb; +		hw->wiphy->n_iface_combinations = 1; +	} +  	if (priv->sta_macids_supported || priv->device_info->fw_image_sta)  		hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); @@ -4385,7 +6153,7 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)  	return 0;  err_unprobe_hw: -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		mwl8k_txq_deinit(hw, i);  	mwl8k_rxq_deinit(hw, 0); @@ -4396,7 +6164,7 @@ err_free_cookie:  	return rc;  } -static int __devinit mwl8k_probe(struct pci_dev *pdev, +static int mwl8k_probe(struct pci_dev *pdev,  				 const struct pci_device_id *id)  {  	static int printed_version; @@ -4443,10 +6211,13 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,  	priv->pdev = pdev;  	priv->device_info = &mwl8k_info_tbl[id->driver_data]; +	if (id->driver_data == MWL8764) +		priv->is_8764 = true;  	priv->sram = pci_iomap(pdev, 0, 0x10000);  	if (priv->sram == NULL) {  		wiphy_err(hw->wiphy, "Cannot map device SRAM\n"); +		rc = -EIO;  		goto err_iounmap;  	} @@ -4459,6 +6230,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,  		priv->regs = pci_iomap(pdev, 2, 0x10000);  		if (priv->regs == NULL) {  			wiphy_err(hw->wiphy, "Cannot map device registers\n"); +			rc = -EIO;  			goto err_iounmap;  		}  	} @@ -4486,6 +6258,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,  	rc = mwl8k_init_firmware(hw, priv->fw_pref, true);  	if (rc)  		goto err_stop_firmware; + +	priv->hw_restart_in_progress = false; + +	priv->running_bsses = 0; +  	return rc;  err_stop_firmware: @@ -4498,7 +6275,6 @@ err_iounmap:  	if (priv->sram != NULL)  		pci_iounmap(pdev, priv->sram); -	pci_set_drvdata(pdev, NULL);  	ieee80211_free_hw(hw);  err_free_reg: @@ -4510,12 +6286,7 @@ err_disable_device:  	return rc;  } -static void __devexit mwl8k_shutdown(struct pci_dev *pdev) -{ -	printk(KERN_ERR "===>%s(%u)\n", __func__, __LINE__); -} - -static void __devexit mwl8k_remove(struct pci_dev *pdev) +static void mwl8k_remove(struct pci_dev *pdev)  {  	struct ieee80211_hw *hw = pci_get_drvdata(pdev);  	struct mwl8k_priv *priv; @@ -4544,10 +6315,10 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)  	mwl8k_hw_reset(priv);  	/* Return all skbs to mac80211 */ -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		mwl8k_txq_reclaim(hw, i, INT_MAX, 1); -	for (i = 0; i < MWL8K_TX_QUEUES; i++) +	for (i = 0; i < mwl8k_tx_queues(priv); i++)  		mwl8k_txq_deinit(hw, i);  	mwl8k_rxq_deinit(hw, 0); @@ -4557,7 +6328,6 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)  unmap:  	pci_iounmap(pdev, priv->regs);  	pci_iounmap(pdev, priv->sram); -	pci_set_drvdata(pdev, NULL);  	ieee80211_free_hw(hw);  	pci_release_regions(pdev);  	pci_disable_device(pdev); @@ -4567,22 +6337,10 @@ static struct pci_driver mwl8k_driver = {  	.name		= MWL8K_NAME,  	.id_table	= mwl8k_pci_id_table,  	.probe		= mwl8k_probe, -	.remove		= __devexit_p(mwl8k_remove), -	.shutdown	= __devexit_p(mwl8k_shutdown), +	.remove		= mwl8k_remove,  }; -static int __init mwl8k_init(void) -{ -	return pci_register_driver(&mwl8k_driver); -} - -static void __exit mwl8k_exit(void) -{ -	pci_unregister_driver(&mwl8k_driver); -} - -module_init(mwl8k_init); -module_exit(mwl8k_exit); +module_pci_driver(mwl8k_driver);  MODULE_DESCRIPTION(MWL8K_DESC);  MODULE_VERSION(MWL8K_VERSION);  | 
