diff mbox

[v7] Add new mac80211 driver mwlwifi.

Message ID b11fd1b49b274142b85296c11c634635@SC-EXCH02.marvell.com (mailing list archive)
State Changes Requested
Delegated to: Kalle Valo
Headers show

Commit Message

David Lin Nov. 12, 2015, 3:51 a.m. UTC
This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
chipsets.
This driver was developed as part of the openwrt.org project to support
Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwifi.

The mwlwifi driver differs from existing mwifiex driver:
o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem
to provide full AP/Wireless Bridge functionality (routers).
o mwifiex is a "fullmac driver" which provides a comprehensive set of
client functions (laptops/embedded devices)
o only mwlwifi supports Marvell AP chip 886X series

NOTE: Users with Marvell 88W8897 chipsets currently should enable
(CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT BOTH.

mwlwifi driver leveraged code from existing MWL8K driver in the
following areas:
- 802.11n setting for mac80211
- Functions needed to hook up to mac80211
- Interactions with mac80211 to establish BA streams
- Partial firmware APIs, some data fields
- Method to pass Rx packets to mac80211 except 11ac rates

In addition, mwlwifi driver supports:
- future scalability and future development (refactored source code)
- Marvell 802.11ac chipsets, including combo BT devices
- 802.11ac related settings and functions
- concurrent AP+STA functionalities with single firmware per chip
- firmware APIs for the supported chipset
- communicating new mac80211 settings to firmware
- Different TX/RX datapath where applicable
- A-MSDU and A-MPDU
- mac80211-based MESH (work in progress)
- Refined the code to establish BA streams

NOTE: MWLWIFI will be organized under new vendor specific folder/marvell,
as per request of the gate keeper and community.

Signed-off-by: David Lin <dlin@marvell.com>

---
 MAINTAINERS                                     |    6 +
 drivers/net/wireless/Kconfig                    |    1 +
 drivers/net/wireless/Makefile                   |    2 +
 drivers/net/wireless/marvell/Kconfig            |    8 +
 drivers/net/wireless/marvell/Makefile           |    1 +
 drivers/net/wireless/marvell/mwlwifi/Kconfig    |   23 +
 drivers/net/wireless/marvell/mwlwifi/Makefile   |   12 +
 drivers/net/wireless/marvell/mwlwifi/debugfs.c  |  433 ++++
 drivers/net/wireless/marvell/mwlwifi/debugfs.h  |   24 +
 drivers/net/wireless/marvell/mwlwifi/dev.h      |  477 +++++
 drivers/net/wireless/marvell/mwlwifi/fwcmd.c    | 2421 +++++++++++++++++++++++
 drivers/net/wireless/marvell/mwlwifi/fwcmd.h    |  198 ++
 drivers/net/wireless/marvell/mwlwifi/fwdl.c     |  199 ++
 drivers/net/wireless/marvell/mwlwifi/fwdl.h     |   25 +
 drivers/net/wireless/marvell/mwlwifi/hostcmd.h  |  786 ++++++++
 drivers/net/wireless/marvell/mwlwifi/isr.c      |  150 ++
 drivers/net/wireless/marvell/mwlwifi/isr.h      |   26 +
 drivers/net/wireless/marvell/mwlwifi/mac80211.c |  699 +++++++
 drivers/net/wireless/marvell/mwlwifi/main.c     |  739 +++++++
 drivers/net/wireless/marvell/mwlwifi/rx.c       |  624 ++++++
 drivers/net/wireless/marvell/mwlwifi/rx.h       |   25 +
 drivers/net/wireless/marvell/mwlwifi/sysadpt.h  |   73 +
 drivers/net/wireless/marvell/mwlwifi/tx.c       | 1251 ++++++++++++
 drivers/net/wireless/marvell/mwlwifi/tx.h       |   36 +
 24 files changed, 8239 insertions(+)
 create mode 100644 drivers/net/wireless/marvell/Kconfig
 create mode 100644 drivers/net/wireless/marvell/Makefile
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/Kconfig
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/Makefile
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/debugfs.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/dev.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwcmd.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwcmd.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwdl.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/fwdl.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/hostcmd.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/isr.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/isr.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/mac80211.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/main.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/rx.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/rx.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/sysadpt.h
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/tx.c
 create mode 100644 drivers/net/wireless/marvell/mwlwifi/tx.h

-- 
1.9.1

Comments

Jes Sorensen Nov. 12, 2015, 7:47 p.m. UTC | #1
David Lin <dlin@marvell.com> writes:
> This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
> chipsets.
> This driver was developed as part of the openwrt.org project to support
> Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwifi.
>
> The mwlwifi driver differs from existing mwifiex driver:
> o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem
> to provide full AP/Wireless Bridge functionality (routers).
> o mwifiex is a "fullmac driver" which provides a comprehensive set of
> client functions (laptops/embedded devices)
> o only mwlwifi supports Marvell AP chip 886X series
>
> NOTE: Users with Marvell 88W8897 chipsets currently should enable
> (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT BOTH.

I didn't get very far reading through this - but there was a few bits
that stood out like a sore thumb.

> mwlwifi driver leveraged code from existing MWL8K driver in the
> following areas:
> - 802.11n setting for mac80211
> - Functions needed to hook up to mac80211
> - Interactions with mac80211 to establish BA streams
> - Partial firmware APIs, some data fields
> - Method to pass Rx packets to mac80211 except 11ac rates
>
> In addition, mwlwifi driver supports:
> - future scalability and future development (refactored source code)
> - Marvell 802.11ac chipsets, including combo BT devices
> - 802.11ac related settings and functions
> - concurrent AP+STA functionalities with single firmware per chip
> - firmware APIs for the supported chipset
> - communicating new mac80211 settings to firmware
> - Different TX/RX datapath where applicable
> - A-MSDU and A-MPDU
> - mac80211-based MESH (work in progress)
> - Refined the code to establish BA streams
>
> NOTE: MWLWIFI will be organized under new vendor specific folder/marvell,
> as per request of the gate keeper and community.
>
> Signed-off-by: David Lin <dlin@marvell.com>
> ---
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 27b27c0..7c32f0a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6629,6 +6629,12 @@ L:	linux-wireless@vger.kernel.org
>  S:	Odd Fixes
>  F:	drivers/net/wireless/mwl8k.c
>  
> +MARVELL MWLWIFI WIRELESS DRIVER
> +M:	David Lin <dlin@marvell.com>
> +L:	linux-wireless@vger.kernel.org
> +S:	Maintained
> +F:	drivers/net/wireless/marvell/mwlwifi
> +
>  MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
>  M:	Nicolas Pitre <nico@fluxnic.net>
>  S:	Odd Fixes
> diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
> index f9f9422..d599b35 100644
> --- a/drivers/net/wireless/Kconfig
> +++ b/drivers/net/wireless/Kconfig
> @@ -285,5 +285,6 @@ source "drivers/net/wireless/zd1211rw/Kconfig"
>  source "drivers/net/wireless/mwifiex/Kconfig"
>  source "drivers/net/wireless/cw1200/Kconfig"
>  source "drivers/net/wireless/rsi/Kconfig"
> +source "drivers/net/wireless/marvell/Kconfig"
>  
>  endif # WLAN
> diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
> index 740fdd3..71504a7 100644
> --- a/drivers/net/wireless/Makefile
> +++ b/drivers/net/wireless/Makefile
> @@ -60,3 +60,5 @@ obj-$(CONFIG_BRCMSMAC)	+= brcm80211/
>  
>  obj-$(CONFIG_CW1200)	+= cw1200/
>  obj-$(CONFIG_RSI_91X)	+= rsi/
> +
> +obj-$(CONFIG_WL_MARVELL)	+= marvell/
> diff --git a/drivers/net/wireless/marvell/Kconfig
> b/drivers/net/wireless/marvell/Kconfig
> new file mode 100644
> index 0000000..d73e642
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/Kconfig
> @@ -0,0 +1,8 @@
> +menuconfig WL_MARVELL
> +	bool "Marvell Wireless LAN support"
> +	---help---
> +	  Enable community drivers for Marvell Wi-Fi devices.
> +
> +if WL_MARVELL
> +source "drivers/net/wireless/marvell/mwlwifi/Kconfig"
> +endif # WL_MARVELL
> diff --git a/drivers/net/wireless/marvell/Makefile
> b/drivers/net/wireless/marvell/Makefile
> new file mode 100644
> index 0000000..70d1b3f
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_MWLWIFI)	+= mwlwifi/
> diff --git a/drivers/net/wireless/marvell/mwlwifi/Kconfig
> b/drivers/net/wireless/marvell/mwlwifi/Kconfig
> new file mode 100644
> index 0000000..a9bcb9c
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/Kconfig
> @@ -0,0 +1,23 @@
> +config MWLWIFI
> + tristate "Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211
> compatible)"
> +	depends on PCI && MAC80211
> +	select FW_LOADER
> +	---help---
> +		Select to build the driver supporting the:
> +
> +		Marvell Wireless Wi-Fi 88W8864 modules
> +		Marvell Wireless Wi-Fi 88W8897 modules
> +
> +		This driver uses the kernel's mac80211 subsystem.
> +
> + If you want to compile the driver as a module (= code which can be
> + inserted in and removed from the running kernel whenever you want),
> + say M here and read <file:Documentation/kbuild/modules.txt>.  The
> +		module will be called mwlwifi.
> +
> + NOTE: Selecting this driver may cause conflict with MWIFIEX driver
> +		that also operates on the same part number 88W8897. Users should
> +		select either MWIFIEX or MWLWIFI, not both. MWIFIEX is fullmac,
> + supporting more comprehensive client functions for laptops/embedded
> +		devices. MWLWIFI is mac80211-based for full AP/Wireless Bridge.
> +
> diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile
> b/drivers/net/wireless/marvell/mwlwifi/Makefile
> new file mode 100644
> index 0000000..88f7efd
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/Makefile
> @@ -0,0 +1,12 @@
> +obj-$(CONFIG_MWLWIFI)	+= mwlwifi.o
> +
> +mwlwifi-objs		+= main.o
> +mwlwifi-objs		+= mac80211.o
> +mwlwifi-objs		+= fwdl.o
> +mwlwifi-objs		+= fwcmd.o
> +mwlwifi-objs		+= tx.o
> +mwlwifi-objs		+= rx.o
> +mwlwifi-objs		+= isr.o
> +mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
> +
> +ccflags-y += -D__CHECK_ENDIAN__
> diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.c
> b/drivers/net/wireless/marvell/mwlwifi/debugfs.c
> new file mode 100644
> index 0000000..997461a
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.c
> @@ -0,0 +1,433 @@
> +/*
> + * Copyright (C) 2006-2015, Marvell International Ltd.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License").  You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available by writing to the Free Software Foundation, Inc.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +/* Description:  This file implements debug fs related functions. */
> +
> +#include <linux/debugfs.h>
> +
> +#include "sysadpt.h"
> +#include "dev.h"
> +#include "hostcmd.h"
> +#include "fwcmd.h"
> +#include "debugfs.h"
> +
> +#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \
> +	if (!debugfs_create_file(#name, 0644, priv->debugfs_phy, \
> +				 priv, &mwl_debugfs_##name##_fops)) \
> +		return; \
> +} while (0)

This macros relies on implicit arguments which is just bad and doesn't
really do anything except for obfuscating the code.

> +#define MWLWIFI_DEBUGFS_FILE_OPS(name) \
> +static const struct file_operations mwl_debugfs_##name##_fops = { \
> +	.read = mwl_debugfs_##name##_read, \
> +	.write = mwl_debugfs_##name##_write, \
> +	.open = simple_open, \
> +}
> +
> +#define MWLWIFI_DEBUGFS_FILE_READ_OPS(name) \
> +static const struct file_operations mwl_debugfs_##name##_fops = { \
> +	.read = mwl_debugfs_##name##_read, \
> +	.open = simple_open, \
> +}
> +
> +#define MWLWIFI_DEBUGFS_FILE_WRITE_OPS(name) \
> +static const struct file_operations mwl_debugfs_##name##_fops = { \
> +	.write = mwl_debugfs_##name##_write, \
> +	.open = simple_open, \
> +}

Again here - you use thiese wrappers once, just declare the structs
explicitly rather than this macro wrapping dance.

> +static int print_mac_addr(char *p, u8 *mac_addr)
> +{
> +	int i;
> +	char *str = p;
> +
> +	str += sprintf(str, "mac address: %02x", mac_addr[0]);
> +	for (i = 1; i < ETH_ALEN; i++)
> +		str += sprintf(str, ":%02x", mac_addr[i]);
> +	str += sprintf(str, "\n");
> +
> +	return (str - p);
> +}
> +
> +static int dump_data(char *p, u8 *data, int len, char *title)
> +{
> +	char *str = p;
> +	int cur_byte = 0;
> +	int i;
> +
> +	str += sprintf(str, "%s\n", title);
> +	for (cur_byte = 0; cur_byte < len; cur_byte += 8) {
> +		if ((cur_byte + 8) < len) {
> +			for (i = 0; i < 8; i++)
> +				str += sprintf(str, "0x%02x ",
> +					       *(data + cur_byte + i));
> +			str += sprintf(str, "\n");
> +		} else {
> +			for (i = 0; i < (len - cur_byte); i++)
> +				str += sprintf(str, "0x%02x ",
> +					       *(data + cur_byte + i));
> +			str += sprintf(str, "\n");
> +			break;
> +		}
> +	}
> +
> +	return (str - p);
> +}
> +
> +static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf,
> +				     size_t count, loff_t *ppos)
> +{
> +	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
> +	unsigned long page = get_zeroed_page(GFP_KERNEL);
> +	char *p = (char *)page;
> +	ssize_t ret;
> +
> +	if (!p)
> +		return -ENOMEM;
> +
> +	p += sprintf(p, "\n");
> +	p += sprintf(p, "driver name: %s\n", MWL_DRV_NAME);
> +	p += sprintf(p, "chip type: %s\n",
> +		     (priv->chip_type == MWL8864) ? "88W8864" : "88W8897");
> +	p += sprintf(p, "hw version: %X\n", priv->hw_data.hw_version);
> +	p += sprintf(p, "driver version: %s\n", MWL_DRV_VERSION);
> +	p += sprintf(p, "firmware version: 0x%08x\n",
> +		     priv->hw_data.fw_release_num);
> +	p += print_mac_addr(p, priv->hw_data.mac_addr);
> +	p += sprintf(p, "2g: %s\n", priv->disable_2g ? "disable" : "enable");
> +	p += sprintf(p, "5g: %s\n", priv->disable_5g ? "disable" : "enable");
> +	p += sprintf(p, "antenna: %d %d\n",
> +		     (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? 4 : 2,
> +		     (priv->antenna_rx == ANTENNA_TX_4_AUTO) ? 4 : 2);
> +	p += sprintf(p, "irq number: %d\n", priv->irq);
> +	p += sprintf(p, "iobase0: %p\n", priv->iobase0);
> +	p += sprintf(p, "iobase1: %p\n", priv->iobase1);
> +	p += sprintf(p, "tx limit: %d\n", priv->txq_limit);
> +	p += sprintf(p, "rx limit: %d\n", priv->recv_limit);
> +	p += sprintf(p, "ap macid support: %08x\n",
> +		     priv->ap_macids_supported);
> +	p += sprintf(p, "sta macid support: %08x\n",
> +		     priv->sta_macids_supported);
> +	p += sprintf(p, "macid used: %08x\n", priv->macids_used);
> +	p += sprintf(p, "mfg mode: %s\n", priv->mfg_mode ? "true" : "false");
> +	p += sprintf(p, "\n");
> +
> +	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
> +				      (unsigned long)p - page);
> +	free_page(page);
> +
> +	return ret;
> +}
> +
> +static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf,
> +				    size_t count, loff_t *ppos)
> +{
> +	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
> +	unsigned long page = get_zeroed_page(GFP_KERNEL);
> +	char *p = (char *)page;
> +	struct mwl_vif *mwl_vif;
> +	struct ieee80211_vif *vif;
> +	char ssid[IEEE80211_MAX_SSID_LEN + 1];
> +	ssize_t ret;
> +
> +	if (!p)
> +		return -ENOMEM;
> +
> +	p += sprintf(p, "\n");
> +	spin_lock_bh(&priv->vif_lock);
> +	list_for_each_entry(mwl_vif, &priv->vif_list, list) {
> +		vif = container_of((char *)mwl_vif, struct ieee80211_vif,
> +				   drv_priv[0]);
> +		p += sprintf(p, "macid: %d\n", mwl_vif->macid);
> +		switch (vif->type) {
> +		case NL80211_IFTYPE_AP:
> +			p += sprintf(p, "type: ap\n");
> +			memcpy(ssid, vif->bss_conf.ssid,
> +			       vif->bss_conf.ssid_len);
> +			ssid[vif->bss_conf.ssid_len] = 0;
> +			p += sprintf(p, "ssid: %s\n", ssid);
> +			p += print_mac_addr(p, mwl_vif->bssid);
> +			break;
> +		case NL80211_IFTYPE_MESH_POINT:
> +			p += sprintf(p, "type: mesh\n");
> +			p += print_mac_addr(p, mwl_vif->bssid);
> +			break;
> +		case NL80211_IFTYPE_STATION:
> +			p += sprintf(p, "type: sta\n");
> +			p += print_mac_addr(p, mwl_vif->sta_mac);
> +			break;
> +		default:
> +			p += sprintf(p, "type: unknown\n");
> +			break;
> +		}
> +		p += sprintf(p, "hw_crypto_enabled: %s\n",
> +			     mwl_vif->is_hw_crypto_enabled ? "true" : "false");
> +		p += sprintf(p, "key idx: %d\n", mwl_vif->keyidx);
> +		p += sprintf(p, "IV: %08x%04x\n", mwl_vif->iv32, mwl_vif->iv16);
> +		p += dump_data(p, mwl_vif->beacon_info.ie_wmm_ptr,
> +			       mwl_vif->beacon_info.ie_wmm_len, "WMM:");
> +		p += dump_data(p, mwl_vif->beacon_info.ie_rsn_ptr,
> +			       mwl_vif->beacon_info.ie_rsn_len, "RSN:");
> +		p += dump_data(p, mwl_vif->beacon_info.ie_rsn48_ptr,
> +			       mwl_vif->beacon_info.ie_rsn48_len, "RSN48:");
> +		p += dump_data(p, mwl_vif->beacon_info.ie_ht_ptr,
> +			       mwl_vif->beacon_info.ie_ht_len, "HT:");
> +		p += dump_data(p, mwl_vif->beacon_info.ie_vht_ptr,
> +			       mwl_vif->beacon_info.ie_vht_len, "VHT:");
> +		p += sprintf(p, "\n");
> +	}
> +	spin_unlock_bh(&priv->vif_lock);

I hope there is no way that the number of vifs can get to the point
where you overflow the page allocated since there is no cap on the
sprintf() usage in dump_data().

> +
> +MWLWIFI_DEBUGFS_FILE_READ_OPS(info);
> +MWLWIFI_DEBUGFS_FILE_READ_OPS(vif);
> +MWLWIFI_DEBUGFS_FILE_READ_OPS(sta);
> +MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu);
> +MWLWIFI_DEBUGFS_FILE_OPS(regrdwr);
> +
> +void mwl_debugfs_init(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +
> +	if (!priv->debugfs_phy)
> +		priv->debugfs_phy = debugfs_create_dir("mwlwifi",
> +						       hw->wiphy->debugfsdir);
> +
> +	if (!priv->debugfs_phy)
> +		return;
> +
> +	MWLWIFI_DEBUGFS_ADD_FILE(info);
> +	MWLWIFI_DEBUGFS_ADD_FILE(vif);
> +	MWLWIFI_DEBUGFS_ADD_FILE(sta);
> +	MWLWIFI_DEBUGFS_ADD_FILE(ampdu);
> +	MWLWIFI_DEBUGFS_ADD_FILE(regrdwr);
> +}
> +
> +void mwl_debugfs_remove(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +
> +	debugfs_remove(priv->debugfs_phy);
> +	priv->debugfs_phy = NULL;
> +}
> diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h
> b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
> new file mode 100644
> index 0000000..dfc2a3c
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
> @@ -0,0 +1,24 @@
> +/*
> + * Copyright (C) 2006-2015, Marvell International Ltd.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License").  You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available by writing to the Free Software Foundation, Inc.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +/* Description:  This file defines debug fs related functions. */
> +
> +#ifndef _debugfs_h_
> +#define _debugfs_h_

The general convention is to user upper-case for the these - not a
biggie though.

> +struct mwl_priv {
> +	struct ieee80211_hw *hw;
> +	struct firmware *fw_ucode;
> +	int chip_type;
> +
> +	struct device_node *dt_node;
> +	struct device_node *pwr_node;
> +	bool disable_2g;
> +	bool disable_5g;
> +	int antenna_tx;
> +	int antenna_rx;
> +
> +	struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
> +	bool cdd;
> +	u16 txantenna2;
> +	u8 powinited;
> +	u16 max_tx_pow[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* max tx power (dBm) */
> +	u16 target_powers[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* target powers   */
> +	u8 cal_tbl[200];
> +
> +	struct pci_dev *pdev;
> +	void __iomem *iobase0; /* MEM Base Address Register 0  */
> +	void __iomem *iobase1; /* MEM Base Address Register 1  */
> +	u32 next_bar_num;
> +
> +	spinlock_t fwcmd_lock;       /* for firmware command         */
> +	unsigned short *pcmd_buf;    /* pointer to CmdBuf (virtual)  */
> +	dma_addr_t pphys_cmd_buf;    /* pointer to CmdBuf (physical) */
> +	bool in_send_cmd;
> +
> +	int irq;
> +	struct mwl_hw_data hw_data;  /* Adapter HW specific info     */
> +
> +	/* various descriptor data */
> +	spinlock_t tx_desc_lock;     /* for tx descriptor data       */
> +	spinlock_t rx_desc_lock;     /* for rx descriptor data       */

You probably don't want to declare these two right next to each other,
if you expect the RX and TX paths of the code to be able to run in
parallel.

Spin locks are not guaranteed to be cache line aligned or cache line
sized, so you can end up with cache line ping pongs between CPUs in this
case.

> +	struct mwl_desc_data desc_data[SYSADPT_NUM_OF_DESC_DATA];
> +	struct sk_buff_head txq[SYSADPT_NUM_OF_DESC_DATA];
> +	struct sk_buff_head delay_q;
> +	/* number of descriptors owned by fw at any one time */
> +	int fw_desc_cnt[SYSADPT_NUM_OF_DESC_DATA];
> +
> +	struct tasklet_struct tx_task;
> +	struct tasklet_struct rx_task;
> +	struct tasklet_struct qe_task;
> +	int txq_limit;
> +	bool is_tx_schedule;
> +	int recv_limit;
> +	bool is_rx_schedule;
> +	bool is_qe_schedule;
> +	s8 noise;                    /* Most recently reported noise in dBm */
> +	struct ieee80211_supported_band band_24;
> +	struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM];
> +	struct ieee80211_rate rates_24[BAND_24_RATE_NUM];
> +	struct ieee80211_supported_band band_50;
> +	struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM];
> +	struct ieee80211_rate rates_50[BAND_50_RATE_NUM];
> +
> +	u32 ap_macids_supported;
> +	u32 sta_macids_supported;
> +	u32 macids_used;
> +	spinlock_t vif_lock;         /* for private interface info   */
> +	struct list_head vif_list;   /* List of interfaces.          */
> +	u32 running_bsses;           /* bitmap of running BSSes      */
> +
> +	spinlock_t sta_lock;         /* for private sta info         */
> +	struct list_head sta_list;   /* List of stations             */

These two are awfully close together too. You might just get away with
it on x86_64, given list_head is 16 bytes, but the x86_64 packing rules
are odd, so I am never quite sure. On x86_32 it will certainly bite you
- provided vif_lock and sta_lock can get taken from two different paths.

> +
> +	bool radio_on;
> +	bool radio_short_preamble;
> +	bool wmm_enabled;
> +	struct ieee80211_tx_queue_params wmm_params[SYSADPT_TX_WMM_QUEUES];
> +
> +	/* Ampdu stream information */
> +	u8 num_ampdu_queues;
> +	spinlock_t stream_lock;      /* for ampdu stream             */
> +	struct mwl_ampdu_stream ampdu[SYSADPT_TX_AMPDU_QUEUES];
> +	struct work_struct watchdog_ba_handle;
> +
> +	bool mfg_mode;
> +
> +#ifdef CONFIG_DEBUG_FS
> +	struct dentry *debugfs_phy;
> +	u32 reg_type;
> +	u32 reg_offset;
> +	u32 reg_value;
> +#endif
> +};
[snip]

> +static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
> +				   struct mwl_vif *vif, u8 *beacon, int len)
> +{
> +	struct ieee80211_mgmt *mgmt;
> +	struct beacon_info *beacon_info;
> +	int baselen;
> +	u8 *pos;
> +	size_t left;
> +	bool elem_parse_failed;
> +
> +	mgmt = (struct ieee80211_mgmt *)beacon;
> +
> +	baselen = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;
> +	if (baselen > len)
> +		return;
> +
> +	beacon_info = &vif->beacon_info;
> +	memset(beacon_info, 0, sizeof(struct beacon_info));
> +	beacon_info->valid = false;
> +	beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
> +	beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
> +
> +	beacon_info->cap_info = le16_to_cpu(mgmt->u.beacon.capab_info);
> +
> +	pos = (u8 *)mgmt->u.beacon.variable;
> +	left = len - baselen;
> +
> +	elem_parse_failed = false;
> +
> +	while (left >= 2) {
> +		u8 id, elen;
> +
> +		id = *pos++;
> +		elen = *pos++;
> +		left -= 2;
> +
> +		if (elen > left) {
> +			elem_parse_failed = true;
> +			break;
> +		}
> +
> +		switch (id) {
> +		case WLAN_EID_SUPP_RATES:
> +		case WLAN_EID_EXT_SUPP_RATES:
> +			{
> +			int idx, bi, oi;
> +			u8 rate;
> +
> +			for (bi = 0; bi < SYSADPT_MAX_DATA_RATES_G;
> +			     bi++) {
> +				if (beacon_info->b_rate_set[bi] == 0)
> +					break;
> +			}
> +
> +			for (oi = 0; oi < SYSADPT_MAX_DATA_RATES_G;
> +			     oi++) {
> +				if (beacon_info->op_rate_set[oi] == 0)
> +					break;
> +			}
> +
> +			for (idx = 0; idx < elen; idx++) {
> +				rate = pos[idx];
> +				if ((rate & 0x80) != 0) {
> +					if (bi < SYSADPT_MAX_DATA_RATES_G)
> +						beacon_info->b_rate_set[bi++]
> +							= rate & 0x7f;
> +					else {
> +						elem_parse_failed = true;
> +						break;
> +					}
> +				}
> +				if (oi < SYSADPT_MAX_DATA_RATES_G)
> +					beacon_info->op_rate_set[oi++] =
> +						rate & 0x7f;
> +				else {
> +					elem_parse_failed = true;
> +					break;
> +				}
> +			}
> +			}
> +			break;
> +		case WLAN_EID_RSN:
> +			beacon_info->ie_rsn48_len = (elen + 2);
> +			beacon_info->ie_rsn48_ptr = (pos - 2);
> +			break;
> +		case WLAN_EID_HT_CAPABILITY:
> +		case WLAN_EID_HT_OPERATION:
> +		case WLAN_EID_OVERLAP_BSS_SCAN_PARAM:
> +		case WLAN_EID_EXT_CAPABILITY:
> +			beacon_info->ie_ht_len += (elen + 2);
> +			if (beacon_info->ie_ht_len >
> +			    sizeof(beacon_info->ie_list_ht)) {
> +				elem_parse_failed = true;
> +			} else {
> +				*beacon_info->ie_ht_ptr++ = id;
> +				*beacon_info->ie_ht_ptr++ = elen;
> +				memcpy(beacon_info->ie_ht_ptr, pos, elen);
> +				beacon_info->ie_ht_ptr += elen;
> +			}
> +			break;
> +#ifdef CONFIG_MAC80211_MESH
> +		case WLAN_EID_MESH_CONFIG:
> +			beacon_info->ie_meshcfg_len = (elen + 2);
> +			beacon_info->ie_meshcfg_ptr = (pos - 2);
> +			break;
> +		case WLAN_EID_MESH_ID:
> +			beacon_info->ie_meshid_len = (elen + 2);
> +			beacon_info->ie_meshid_ptr = (pos - 2);
> +			break;
> +		case WLAN_EID_CHAN_SWITCH_PARAM:
> +			beacon_info->ie_meshchsw_len = (elen + 2);
> +			beacon_info->ie_meshchsw_ptr = (pos - 2);
> +			break;
> +#endif
> +		case WLAN_EID_VHT_CAPABILITY:
> +		case WLAN_EID_VHT_OPERATION:
> +		case WLAN_EID_OPMODE_NOTIF:
> +			beacon_info->ie_vht_len += (elen + 2);
> +			if (beacon_info->ie_vht_len >
> +			    sizeof(beacon_info->ie_list_vht)) {
> +				elem_parse_failed = true;
> +			} else {
> +				*beacon_info->ie_vht_ptr++ = id;
> +				*beacon_info->ie_vht_ptr++ = elen;
> +				memcpy(beacon_info->ie_vht_ptr, pos, elen);
> +				beacon_info->ie_vht_ptr += elen;
> +			}
> +			break;
> +		case WLAN_EID_VENDOR_SPECIFIC:
> +			if ((pos[0] == 0x00) && (pos[1] == 0x50) &&
> +			    (pos[2] == 0xf2)) {
> +				if (pos[3] == 0x01) {
> +					beacon_info->ie_rsn_len = (elen + 2);
> +					beacon_info->ie_rsn_ptr = (pos - 2);
> +				}
> +
> +				if (pos[3] == 0x02) {
> +					beacon_info->ie_wmm_len = (elen + 2);
> +					beacon_info->ie_wmm_ptr = (pos - 2);
> +				}
> +			}
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		left -= elen;
> +		pos += elen;
> +	}
> +
> +	if (!elem_parse_failed) {
> +		beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
> +		beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
> +		beacon_info->valid = true;
> +	}
> +}

Any reason you're not using cfg80211_find_ie() here? Seems to be
re-inventing the wheel to me.

> +void mwl_fwcmd_reset(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +
> +	if (mwl_fwcmd_chk_adapter(priv))
> +		writel(ISR_RESET,
> +		       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
> +}
> +
> +void mwl_fwcmd_int_enable(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +
> +	if (mwl_fwcmd_chk_adapter(priv)) {
> +		writel(0x00,
> +		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
> +		writel((MACREG_A2HRIC_BIT_MASK),
> +		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);

Please avoid superfluous use of parentheses.

> +int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +	struct hostcmd_cmd_get_hw_spec *pcmd;
> +	int retry;
> +	int i;
> +
> +	if (priv->mfg_mode)
> +		return 0;
> +
> +	pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0];
> +
> +	spin_lock_bh(&priv->fwcmd_lock);
> +
> +	wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd);
> +	memset(pcmd, 0x00, sizeof(*pcmd));
> +	eth_broadcast_addr(pcmd->permanent_addr);
> +	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_HW_SPEC);
> +	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
> +	pcmd->fw_awake_cookie = cpu_to_le32(priv->pphys_cmd_buf + 2048);
> +
> +	retry = 0;
> +	while (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_HW_SPEC)) {
> +		if (retry++ > MAX_WAIT_GET_HW_SPECS_ITERATONS) {
> +			wiphy_err(hw->wiphy, "can't get hw specs\n");
> +			spin_unlock_bh(&priv->fwcmd_lock);
> +			return -EIO;
> +		}
> +
> +		mdelay(1000);
> +		wiphy_debug(hw->wiphy,
> +			    "repeat command = %p\n", pcmd);
> +	}

*cough* mdelay(1000) while holding a spin lock and within in a while()
*loop? *cough*

This needs a little cleaning up. Please have a look at
Documentation/timers/timers-howto.txt

> +
> +	ether_addr_copy(&priv->hw_data.mac_addr[0], pcmd->permanent_addr);
> +	priv->desc_data[0].wcb_base =
> +		le32_to_cpu(pcmd->wcb_base0) & 0x0000ffff;
> +	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
> +		priv->desc_data[i].wcb_base =
> +			le32_to_cpu(pcmd->wcb_base[i - 1]) & 0x0000ffff;
> +	priv->desc_data[0].rx_desc_read =
> +		le32_to_cpu(pcmd->rxpd_rd_ptr) & 0x0000ffff;
> +	priv->desc_data[0].rx_desc_write =
> +		le32_to_cpu(pcmd->rxpd_wr_ptr) & 0x0000ffff;
> +	priv->hw_data.region_code = le16_to_cpu(pcmd->region_code) & 0x00ff;
> +	priv->hw_data.fw_release_num = le32_to_cpu(pcmd->fw_release_num);
> +	priv->hw_data.max_num_tx_desc = le16_to_cpu(pcmd->num_wcb);
> +	priv->hw_data.max_num_mc_addr = le16_to_cpu(pcmd->num_mcast_addr);
> +	priv->hw_data.num_antennas = le16_to_cpu(pcmd->num_antenna);
> +	priv->hw_data.hw_version = pcmd->version;
> +	priv->hw_data.host_interface = pcmd->host_if;
> +
> +	spin_unlock_bh(&priv->fwcmd_lock);
> +
> +	return 0;
> +}
> +
> +int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +	struct hostcmd_cmd_set_hw_spec *pcmd;
> +	int i;
> +
> +	if (priv->mfg_mode)
> +		return 0;
> +
> +	pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0];
> +
> +	spin_lock_bh(&priv->fwcmd_lock);
> +
> +	memset(pcmd, 0x00, sizeof(*pcmd));
> +	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_HW_SPEC);
> +	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
> +	pcmd->wcb_base[0] = cpu_to_le32(priv->desc_data[0].pphys_tx_ring);
> +	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
> +		pcmd->wcb_base[i] =
> +			cpu_to_le32(priv->desc_data[i].pphys_tx_ring);
> +	pcmd->tx_wcb_num_per_queue = cpu_to_le32(SYSADPT_MAX_NUM_TX_DESC);
> +	pcmd->num_tx_queues = cpu_to_le32(SYSADPT_NUM_OF_DESC_DATA);
> +	pcmd->total_rx_wcb = cpu_to_le32(SYSADPT_MAX_NUM_RX_DESC);
> +	pcmd->rxpd_wr_ptr = cpu_to_le32(priv->desc_data[0].pphys_rx_ring);
> +	pcmd->features = 0;
> +
> +	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_HW_SPEC)) {
> +		spin_unlock_bh(&priv->fwcmd_lock);
> +		wiphy_err(hw->wiphy, "failed execution\n");
> +		return -EIO;
> +	}
> +
> +	spin_unlock_bh(&priv->fwcmd_lock);
> +
> +	return 0;
> +}

It looks like fwcmd_lock() is used in this way in every instance. Could
you not allocate a pool of command buffers, put them on a list, and
only take the lock while you pull them out and reinsert them back into
the list?

If you need to hold a lock during firmware command execution, you
probably should switch to a mutex mechanism rather than spin locks.

> +
> +int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,
> +		       struct ieee80211_low_level_stats *stats)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +	struct hostcmd_cmd_802_11_get_stat *pcmd;
> +
> +	pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0];
> +
> +	spin_lock_bh(&priv->fwcmd_lock);
> +
> +	memset(pcmd, 0x00, sizeof(*pcmd));
> +	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_GET_STAT);
> +	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
> +
> +	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_GET_STAT)) {
> +		spin_unlock_bh(&priv->fwcmd_lock);
> +		wiphy_err(hw->wiphy, "failed execution\n");
> +		return -EIO;
> +	}
> +
> +	stats->dot11ACKFailureCount =
> +		le32_to_cpu(pcmd->ack_failures);
> +	stats->dot11RTSFailureCount =
> +		le32_to_cpu(pcmd->rts_failures);
> +	stats->dot11FCSErrorCount =
> +		le32_to_cpu(pcmd->rx_fcs_errors);
> +	stats->dot11RTSSuccessCount =
> +		le32_to_cpu(pcmd->rts_successes);
> +
> +	spin_unlock_bh(&priv->fwcmd_lock);
> +
> +	return 0;
> +}
> +
> +int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw)
> +{
> +	return mwl_fwcmd_802_11_radio_control(hw->priv, true, false);
> +}
> +
> +int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw)
> +{
> +	return mwl_fwcmd_802_11_radio_control(hw->priv, false, false);
> +}

These last two makes me think you only use the spin lock for buffer
management and not the command execution itself?

I am out of time for today, I'll try to see if I can find more time
later - please look into fixing the above.

Jes
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Nov. 20, 2015, 11:22 a.m. UTC | #2
On Thu, 2015-11-12 at 03:51 +0000, David Lin wrote:
> 
> +static int print_mac_addr(char *p, u8 *mac_addr)
> +{
> +	int i;
> +	char *str = p;
> +
> +	str += sprintf(str, "mac address: %02x", mac_addr[0]);
> +	for (i = 1; i < ETH_ALEN; i++)
> +		str += sprintf(str, ":%02x", mac_addr[i]);
> +	str += sprintf(str, "\n");
> +
> +	return (str - p);
> +}

You can use %pM here (and perhaps get rid of the function then)

> +void mwl_debugfs_remove(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv = hw->priv;
> +
> +	debugfs_remove(priv->debugfs_phy);

doesn't that have to be _recursive()?

> +#define MWL_DRV_NAME     KBUILD_MODNAME
> +#define MWL_DRV_VERSION	 "10.3.0.14"

versions like that are generally fairly useless since you don't update
them for every commit ...

> +struct mwl_tx_desc {
> +	u8 data_rate;
> +	u8 tx_priority;
> +	__le16 qos_ctrl;
[...]

I still wouldn't mix device/firmware API with driver-use structures in
the same file, it gets confusing. It's your driver though, so if you
really want to confuse yourselves ... assuming you're going to maintain
it :)

> +struct mwl_sta {
> +	struct list_head list;
> +	bool is_mesh_node;
> +	bool is_ampdu_allowed;
> +	struct mwl_tx_info tx_stats[MWL_MAX_TID];
> +	bool is_amsdu_allowed;
> +	spinlock_t amsdu_lock;      /* for amsdu
> aggregation       */
> +	struct mwl_amsdu_ctrl amsdu_ctrl;
> +	u16 iv16;
> +	u32 iv32;
> +};

I still don't see how this iv stuff in the *station* can possibly be
right. I think I also pointed out earlier that you can just let
mac80211 assign the PN/IV.

> +static int mwl_fwcmd_wait_complete(struct mwl_priv *priv, unsigned
> short cmd)
> +{
> 
[...]
> +	mdelay(3);
> +
> +	return 0;
> +}
> +
> +static int mwl_fwcmd_exec_cmd(struct mwl_priv *priv, unsigned short
> cmd)
> +{
[...]
> +		if (mwl_fwcmd_wait_complete(priv, 0x8000 | cmd)) {
[...]
> +}
> +
> +static int mwl_fwcmd_802_11_radio_control(struct mwl_priv *priv,
> +					  bool enable, bool force)
> +{
[...]
> +	spin_lock_bh(&priv->fwcmd_lock);
[...]
> +	if (mwl_fwcmd_exec_cmd(priv,
> HOSTCMD_CMD_802_11_RADIO_CONTROL)) {
> +		spin_unlock_bh(&priv->fwcmd_lock);

This seems like a terrible idea. Spinning for 3 milliseconds (and more)
while blocking local soft-irqs is really bad for general latency on the
system. I don't like it at all.

And there doesn't really seem to be much reason for it either as far as
I can tell - almost all places can (as I pointed out before) live with
this being a mutex, you just need special handling in very few places
that really do want to send a command while not being able to sleep.

> +++ b/drivers/net/wireless/marvell/mwlwifi/hostcmd.h

This looks like you do have a file for commands - maybe move that TX
thing above here?

> +const struct ieee80211_ops mwl_mac80211_ops = {
> +	.tx                 = mwl_mac80211_tx,
> +	.start              = mwl_mac80211_start,
> +	.stop               = mwl_mac80211_stop,
> +	.add_interface      = mwl_mac80211_add_interface,
> +	.remove_interface   = mwl_mac80211_remove_interface,
> +	.config             = mwl_mac80211_config,
> +	.bss_info_changed   = mwl_mac80211_bss_info_changed,
> +	.configure_filter   = mwl_mac80211_configure_filter,
> +	.set_key            = mwl_mac80211_set_key,
> +	.set_rts_threshold  = mwl_mac80211_set_rts_threshold,
> +	.sta_add            = mwl_mac80211_sta_add,
> +	.sta_remove         = mwl_mac80211_sta_remove,
> +	.conf_tx            = mwl_mac80211_conf_tx,
> +	.get_stats          = mwl_mac80211_get_stats,
> +	.get_survey         = mwl_mac80211_get_survey,
> +	.ampdu_action       = mwl_mac80211_ampdu_action,
> +};

Apart from .tx and .configure_filter, all of these can sleep I think.

> +	rc = mwl_init_firmware(priv, fw_name);
> 
Since you do this in .probe, you should consider loading the firmware
asynchronously.

> +#ifdef CONFIG_SUPPORT_MFG

This Kconfig variable doesn't exist.

> +static inline bool mwl_rx_process_mesh_amsdu(struct mwl_priv *priv,
> +					     struct sk_buff *skb,

	
	
	
Please instead teach mac80211 to do it right.

> +	if (ieee80211_is_data(wh->frame_control)) {
> +		if (is_multicast_ether_addr(wh->addr1)) {
> +			if (ccmp) {
> +				mwl_tx_insert_ccmp_hdr(dma_data-
> >data,
> +						       mwl_vif-
> >keyidx,
> +						       mwl_vif-
> >iv16,
> +						       mwl_vif-
> >iv32);
> +				INCREASE_IV(mwl_vif->iv16, mwl_vif-
> >iv32);
> +			}

Can't you let mac80211 deal with this?

Now I actually am beginning to understand - you need this for GTK only,
so it might even work in the station ... but still, don't do it, set
the flag to make mac80211 do it on the GTK.

> +	if (mgmtframe) {
> +		u16 capab;
> +
> +		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)) {
> +			capab = le16_to_cpu(mgmt-
> >u.action.u.addba_req.capab);
> +			tid = (capab &
> IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
> +			index = mwl_tx_tid_queue_mapping(tid);
> +			capab |= 1;
> +			mgmt->u.action.u.addba_req.capab =
> cpu_to_le16(capab);
> +		}

What's going on here? Why are you modifying the action frames on the
fly??!

johannes
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Lin Nov. 26, 2015, 8:27 a.m. UTC | #3
PiBPbiBOb3ZlbWJlciAyMCwgMjAxNSA3OjIyIFBNLCBKb2hhbm5lcyBCZXJnIHdyb3RlOg0KPiAN
Cj4gT24gVGh1LCAyMDE1LTExLTEyIGF0IDAzOjUxICswMDAwLCBEYXZpZCBMaW4gd3JvdGU6DQo+
ID4NCj4gPiArc3RhdGljIGludCBwcmludF9tYWNfYWRkcihjaGFyICpwLCB1OCAqbWFjX2FkZHIp
IHsNCj4gPiArCWludCBpOw0KPiA+ICsJY2hhciAqc3RyID0gcDsNCj4gPiArDQo+ID4gKwlzdHIg
Kz0gc3ByaW50ZihzdHIsICJtYWMgYWRkcmVzczogJTAyeCIsIG1hY19hZGRyWzBdKTsNCj4gPiAr
CWZvciAoaSA9IDE7IGkgPCBFVEhfQUxFTjsgaSsrKQ0KPiA+ICsJCXN0ciArPSBzcHJpbnRmKHN0
ciwgIjolMDJ4IiwgbWFjX2FkZHJbaV0pOw0KPiA+ICsJc3RyICs9IHNwcmludGYoc3RyLCAiXG4i
KTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gKHN0ciAtIHApOw0KPiA+ICt9DQo+IA0KPiBZb3UgY2Fu
IHVzZSAlcE0gaGVyZSAoYW5kIHBlcmhhcHMgZ2V0IHJpZCBvZiB0aGUgZnVuY3Rpb24gdGhlbikN
Cj4gDQoNCkkgd2lsbCBtb2RpZnkgaXQuDQoNCj4gPiArdm9pZCBtd2xfZGVidWdmc19yZW1vdmUo
c3RydWN0IGllZWU4MDIxMV9odyAqaHcpIHsNCj4gPiArCXN0cnVjdCBtd2xfcHJpdiAqcHJpdiA9
IGh3LT5wcml2Ow0KPiA+ICsNCj4gPiArCWRlYnVnZnNfcmVtb3ZlKHByaXYtPmRlYnVnZnNfcGh5
KTsNCj4gDQo+IGRvZXNuJ3QgdGhhdCBoYXZlIHRvIGJlIF9yZWN1cnNpdmUoKT8NCj4gDQo+ID4g
KyNkZWZpbmUgTVdMX0RSVl9OQU1FwqDCoMKgwqDCoEtCVUlMRF9NT0ROQU1FDQo+ID4gKyNkZWZp
bmUgTVdMX0RSVl9WRVJTSU9OCcKgIjEwLjMuMC4xNCINCj4gDQo+IHZlcnNpb25zIGxpa2UgdGhh
dCBhcmUgZ2VuZXJhbGx5IGZhaXJseSB1c2VsZXNzIHNpbmNlIHlvdSBkb24ndCB1cGRhdGUgdGhl
bSBmb3INCj4gZXZlcnkgY29tbWl0IC4uLg0KPiANCg0KV2Ugc3RpbGwgbmVlZCB2ZXJzaW9uIGlu
Zm9ybWF0aW9uIGZvciBmb3JtYWwgcmVsZWFzZS4NCg0KPiA+ICtzdHJ1Y3QgbXdsX3R4X2Rlc2Mg
ew0KPiA+ICsJdTggZGF0YV9yYXRlOw0KPiA+ICsJdTggdHhfcHJpb3JpdHk7DQo+ID4gKwlfX2xl
MTYgcW9zX2N0cmw7DQo+IFsuLi5dDQo+IA0KPiBJIHN0aWxsIHdvdWxkbid0IG1peCBkZXZpY2Uv
ZmlybXdhcmUgQVBJIHdpdGggZHJpdmVyLXVzZSBzdHJ1Y3R1cmVzIGluIHRoZSBzYW1lDQo+IGZp
bGUsIGl0IGdldHMgY29uZnVzaW5nLiBJdCdzIHlvdXIgZHJpdmVyIHRob3VnaCwgc28gaWYgeW91
IHJlYWxseSB3YW50IHRvIGNvbmZ1c2UNCj4geW91cnNlbHZlcyAuLi4gYXNzdW1pbmcgeW91J3Jl
IGdvaW5nIHRvIG1haW50YWluIGl0IDopDQo+DQoNClRoZXNlIGZpZWxkcyBhcmUgZmlybXdhcmUg
QVBJLiBZZXMsIHdlIHdpbGwgbWFpbnRhaW4gaXQuDQoNCj4gPiArc3RydWN0IG13bF9zdGEgew0K
PiA+ICsJc3RydWN0IGxpc3RfaGVhZCBsaXN0Ow0KPiA+ICsJYm9vbCBpc19tZXNoX25vZGU7DQo+
ID4gKwlib29sIGlzX2FtcGR1X2FsbG93ZWQ7DQo+ID4gKwlzdHJ1Y3QgbXdsX3R4X2luZm8gdHhf
c3RhdHNbTVdMX01BWF9USURdOw0KPiA+ICsJYm9vbCBpc19hbXNkdV9hbGxvd2VkOw0KPiA+ICsJ
c3BpbmxvY2tfdCBhbXNkdV9sb2NrO8KgwqDCoMKgwqDCoC8qIGZvciBhbXNkdQ0KPiA+IGFnZ3Jl
Z2F0aW9uwqDCoMKgwqDCoMKgwqAqLw0KPiA+ICsJc3RydWN0IG13bF9hbXNkdV9jdHJsIGFtc2R1
X2N0cmw7DQo+ID4gKwl1MTYgaXYxNjsNCj4gPiArCXUzMiBpdjMyOw0KPiA+ICt9Ow0KPiANCj4g
SSBzdGlsbCBkb24ndCBzZWUgaG93IHRoaXMgaXYgc3R1ZmYgaW4gdGhlICpzdGF0aW9uKiBjYW4g
cG9zc2libHkgYmUgcmlnaHQuIEkgdGhpbmsgSQ0KPiBhbHNvIHBvaW50ZWQgb3V0IGVhcmxpZXIg
dGhhdCB5b3UgY2FuIGp1c3QgbGV0DQo+IG1hYzgwMjExIGFzc2lnbiB0aGUgUE4vSVYuDQo+IA0K
DQpJIHdpbGwgdHJ5IHRvIG1vZGlmeSB0aGUgY29kZSB0byByZW1vdmUgdGhlbS4NCg0KPiA+ICtz
dGF0aWMgaW50IG13bF9md2NtZF93YWl0X2NvbXBsZXRlKHN0cnVjdCBtd2xfcHJpdiAqcHJpdiwg
dW5zaWduZWQNCj4gPiBzaG9ydCBjbWQpDQo+ID4gK3sNCj4gPg0KPiBbLi4uXQ0KPiA+ICsJbWRl
bGF5KDMpOw0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0
aWMgaW50IG13bF9md2NtZF9leGVjX2NtZChzdHJ1Y3QgbXdsX3ByaXYgKnByaXYsIHVuc2lnbmVk
IHNob3J0DQo+ID4gY21kKQ0KPiA+ICt7DQo+IFsuLi5dDQo+ID4gKwkJaWYgKG13bF9md2NtZF93
YWl0X2NvbXBsZXRlKHByaXYsIDB4ODAwMCB8IGNtZCkpIHsNCj4gWy4uLl0NCj4gPiArfQ0KPiA+
ICsNCj4gPiArc3RhdGljIGludCBtd2xfZndjbWRfODAyXzExX3JhZGlvX2NvbnRyb2woc3RydWN0
IG13bF9wcml2ICpwcml2LA0KPiA+ICsJCQkJCcKgwqBib29sIGVuYWJsZSwgYm9vbCBmb3JjZSkN
Cj4gPiArew0KPiBbLi4uXQ0KPiA+ICsJc3Bpbl9sb2NrX2JoKCZwcml2LT5md2NtZF9sb2NrKTsN
Cj4gWy4uLl0NCj4gPiArCWlmIChtd2xfZndjbWRfZXhlY19jbWQocHJpdiwNCj4gPiBIT1NUQ01E
X0NNRF84MDJfMTFfUkFESU9fQ09OVFJPTCkpIHsNCj4gPiArCQlzcGluX3VubG9ja19iaCgmcHJp
di0+ZndjbWRfbG9jayk7DQo+IA0KPiBUaGlzIHNlZW1zIGxpa2UgYSB0ZXJyaWJsZSBpZGVhLiBT
cGlubmluZyBmb3IgMyBtaWxsaXNlY29uZHMgKGFuZCBtb3JlKSB3aGlsZQ0KPiBibG9ja2luZyBs
b2NhbCBzb2Z0LWlycXMgaXMgcmVhbGx5IGJhZCBmb3IgZ2VuZXJhbCBsYXRlbmN5IG9uIHRoZSBz
eXN0ZW0uIEkgZG9uJ3QNCj4gbGlrZSBpdCBhdCBhbGwuDQo+IA0KPiBBbmQgdGhlcmUgZG9lc24n
dCByZWFsbHkgc2VlbSB0byBiZSBtdWNoIHJlYXNvbiBmb3IgaXQgZWl0aGVyIGFzIGZhciBhcyBJ
IGNhbg0KPiB0ZWxsIC0gYWxtb3N0IGFsbCBwbGFjZXMgY2FuIChhcyBJIHBvaW50ZWQgb3V0IGJl
Zm9yZSkgbGl2ZSB3aXRoIHRoaXMgYmVpbmcgYQ0KPiBtdXRleCwgeW91IGp1c3QgbmVlZCBzcGVj
aWFsIGhhbmRsaW5nIGluIHZlcnkgZmV3IHBsYWNlcyB0aGF0IHJlYWxseSBkbyB3YW50IHRvDQo+
IHNlbmQgYSBjb21tYW5kIHdoaWxlIG5vdCBiZWluZyBhYmxlIHRvIHNsZWVwLg0KPiANCg0KSSB3
aWxsIHRyeSB0byB1c2UgbXV0ZXggaW4gbmV4dCBwYXRjaCBzb29uLg0KDQo+ID4gKysrIGIvZHJp
dmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL2hvc3RjbWQuaA0KPiANCj4gVGhpcyBs
b29rcyBsaWtlIHlvdSBkbyBoYXZlIGEgZmlsZSBmb3IgY29tbWFuZHMgLSBtYXliZSBtb3ZlIHRo
YXQgVFggdGhpbmcNCj4gYWJvdmUgaGVyZT8NCj4gDQoNCkl0IGlzIGJldHRlciB0byBrZWVwIGN1
cnJlbnQgZmlsZSBzdHJ1Y3R1cmUgKGVhc2llciBmb3Igb3VyIGludGVybmFsIG1haW50ZW5hbmNl
IHN5bmNpbmcgb3RoZXIgZmlsZXMpDQoNCj4gPiArY29uc3Qgc3RydWN0IGllZWU4MDIxMV9vcHMg
bXdsX21hYzgwMjExX29wcyA9IHsNCj4gPiArCS50eMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgwqA9IG13bF9tYWM4MDIxMV90eCwNCj4gPiArCS5zdGFydMKgwqDCoMKgwqDCoMKgwqDC
oMKgwqDCoMKgwqA9IG13bF9tYWM4MDIxMV9zdGFydCwNCj4gPiArCS5zdG9wwqDCoMKgwqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgPSBtd2xfbWFjODAyMTFfc3RvcCwNCj4gPiArCS5hZGRfaW50ZXJm
YWNlwqDCoMKgwqDCoMKgPSBtd2xfbWFjODAyMTFfYWRkX2ludGVyZmFjZSwNCj4gPiArCS5yZW1v
dmVfaW50ZXJmYWNlwqDCoMKgPSBtd2xfbWFjODAyMTFfcmVtb3ZlX2ludGVyZmFjZSwNCj4gPiAr
CS5jb25maWfCoMKgwqDCoMKgwqDCoMKgwqDCoMKgwqDCoD0gbXdsX21hYzgwMjExX2NvbmZpZywN
Cj4gPiArCS5ic3NfaW5mb19jaGFuZ2VkwqDCoMKgPSBtd2xfbWFjODAyMTFfYnNzX2luZm9fY2hh
bmdlZCwNCj4gPiArCS5jb25maWd1cmVfZmlsdGVywqDCoMKgPSBtd2xfbWFjODAyMTFfY29uZmln
dXJlX2ZpbHRlciwNCj4gPiArCS5zZXRfa2V5wqDCoMKgwqDCoMKgwqDCoMKgwqDCoMKgPSBtd2xf
bWFjODAyMTFfc2V0X2tleSwNCj4gPiArCS5zZXRfcnRzX3RocmVzaG9sZMKgwqA9IG13bF9tYWM4
MDIxMV9zZXRfcnRzX3RocmVzaG9sZCwNCj4gPiArCS5zdGFfYWRkwqDCoMKgwqDCoMKgwqDCoMKg
wqDCoMKgPSBtd2xfbWFjODAyMTFfc3RhX2FkZCwNCj4gPiArCS5zdGFfcmVtb3ZlwqDCoMKgwqDC
oMKgwqDCoMKgPSBtd2xfbWFjODAyMTFfc3RhX3JlbW92ZSwNCj4gPiArCS5jb25mX3R4wqDCoMKg
wqDCoMKgwqDCoMKgwqDCoMKgPSBtd2xfbWFjODAyMTFfY29uZl90eCwNCj4gPiArCS5nZXRfc3Rh
dHPCoMKgwqDCoMKgwqDCoMKgwqDCoD0gbXdsX21hYzgwMjExX2dldF9zdGF0cywNCj4gPiArCS5n
ZXRfc3VydmV5wqDCoMKgwqDCoMKgwqDCoMKgPSBtd2xfbWFjODAyMTFfZ2V0X3N1cnZleSwNCj4g
PiArCS5hbXBkdV9hY3Rpb27CoMKgwqDCoMKgwqDCoD0gbXdsX21hYzgwMjExX2FtcGR1X2FjdGlv
biwgfTsNCj4gDQo+IEFwYXJ0IGZyb20gLnR4IGFuZCAuY29uZmlndXJlX2ZpbHRlciwgYWxsIG9m
IHRoZXNlIGNhbiBzbGVlcCBJIHRoaW5rLg0KPiANCj4gPiArCXJjID0gbXdsX2luaXRfZmlybXdh
cmUocHJpdiwgZndfbmFtZSk7DQo+ID4NCj4gU2luY2UgeW91IGRvIHRoaXMgaW4gLnByb2JlLCB5
b3Ugc2hvdWxkIGNvbnNpZGVyIGxvYWRpbmcgdGhlIGZpcm13YXJlDQo+IGFzeW5jaHJvbm91c2x5
Lg0KPiANCg0KSSBob3BlIEkgY2FuIHBvc3Rwb25lIHRoaXMgbW9kaWZpY2F0aW9uIGxhdGVyLg0K
DQo+ID4gKyNpZmRlZiBDT05GSUdfU1VQUE9SVF9NRkcNCj4gDQo+IFRoaXMgS2NvbmZpZyB2YXJp
YWJsZSBkb2Vzbid0IGV4aXN0Lg0KPiANCg0KVGhlIGNvbXBpbGUgdmFyaWFibGUgaXMgdXNlZCBw
cml2YXRlbHkgYnkgTWFydmVsbCBhbmQgb3VyIGN1c3RvbWVycyBpbiBwcm9kdWN0aW9uIGxpbmUu
DQoNCj4gPiArc3RhdGljIGlubGluZSBib29sIG13bF9yeF9wcm9jZXNzX21lc2hfYW1zZHUoc3Ry
dWN0IG13bF9wcml2ICpwcml2LA0KPiA+ICsJCQkJCcKgwqDCoMKgwqBzdHJ1Y3Qgc2tfYnVmZiAq
c2tiLA0KPiANCj4gDQo+IA0KPiANCj4gUGxlYXNlIGluc3RlYWQgdGVhY2ggbWFjODAyMTEgdG8g
ZG8gaXQgcmlnaHQuDQo+IA0KDQpNYWM4MDIxMSBkb2VzIG5vdCBzdXBwb3J0IG1lc2ggQU1TRFUg
bm93LCB3ZSBpbXBsZW1lbnQgdGhpcyBmdW5jdGlvbiBpbiBtd2x3aWZpIGRyaXZlciBmb3IgdGhl
IHRpbWUgYmVpbmcuIEV4Y2VwdCBmb3IgbWVzaCBBTVNEVSwgd2Ugc3RpbGwgbGV2ZXJhZ2UgbWFj
ODAyMTEgdG8gaGFuZGxlIGRhdGEgQU1TRFUuDQoNCj4gPiArCWlmIChpZWVlODAyMTFfaXNfZGF0
YSh3aC0+ZnJhbWVfY29udHJvbCkpIHsNCj4gPiArCQlpZiAoaXNfbXVsdGljYXN0X2V0aGVyX2Fk
ZHIod2gtPmFkZHIxKSkgew0KPiA+ICsJCQlpZiAoY2NtcCkgew0KPiA+ICsJCQkJbXdsX3R4X2lu
c2VydF9jY21wX2hkcihkbWFfZGF0YS0NCj4gPiA+ZGF0YSwNCj4gPiArCQkJCQkJwqDCoMKgwqDC
oMKgwqBtd2xfdmlmLQ0KPiA+ID5rZXlpZHgsDQo+ID4gKwkJCQkJCcKgwqDCoMKgwqDCoMKgbXds
X3ZpZi0NCj4gPiA+aXYxNiwNCj4gPiArCQkJCQkJwqDCoMKgwqDCoMKgwqBtd2xfdmlmLQ0KPiA+
ID5pdjMyKTsNCj4gPiArCQkJCUlOQ1JFQVNFX0lWKG13bF92aWYtPml2MTYsIG13bF92aWYtDQo+
ID4gPml2MzIpOw0KPiA+ICsJCQl9DQo+IA0KPiBDYW4ndCB5b3UgbGV0IG1hYzgwMjExIGRlYWwg
d2l0aCB0aGlzPw0KPiANCj4gTm93IEkgYWN0dWFsbHkgYW0gYmVnaW5uaW5nIHRvIHVuZGVyc3Rh
bmQgLSB5b3UgbmVlZCB0aGlzIGZvciBHVEsgb25seSwgc28gaXQNCj4gbWlnaHQgZXZlbiB3b3Jr
IGluIHRoZSBzdGF0aW9uIC4uLiBidXQgc3RpbGwsIGRvbid0IGRvIGl0LCBzZXQgdGhlIGZsYWcg
dG8gbWFrZQ0KPiBtYWM4MDIxMSBkbyBpdCBvbiB0aGUgR1RLLg0KPiANCg0KSSB3aWxsIGNoZWNr
IG1hYzgwMjExIHRvIHNlZSBpZiBJIGNhbiBsZXQgbWFjODAyMTEgZG8gaXQuDQoNCj4gPiArCWlm
IChtZ210ZnJhbWUpIHsNCj4gPiArCQl1MTYgY2FwYWI7DQo+ID4gKw0KPiA+ICsJCWlmICh1bmxp
a2VseShpZWVlODAyMTFfaXNfYWN0aW9uKHdoLT5mcmFtZV9jb250cm9sKQ0KPiA+ICYmDQo+ID4g
KwkJCcKgwqDCoMKgwqBtZ210LT51LmFjdGlvbi5jYXRlZ29yeSA9PQ0KPiA+IFdMQU5fQ0FURUdP
UllfQkFDSyAmJg0KPiA+ICsJCQnCoMKgwqDCoMKgbWdtdC0+dS5hY3Rpb24udS5hZGRiYV9yZXEu
YWN0aW9uX2NvZGUNCj4gPiA9PQ0KPiA+ICsJCQnCoMKgwqDCoMKgV0xBTl9BQ1RJT05fQUREQkFf
UkVRKSkgew0KPiA+ICsJCQljYXBhYiA9IGxlMTZfdG9fY3B1KG1nbXQtDQo+ID4gPnUuYWN0aW9u
LnUuYWRkYmFfcmVxLmNhcGFiKTsNCj4gPiArCQkJdGlkID0gKGNhcGFiICYNCj4gPiBJRUVFODAy
MTFfQUREQkFfUEFSQU1fVElEX01BU0spID4+IDI7DQo+ID4gKwkJCWluZGV4ID0gbXdsX3R4X3Rp
ZF9xdWV1ZV9tYXBwaW5nKHRpZCk7DQo+ID4gKwkJCWNhcGFiIHw9IDE7DQo+ID4gKwkJCW1nbXQt
PnUuYWN0aW9uLnUuYWRkYmFfcmVxLmNhcGFiID0NCj4gPiBjcHVfdG9fbGUxNihjYXBhYik7DQo+
ID4gKwkJfQ0KPiANCj4gV2hhdCdzIGdvaW5nIG9uIGhlcmU/IFdoeSBhcmUgeW91IG1vZGlmeWlu
ZyB0aGUgYWN0aW9uIGZyYW1lcyBvbiB0aGUgZmx5Pz8hDQo+IA0KDQpEdWUgdG8gbXdsd2lmaSBz
dXBwb3J0cyBUeC9SeCBBTVNEVSwgdGhlc2UgZnJhbWVzIGFyZSBtb2RpZmllZCB0byBpbmZvcm0g
Y2xpZW50IHRoYXQgd2Ugc3VwcG9ydCBBTVNEVS4NCkZvciBSeCBBTVNEVSwgbXdsd2lmaSBoYW5k
bGVkIG1lc2ggQU1TRFUgYW5kIGxldCBtYWM4MDIxMSBoYW5kbGUgZGF0YSBBTVNEVS4NCg0KPiBq
b2hhbm5lcw0K
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Lin Nov. 26, 2015, 8:34 a.m. UTC | #4
> On November 13, 2015 3:48 AM, Jes Sorensen wrote:

> 

> David Lin <dlin@marvell.com> writes:

> > This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897

> > chipsets.

> > This driver was developed as part of the openwrt.org project to

> > support Linksys WRT1900AC and is maintained on

> https://github.com/kaloz/mwlwifi.

> >

> > The mwlwifi driver differs from existing mwifiex driver:

> > o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem

> > to provide full AP/Wireless Bridge functionality (routers).

> > o mwifiex is a "fullmac driver" which provides a comprehensive set of

> > client functions (laptops/embedded devices) o only mwlwifi supports

> > Marvell AP chip 886X series

> >

> > NOTE: Users with Marvell 88W8897 chipsets currently should enable

> > (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT

> BOTH.

> 

> I didn't get very far reading through this - but there was a few bits that stood

> out like a sore thumb.

> 

> > mwlwifi driver leveraged code from existing MWL8K driver in the

> > following areas:

> > - 802.11n setting for mac80211

> > - Functions needed to hook up to mac80211

> > - Interactions with mac80211 to establish BA streams

> > - Partial firmware APIs, some data fields

> > - Method to pass Rx packets to mac80211 except 11ac rates

> >

> > In addition, mwlwifi driver supports:

> > - future scalability and future development (refactored source code)

> > - Marvell 802.11ac chipsets, including combo BT devices

> > - 802.11ac related settings and functions

> > - concurrent AP+STA functionalities with single firmware per chip

> > - firmware APIs for the supported chipset

> > - communicating new mac80211 settings to firmware

> > - Different TX/RX datapath where applicable

> > - A-MSDU and A-MPDU

> > - mac80211-based MESH (work in progress)

> > - Refined the code to establish BA streams

> >

> > NOTE: MWLWIFI will be organized under new vendor specific

> > folder/marvell, as per request of the gate keeper and community.

> >

> > Signed-off-by: David Lin <dlin@marvell.com>

> > ---

> >

> > diff --git a/MAINTAINERS b/MAINTAINERS index 27b27c0..7c32f0a 100644

> > --- a/MAINTAINERS

> > +++ b/MAINTAINERS

> > @@ -6629,6 +6629,12 @@ L:	linux-wireless@vger.kernel.org

> >  S:	Odd Fixes

> >  F:	drivers/net/wireless/mwl8k.c

> >

> > +MARVELL MWLWIFI WIRELESS DRIVER

> > +M:	David Lin <dlin@marvell.com>

> > +L:	linux-wireless@vger.kernel.org

> > +S:	Maintained

> > +F:	drivers/net/wireless/marvell/mwlwifi

> > +

> >  MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER

> >  M:	Nicolas Pitre <nico@fluxnic.net>

> >  S:	Odd Fixes

> > diff --git a/drivers/net/wireless/Kconfig

> > b/drivers/net/wireless/Kconfig index f9f9422..d599b35 100644

> > --- a/drivers/net/wireless/Kconfig

> > +++ b/drivers/net/wireless/Kconfig

> > @@ -285,5 +285,6 @@ source "drivers/net/wireless/zd1211rw/Kconfig"

> >  source "drivers/net/wireless/mwifiex/Kconfig"

> >  source "drivers/net/wireless/cw1200/Kconfig"

> >  source "drivers/net/wireless/rsi/Kconfig"

> > +source "drivers/net/wireless/marvell/Kconfig"

> >

> >  endif # WLAN

> > diff --git a/drivers/net/wireless/Makefile

> > b/drivers/net/wireless/Makefile index 740fdd3..71504a7 100644

> > --- a/drivers/net/wireless/Makefile

> > +++ b/drivers/net/wireless/Makefile

> > @@ -60,3 +60,5 @@ obj-$(CONFIG_BRCMSMAC)	+= brcm80211/

> >

> >  obj-$(CONFIG_CW1200)	+= cw1200/

> >  obj-$(CONFIG_RSI_91X)	+= rsi/

> > +

> > +obj-$(CONFIG_WL_MARVELL)	+= marvell/

> > diff --git a/drivers/net/wireless/marvell/Kconfig

> > b/drivers/net/wireless/marvell/Kconfig

> > new file mode 100644

> > index 0000000..d73e642

> > --- /dev/null

> > +++ b/drivers/net/wireless/marvell/Kconfig

> > @@ -0,0 +1,8 @@

> > +menuconfig WL_MARVELL

> > +	bool "Marvell Wireless LAN support"

> > +	---help---

> > +	  Enable community drivers for Marvell Wi-Fi devices.

> > +

> > +if WL_MARVELL

> > +source "drivers/net/wireless/marvell/mwlwifi/Kconfig"

> > +endif # WL_MARVELL

> > diff --git a/drivers/net/wireless/marvell/Makefile

> > b/drivers/net/wireless/marvell/Makefile

> > new file mode 100644

> > index 0000000..70d1b3f

> > --- /dev/null

> > +++ b/drivers/net/wireless/marvell/Makefile

> > @@ -0,0 +1 @@

> > +obj-$(CONFIG_MWLWIFI)	+= mwlwifi/

> > diff --git a/drivers/net/wireless/marvell/mwlwifi/Kconfig

> > b/drivers/net/wireless/marvell/mwlwifi/Kconfig

> > new file mode 100644

> > index 0000000..a9bcb9c

> > --- /dev/null

> > +++ b/drivers/net/wireless/marvell/mwlwifi/Kconfig

> > @@ -0,0 +1,23 @@

> > +config MWLWIFI

> > + tristate "Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211

> > compatible)"

> > +	depends on PCI && MAC80211

> > +	select FW_LOADER

> > +	---help---

> > +		Select to build the driver supporting the:

> > +

> > +		Marvell Wireless Wi-Fi 88W8864 modules

> > +		Marvell Wireless Wi-Fi 88W8897 modules

> > +

> > +		This driver uses the kernel's mac80211 subsystem.

> > +

> > + If you want to compile the driver as a module (= code which can be

> > +inserted in and removed from the running kernel whenever you want),

> > +say M here and read <file:Documentation/kbuild/modules.txt>.  The

> > +		module will be called mwlwifi.

> > +

> > + NOTE: Selecting this driver may cause conflict with MWIFIEX driver

> > +		that also operates on the same part number 88W8897. Users should

> > +		select either MWIFIEX or MWLWIFI, not both. MWIFIEX is fullmac,

> > +supporting more comprehensive client functions for laptops/embedded

> > +		devices. MWLWIFI is mac80211-based for full AP/Wireless Bridge.

> > +

> > diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile

> > b/drivers/net/wireless/marvell/mwlwifi/Makefile

> > new file mode 100644

> > index 0000000..88f7efd

> > --- /dev/null

> > +++ b/drivers/net/wireless/marvell/mwlwifi/Makefile

> > @@ -0,0 +1,12 @@

> > +obj-$(CONFIG_MWLWIFI)	+= mwlwifi.o

> > +

> > +mwlwifi-objs		+= main.o

> > +mwlwifi-objs		+= mac80211.o

> > +mwlwifi-objs		+= fwdl.o

> > +mwlwifi-objs		+= fwcmd.o

> > +mwlwifi-objs		+= tx.o

> > +mwlwifi-objs		+= rx.o

> > +mwlwifi-objs		+= isr.o

> > +mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o

> > +

> > +ccflags-y += -D__CHECK_ENDIAN__

> > diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.c

> > b/drivers/net/wireless/marvell/mwlwifi/debugfs.c

> > new file mode 100644

> > index 0000000..997461a

> > --- /dev/null

> > +++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.c

> > @@ -0,0 +1,433 @@

> > +/*

> > + * Copyright (C) 2006-2015, Marvell International Ltd.

> > + *

> > + * This software file (the "File") is distributed by Marvell

> > +International

> > + * Ltd. under the terms of the GNU General Public License Version 2,

> > +June 1991

> > + * (the "License").  You may use, redistribute and/or modify this

> > +File in

> > + * accordance with the terms and conditions of the License, a copy of

> > +which

> > + * is available by writing to the Free Software Foundation, Inc.

> > + *

> > + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND

> > +THE

> > + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A

> PARTICULAR

> > +PURPOSE

> > + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details

> > +about

> > + * this warranty disclaimer.

> > + */

> > +

> > +/* Description:  This file implements debug fs related functions. */

> > +

> > +#include <linux/debugfs.h>

> > +

> > +#include "sysadpt.h"

> > +#include "dev.h"

> > +#include "hostcmd.h"

> > +#include "fwcmd.h"

> > +#include "debugfs.h"

> > +

> > +#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \

> > +	if (!debugfs_create_file(#name, 0644, priv->debugfs_phy, \

> > +				 priv, &mwl_debugfs_##name##_fops)) \

> > +		return; \

> > +} while (0)

> 

> This macros relies on implicit arguments which is just bad and doesn't really

> do anything except for obfuscating the code.

> 


Thanks for feedback. These codes are leveraged from existing mwifiex driver for consistency. Please kindly accept it as is now, and I will initiate a task to discuss with mwifiex maintainer about this comment. Then we will come back with suggestion/change.

> > +#define MWLWIFI_DEBUGFS_FILE_OPS(name) \ static const struct

> > +file_operations mwl_debugfs_##name##_fops = { \

> > +	.read = mwl_debugfs_##name##_read, \

> > +	.write = mwl_debugfs_##name##_write, \

> > +	.open = simple_open, \

> > +}

> > +

> > +#define MWLWIFI_DEBUGFS_FILE_READ_OPS(name) \ static const struct

> > +file_operations mwl_debugfs_##name##_fops = { \

> > +	.read = mwl_debugfs_##name##_read, \

> > +	.open = simple_open, \

> > +}

> > +

> > +#define MWLWIFI_DEBUGFS_FILE_WRITE_OPS(name) \ static const struct

> > +file_operations mwl_debugfs_##name##_fops = { \

> > +	.write = mwl_debugfs_##name##_write, \

> > +	.open = simple_open, \

> > +}

> 

> Again here - you use thiese wrappers once, just declare the structs explicitly

> rather than this macro wrapping dance.

> 


Thanks for feedback. These codes are leveraged from existing mwifiex driver for consistency. Please kindly accept it as is now, and I will initiate a task to discuss with mwifiex maintainer about this comment. Then we will come back with suggestion/change.

> > +static int print_mac_addr(char *p, u8 *mac_addr) {

> > +	int i;

> > +	char *str = p;

> > +

> > +	str += sprintf(str, "mac address: %02x", mac_addr[0]);

> > +	for (i = 1; i < ETH_ALEN; i++)

> > +		str += sprintf(str, ":%02x", mac_addr[i]);

> > +	str += sprintf(str, "\n");

> > +

> > +	return (str - p);

> > +}

> > +

> > +static int dump_data(char *p, u8 *data, int len, char *title) {

> > +	char *str = p;

> > +	int cur_byte = 0;

> > +	int i;

> > +

> > +	str += sprintf(str, "%s\n", title);

> > +	for (cur_byte = 0; cur_byte < len; cur_byte += 8) {

> > +		if ((cur_byte + 8) < len) {

> > +			for (i = 0; i < 8; i++)

> > +				str += sprintf(str, "0x%02x ",

> > +					       *(data + cur_byte + i));

> > +			str += sprintf(str, "\n");

> > +		} else {

> > +			for (i = 0; i < (len - cur_byte); i++)

> > +				str += sprintf(str, "0x%02x ",

> > +					       *(data + cur_byte + i));

> > +			str += sprintf(str, "\n");

> > +			break;

> > +		}

> > +	}

> > +

> > +	return (str - p);

> > +}

> > +

> > +static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf,

> > +				     size_t count, loff_t *ppos)

> > +{

> > +	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;

> > +	unsigned long page = get_zeroed_page(GFP_KERNEL);

> > +	char *p = (char *)page;

> > +	ssize_t ret;

> > +

> > +	if (!p)

> > +		return -ENOMEM;

> > +

> > +	p += sprintf(p, "\n");

> > +	p += sprintf(p, "driver name: %s\n", MWL_DRV_NAME);

> > +	p += sprintf(p, "chip type: %s\n",

> > +		     (priv->chip_type == MWL8864) ? "88W8864" : "88W8897");

> > +	p += sprintf(p, "hw version: %X\n", priv->hw_data.hw_version);

> > +	p += sprintf(p, "driver version: %s\n", MWL_DRV_VERSION);

> > +	p += sprintf(p, "firmware version: 0x%08x\n",

> > +		     priv->hw_data.fw_release_num);

> > +	p += print_mac_addr(p, priv->hw_data.mac_addr);

> > +	p += sprintf(p, "2g: %s\n", priv->disable_2g ? "disable" : "enable");

> > +	p += sprintf(p, "5g: %s\n", priv->disable_5g ? "disable" : "enable");

> > +	p += sprintf(p, "antenna: %d %d\n",

> > +		     (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? 4 : 2,

> > +		     (priv->antenna_rx == ANTENNA_TX_4_AUTO) ? 4 : 2);

> > +	p += sprintf(p, "irq number: %d\n", priv->irq);

> > +	p += sprintf(p, "iobase0: %p\n", priv->iobase0);

> > +	p += sprintf(p, "iobase1: %p\n", priv->iobase1);

> > +	p += sprintf(p, "tx limit: %d\n", priv->txq_limit);

> > +	p += sprintf(p, "rx limit: %d\n", priv->recv_limit);

> > +	p += sprintf(p, "ap macid support: %08x\n",

> > +		     priv->ap_macids_supported);

> > +	p += sprintf(p, "sta macid support: %08x\n",

> > +		     priv->sta_macids_supported);

> > +	p += sprintf(p, "macid used: %08x\n", priv->macids_used);

> > +	p += sprintf(p, "mfg mode: %s\n", priv->mfg_mode ? "true" : "false");

> > +	p += sprintf(p, "\n");

> > +

> > +	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,

> > +				      (unsigned long)p - page);

> > +	free_page(page);

> > +

> > +	return ret;

> > +}

> > +

> > +static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf,

> > +				    size_t count, loff_t *ppos)

> > +{

> > +	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;

> > +	unsigned long page = get_zeroed_page(GFP_KERNEL);

> > +	char *p = (char *)page;

> > +	struct mwl_vif *mwl_vif;

> > +	struct ieee80211_vif *vif;

> > +	char ssid[IEEE80211_MAX_SSID_LEN + 1];

> > +	ssize_t ret;

> > +

> > +	if (!p)

> > +		return -ENOMEM;

> > +

> > +	p += sprintf(p, "\n");

> > +	spin_lock_bh(&priv->vif_lock);

> > +	list_for_each_entry(mwl_vif, &priv->vif_list, list) {

> > +		vif = container_of((char *)mwl_vif, struct ieee80211_vif,

> > +				   drv_priv[0]);

> > +		p += sprintf(p, "macid: %d\n", mwl_vif->macid);

> > +		switch (vif->type) {

> > +		case NL80211_IFTYPE_AP:

> > +			p += sprintf(p, "type: ap\n");

> > +			memcpy(ssid, vif->bss_conf.ssid,

> > +			       vif->bss_conf.ssid_len);

> > +			ssid[vif->bss_conf.ssid_len] = 0;

> > +			p += sprintf(p, "ssid: %s\n", ssid);

> > +			p += print_mac_addr(p, mwl_vif->bssid);

> > +			break;

> > +		case NL80211_IFTYPE_MESH_POINT:

> > +			p += sprintf(p, "type: mesh\n");

> > +			p += print_mac_addr(p, mwl_vif->bssid);

> > +			break;

> > +		case NL80211_IFTYPE_STATION:

> > +			p += sprintf(p, "type: sta\n");

> > +			p += print_mac_addr(p, mwl_vif->sta_mac);

> > +			break;

> > +		default:

> > +			p += sprintf(p, "type: unknown\n");

> > +			break;

> > +		}

> > +		p += sprintf(p, "hw_crypto_enabled: %s\n",

> > +			     mwl_vif->is_hw_crypto_enabled ? "true" : "false");

> > +		p += sprintf(p, "key idx: %d\n", mwl_vif->keyidx);

> > +		p += sprintf(p, "IV: %08x%04x\n", mwl_vif->iv32, mwl_vif->iv16);

> > +		p += dump_data(p, mwl_vif->beacon_info.ie_wmm_ptr,

> > +			       mwl_vif->beacon_info.ie_wmm_len, "WMM:");

> > +		p += dump_data(p, mwl_vif->beacon_info.ie_rsn_ptr,

> > +			       mwl_vif->beacon_info.ie_rsn_len, "RSN:");

> > +		p += dump_data(p, mwl_vif->beacon_info.ie_rsn48_ptr,

> > +			       mwl_vif->beacon_info.ie_rsn48_len, "RSN48:");

> > +		p += dump_data(p, mwl_vif->beacon_info.ie_ht_ptr,

> > +			       mwl_vif->beacon_info.ie_ht_len, "HT:");

> > +		p += dump_data(p, mwl_vif->beacon_info.ie_vht_ptr,

> > +			       mwl_vif->beacon_info.ie_vht_len, "VHT:");

> > +		p += sprintf(p, "\n");

> > +	}

> > +	spin_unlock_bh(&priv->vif_lock);

> 

> I hope there is no way that the number of vifs can get to the point where you

> overflow the page allocated since there is no cap on the

> sprintf() usage in dump_data().

> 


I will modify it.

> > +

> > +MWLWIFI_DEBUGFS_FILE_READ_OPS(info);

> > +MWLWIFI_DEBUGFS_FILE_READ_OPS(vif);

> > +MWLWIFI_DEBUGFS_FILE_READ_OPS(sta);

> > +MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu);

> > +MWLWIFI_DEBUGFS_FILE_OPS(regrdwr);

> > +

> > +void mwl_debugfs_init(struct ieee80211_hw *hw) {

> > +	struct mwl_priv *priv = hw->priv;

> > +

> > +	if (!priv->debugfs_phy)

> > +		priv->debugfs_phy = debugfs_create_dir("mwlwifi",

> > +						       hw->wiphy->debugfsdir);

> > +

> > +	if (!priv->debugfs_phy)

> > +		return;

> > +

> > +	MWLWIFI_DEBUGFS_ADD_FILE(info);

> > +	MWLWIFI_DEBUGFS_ADD_FILE(vif);

> > +	MWLWIFI_DEBUGFS_ADD_FILE(sta);

> > +	MWLWIFI_DEBUGFS_ADD_FILE(ampdu);

> > +	MWLWIFI_DEBUGFS_ADD_FILE(regrdwr);

> > +}

> > +

> > +void mwl_debugfs_remove(struct ieee80211_hw *hw) {

> > +	struct mwl_priv *priv = hw->priv;

> > +

> > +	debugfs_remove(priv->debugfs_phy);

> > +	priv->debugfs_phy = NULL;

> > +}

> > diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h

> > b/drivers/net/wireless/marvell/mwlwifi/debugfs.h

> > new file mode 100644

> > index 0000000..dfc2a3c

> > --- /dev/null

> > +++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h

> > @@ -0,0 +1,24 @@

> > +/*

> > + * Copyright (C) 2006-2015, Marvell International Ltd.

> > + *

> > + * This software file (the "File") is distributed by Marvell

> > +International

> > + * Ltd. under the terms of the GNU General Public License Version 2,

> > +June 1991

> > + * (the "License").  You may use, redistribute and/or modify this

> > +File in

> > + * accordance with the terms and conditions of the License, a copy of

> > +which

> > + * is available by writing to the Free Software Foundation, Inc.

> > + *

> > + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND

> > +THE

> > + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A

> PARTICULAR

> > +PURPOSE

> > + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details

> > +about

> > + * this warranty disclaimer.

> > + */

> > +

> > +/* Description:  This file defines debug fs related functions. */

> > +

> > +#ifndef _debugfs_h_

> > +#define _debugfs_h_

> 

> The general convention is to user upper-case for the these - not a biggie

> though.

> 


I will modify it.

> > +struct mwl_priv {

> > +	struct ieee80211_hw *hw;

> > +	struct firmware *fw_ucode;

> > +	int chip_type;

> > +

> > +	struct device_node *dt_node;

> > +	struct device_node *pwr_node;

> > +	bool disable_2g;

> > +	bool disable_5g;

> > +	int antenna_tx;

> > +	int antenna_rx;

> > +

> > +	struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];

> > +	bool cdd;

> > +	u16 txantenna2;

> > +	u8 powinited;

> > +	u16 max_tx_pow[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* max tx power

> (dBm) */

> > +	u16 target_powers[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* target

> powers   */

> > +	u8 cal_tbl[200];

> > +

> > +	struct pci_dev *pdev;

> > +	void __iomem *iobase0; /* MEM Base Address Register 0  */

> > +	void __iomem *iobase1; /* MEM Base Address Register 1  */

> > +	u32 next_bar_num;

> > +

> > +	spinlock_t fwcmd_lock;       /* for firmware command         */

> > +	unsigned short *pcmd_buf;    /* pointer to CmdBuf (virtual)  */

> > +	dma_addr_t pphys_cmd_buf;    /* pointer to CmdBuf (physical) */

> > +	bool in_send_cmd;

> > +

> > +	int irq;

> > +	struct mwl_hw_data hw_data;  /* Adapter HW specific info     */

> > +

> > +	/* various descriptor data */

> > +	spinlock_t tx_desc_lock;     /* for tx descriptor data       */

> > +	spinlock_t rx_desc_lock;     /* for rx descriptor data       */

> 

> You probably don't want to declare these two right next to each other, if you

> expect the RX and TX paths of the code to be able to run in parallel.

> 

> Spin locks are not guaranteed to be cache line aligned or cache line sized, so

> you can end up with cache line ping pongs between CPUs in this case.

> 


I will check and modify it.

> > +	struct mwl_desc_data desc_data[SYSADPT_NUM_OF_DESC_DATA];

> > +	struct sk_buff_head txq[SYSADPT_NUM_OF_DESC_DATA];

> > +	struct sk_buff_head delay_q;

> > +	/* number of descriptors owned by fw at any one time */

> > +	int fw_desc_cnt[SYSADPT_NUM_OF_DESC_DATA];

> > +

> > +	struct tasklet_struct tx_task;

> > +	struct tasklet_struct rx_task;

> > +	struct tasklet_struct qe_task;

> > +	int txq_limit;

> > +	bool is_tx_schedule;

> > +	int recv_limit;

> > +	bool is_rx_schedule;

> > +	bool is_qe_schedule;

> > +	s8 noise;                    /* Most recently reported noise in dBm

> */

> > +	struct ieee80211_supported_band band_24;

> > +	struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM];

> > +	struct ieee80211_rate rates_24[BAND_24_RATE_NUM];

> > +	struct ieee80211_supported_band band_50;

> > +	struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM];

> > +	struct ieee80211_rate rates_50[BAND_50_RATE_NUM];

> > +

> > +	u32 ap_macids_supported;

> > +	u32 sta_macids_supported;

> > +	u32 macids_used;

> > +	spinlock_t vif_lock;         /* for private interface info   */

> > +	struct list_head vif_list;   /* List of interfaces.          */

> > +	u32 running_bsses;           /* bitmap of running BSSes      */

> > +

> > +	spinlock_t sta_lock;         /* for private sta info         */

> > +	struct list_head sta_list;   /* List of stations             */

> 

> These two are awfully close together too. You might just get away with it on

> x86_64, given list_head is 16 bytes, but the x86_64 packing rules are odd, so I

> am never quite sure. On x86_32 it will certainly bite you

> - provided vif_lock and sta_lock can get taken from two different paths.

> 


I will check and modify it.

> > +

> > +	bool radio_on;

> > +	bool radio_short_preamble;

> > +	bool wmm_enabled;

> > +	struct ieee80211_tx_queue_params

> wmm_params[SYSADPT_TX_WMM_QUEUES];

> > +

> > +	/* Ampdu stream information */

> > +	u8 num_ampdu_queues;

> > +	spinlock_t stream_lock;      /* for ampdu stream             */

> > +	struct mwl_ampdu_stream ampdu[SYSADPT_TX_AMPDU_QUEUES];

> > +	struct work_struct watchdog_ba_handle;

> > +

> > +	bool mfg_mode;

> > +

> > +#ifdef CONFIG_DEBUG_FS

> > +	struct dentry *debugfs_phy;

> > +	u32 reg_type;

> > +	u32 reg_offset;

> > +	u32 reg_value;

> > +#endif

> > +};

> [snip]

> 

> > +static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,

> > +				   struct mwl_vif *vif, u8 *beacon, int len) {

> > +	struct ieee80211_mgmt *mgmt;

> > +	struct beacon_info *beacon_info;

> > +	int baselen;

> > +	u8 *pos;

> > +	size_t left;

> > +	bool elem_parse_failed;

> > +

> > +	mgmt = (struct ieee80211_mgmt *)beacon;

> > +

> > +	baselen = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;

> > +	if (baselen > len)

> > +		return;

> > +

> > +	beacon_info = &vif->beacon_info;

> > +	memset(beacon_info, 0, sizeof(struct beacon_info));

> > +	beacon_info->valid = false;

> > +	beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];

> > +	beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];

> > +

> > +	beacon_info->cap_info = le16_to_cpu(mgmt->u.beacon.capab_info);

> > +

> > +	pos = (u8 *)mgmt->u.beacon.variable;

> > +	left = len - baselen;

> > +

> > +	elem_parse_failed = false;

> > +

> > +	while (left >= 2) {

> > +		u8 id, elen;

> > +

> > +		id = *pos++;

> > +		elen = *pos++;

> > +		left -= 2;

> > +

> > +		if (elen > left) {

> > +			elem_parse_failed = true;

> > +			break;

> > +		}

> > +

> > +		switch (id) {

> > +		case WLAN_EID_SUPP_RATES:

> > +		case WLAN_EID_EXT_SUPP_RATES:

> > +			{

> > +			int idx, bi, oi;

> > +			u8 rate;

> > +

> > +			for (bi = 0; bi < SYSADPT_MAX_DATA_RATES_G;

> > +			     bi++) {

> > +				if (beacon_info->b_rate_set[bi] == 0)

> > +					break;

> > +			}

> > +

> > +			for (oi = 0; oi < SYSADPT_MAX_DATA_RATES_G;

> > +			     oi++) {

> > +				if (beacon_info->op_rate_set[oi] == 0)

> > +					break;

> > +			}

> > +

> > +			for (idx = 0; idx < elen; idx++) {

> > +				rate = pos[idx];

> > +				if ((rate & 0x80) != 0) {

> > +					if (bi < SYSADPT_MAX_DATA_RATES_G)

> > +						beacon_info->b_rate_set[bi++]

> > +							= rate & 0x7f;

> > +					else {

> > +						elem_parse_failed = true;

> > +						break;

> > +					}

> > +				}

> > +				if (oi < SYSADPT_MAX_DATA_RATES_G)

> > +					beacon_info->op_rate_set[oi++] =

> > +						rate & 0x7f;

> > +				else {

> > +					elem_parse_failed = true;

> > +					break;

> > +				}

> > +			}

> > +			}

> > +			break;

> > +		case WLAN_EID_RSN:

> > +			beacon_info->ie_rsn48_len = (elen + 2);

> > +			beacon_info->ie_rsn48_ptr = (pos - 2);

> > +			break;

> > +		case WLAN_EID_HT_CAPABILITY:

> > +		case WLAN_EID_HT_OPERATION:

> > +		case WLAN_EID_OVERLAP_BSS_SCAN_PARAM:

> > +		case WLAN_EID_EXT_CAPABILITY:

> > +			beacon_info->ie_ht_len += (elen + 2);

> > +			if (beacon_info->ie_ht_len >

> > +			    sizeof(beacon_info->ie_list_ht)) {

> > +				elem_parse_failed = true;

> > +			} else {

> > +				*beacon_info->ie_ht_ptr++ = id;

> > +				*beacon_info->ie_ht_ptr++ = elen;

> > +				memcpy(beacon_info->ie_ht_ptr, pos, elen);

> > +				beacon_info->ie_ht_ptr += elen;

> > +			}

> > +			break;

> > +#ifdef CONFIG_MAC80211_MESH

> > +		case WLAN_EID_MESH_CONFIG:

> > +			beacon_info->ie_meshcfg_len = (elen + 2);

> > +			beacon_info->ie_meshcfg_ptr = (pos - 2);

> > +			break;

> > +		case WLAN_EID_MESH_ID:

> > +			beacon_info->ie_meshid_len = (elen + 2);

> > +			beacon_info->ie_meshid_ptr = (pos - 2);

> > +			break;

> > +		case WLAN_EID_CHAN_SWITCH_PARAM:

> > +			beacon_info->ie_meshchsw_len = (elen + 2);

> > +			beacon_info->ie_meshchsw_ptr = (pos - 2);

> > +			break;

> > +#endif

> > +		case WLAN_EID_VHT_CAPABILITY:

> > +		case WLAN_EID_VHT_OPERATION:

> > +		case WLAN_EID_OPMODE_NOTIF:

> > +			beacon_info->ie_vht_len += (elen + 2);

> > +			if (beacon_info->ie_vht_len >

> > +			    sizeof(beacon_info->ie_list_vht)) {

> > +				elem_parse_failed = true;

> > +			} else {

> > +				*beacon_info->ie_vht_ptr++ = id;

> > +				*beacon_info->ie_vht_ptr++ = elen;

> > +				memcpy(beacon_info->ie_vht_ptr, pos, elen);

> > +				beacon_info->ie_vht_ptr += elen;

> > +			}

> > +			break;

> > +		case WLAN_EID_VENDOR_SPECIFIC:

> > +			if ((pos[0] == 0x00) && (pos[1] == 0x50) &&

> > +			    (pos[2] == 0xf2)) {

> > +				if (pos[3] == 0x01) {

> > +					beacon_info->ie_rsn_len = (elen + 2);

> > +					beacon_info->ie_rsn_ptr = (pos - 2);

> > +				}

> > +

> > +				if (pos[3] == 0x02) {

> > +					beacon_info->ie_wmm_len = (elen + 2);

> > +					beacon_info->ie_wmm_ptr = (pos - 2);

> > +				}

> > +			}

> > +			break;

> > +		default:

> > +			break;

> > +		}

> > +

> > +		left -= elen;

> > +		pos += elen;

> > +	}

> > +

> > +	if (!elem_parse_failed) {

> > +		beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];

> > +		beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];

> > +		beacon_info->valid = true;

> > +	}

> > +}

> 

> Any reason you're not using cfg80211_find_ie() here? Seems to be

> re-inventing the wheel to me.

> 


It is better to use this function to parse beacon one time and get all IEs we need. It is not a good idea to use cfg80211_find_ie() to find IEs we need one by one.

> > +void mwl_fwcmd_reset(struct ieee80211_hw *hw) {

> > +	struct mwl_priv *priv = hw->priv;

> > +

> > +	if (mwl_fwcmd_chk_adapter(priv))

> > +		writel(ISR_RESET,

> > +		       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); }

> > +

> > +void mwl_fwcmd_int_enable(struct ieee80211_hw *hw) {

> > +	struct mwl_priv *priv = hw->priv;

> > +

> > +	if (mwl_fwcmd_chk_adapter(priv)) {

> > +		writel(0x00,

> > +		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);

> > +		writel((MACREG_A2HRIC_BIT_MASK),

> > +		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);

> 

> Please avoid superfluous use of parentheses.

> 


I will modify it.

> > +int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw) {

> > +	struct mwl_priv *priv = hw->priv;

> > +	struct hostcmd_cmd_get_hw_spec *pcmd;

> > +	int retry;

> > +	int i;

> > +

> > +	if (priv->mfg_mode)

> > +		return 0;

> > +

> > +	pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0];

> > +

> > +	spin_lock_bh(&priv->fwcmd_lock);

> > +

> > +	wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd);

> > +	memset(pcmd, 0x00, sizeof(*pcmd));

> > +	eth_broadcast_addr(pcmd->permanent_addr);

> > +	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_HW_SPEC);

> > +	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));

> > +	pcmd->fw_awake_cookie = cpu_to_le32(priv->pphys_cmd_buf + 2048);

> > +

> > +	retry = 0;

> > +	while (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_HW_SPEC)) {

> > +		if (retry++ > MAX_WAIT_GET_HW_SPECS_ITERATONS) {

> > +			wiphy_err(hw->wiphy, "can't get hw specs\n");

> > +			spin_unlock_bh(&priv->fwcmd_lock);

> > +			return -EIO;

> > +		}

> > +

> > +		mdelay(1000);

> > +		wiphy_debug(hw->wiphy,

> > +			    "repeat command = %p\n", pcmd);

> > +	}

> 

> *cough* mdelay(1000) while holding a spin lock and within in a while() *loop?

> *cough*

> 

> This needs a little cleaning up. Please have a look at

> Documentation/timers/timers-howto.txt

> 


I will try to use mutex and msleep().

> > +

> > +	ether_addr_copy(&priv->hw_data.mac_addr[0],

> pcmd->permanent_addr);

> > +	priv->desc_data[0].wcb_base =

> > +		le32_to_cpu(pcmd->wcb_base0) & 0x0000ffff;

> > +	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)

> > +		priv->desc_data[i].wcb_base =

> > +			le32_to_cpu(pcmd->wcb_base[i - 1]) & 0x0000ffff;

> > +	priv->desc_data[0].rx_desc_read =

> > +		le32_to_cpu(pcmd->rxpd_rd_ptr) & 0x0000ffff;

> > +	priv->desc_data[0].rx_desc_write =

> > +		le32_to_cpu(pcmd->rxpd_wr_ptr) & 0x0000ffff;

> > +	priv->hw_data.region_code = le16_to_cpu(pcmd->region_code) & 0x00ff;

> > +	priv->hw_data.fw_release_num = le32_to_cpu(pcmd->fw_release_num);

> > +	priv->hw_data.max_num_tx_desc = le16_to_cpu(pcmd->num_wcb);

> > +	priv->hw_data.max_num_mc_addr =

> le16_to_cpu(pcmd->num_mcast_addr);

> > +	priv->hw_data.num_antennas = le16_to_cpu(pcmd->num_antenna);

> > +	priv->hw_data.hw_version = pcmd->version;

> > +	priv->hw_data.host_interface = pcmd->host_if;

> > +

> > +	spin_unlock_bh(&priv->fwcmd_lock);

> > +

> > +	return 0;

> > +}

> > +

> > +int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw) {

> > +	struct mwl_priv *priv = hw->priv;

> > +	struct hostcmd_cmd_set_hw_spec *pcmd;

> > +	int i;

> > +

> > +	if (priv->mfg_mode)

> > +		return 0;

> > +

> > +	pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0];

> > +

> > +	spin_lock_bh(&priv->fwcmd_lock);

> > +

> > +	memset(pcmd, 0x00, sizeof(*pcmd));

> > +	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_HW_SPEC);

> > +	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));

> > +	pcmd->wcb_base[0] = cpu_to_le32(priv->desc_data[0].pphys_tx_ring);

> > +	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)

> > +		pcmd->wcb_base[i] =

> > +			cpu_to_le32(priv->desc_data[i].pphys_tx_ring);

> > +	pcmd->tx_wcb_num_per_queue =

> cpu_to_le32(SYSADPT_MAX_NUM_TX_DESC);

> > +	pcmd->num_tx_queues = cpu_to_le32(SYSADPT_NUM_OF_DESC_DATA);

> > +	pcmd->total_rx_wcb = cpu_to_le32(SYSADPT_MAX_NUM_RX_DESC);

> > +	pcmd->rxpd_wr_ptr = cpu_to_le32(priv->desc_data[0].pphys_rx_ring);

> > +	pcmd->features = 0;

> > +

> > +	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_HW_SPEC)) {

> > +		spin_unlock_bh(&priv->fwcmd_lock);

> > +		wiphy_err(hw->wiphy, "failed execution\n");

> > +		return -EIO;

> > +	}

> > +

> > +	spin_unlock_bh(&priv->fwcmd_lock);

> > +

> > +	return 0;

> > +}

> 

> It looks like fwcmd_lock() is used in this way in every instance. Could you not

> allocate a pool of command buffers, put them on a list, and only take the lock

> while you pull them out and reinsert them back into the list?

> 

> If you need to hold a lock during firmware command execution, you probably

> should switch to a mutex mechanism rather than spin locks.

> 


I will try to use mutex.

> > +

> > +int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,

> > +		       struct ieee80211_low_level_stats *stats) {

> > +	struct mwl_priv *priv = hw->priv;

> > +	struct hostcmd_cmd_802_11_get_stat *pcmd;

> > +

> > +	pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0];

> > +

> > +	spin_lock_bh(&priv->fwcmd_lock);

> > +

> > +	memset(pcmd, 0x00, sizeof(*pcmd));

> > +	pcmd->cmd_hdr.cmd =

> cpu_to_le16(HOSTCMD_CMD_802_11_GET_STAT);

> > +	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));

> > +

> > +	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_GET_STAT)) {

> > +		spin_unlock_bh(&priv->fwcmd_lock);

> > +		wiphy_err(hw->wiphy, "failed execution\n");

> > +		return -EIO;

> > +	}

> > +

> > +	stats->dot11ACKFailureCount =

> > +		le32_to_cpu(pcmd->ack_failures);

> > +	stats->dot11RTSFailureCount =

> > +		le32_to_cpu(pcmd->rts_failures);

> > +	stats->dot11FCSErrorCount =

> > +		le32_to_cpu(pcmd->rx_fcs_errors);

> > +	stats->dot11RTSSuccessCount =

> > +		le32_to_cpu(pcmd->rts_successes);

> > +

> > +	spin_unlock_bh(&priv->fwcmd_lock);

> > +

> > +	return 0;

> > +}

> > +

> > +int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw) {

> > +	return mwl_fwcmd_802_11_radio_control(hw->priv, true, false); }

> > +

> > +int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw) {

> > +	return mwl_fwcmd_802_11_radio_control(hw->priv, false, false); }

> 

> These last two makes me think you only use the spin lock for buffer

> management and not the command execution itself?

> 

> I am out of time for today, I'll try to see if I can find more time later - please

> look into fixing the above.

> 

> Jes
Johannes Berg Nov. 26, 2015, 9:40 a.m. UTC | #5
On Thu, 2015-11-26 at 08:27 +0000, David Lin wrote:

> > Since you do this in .probe, you should consider loading the
> > firmware asynchronously.
> > 
> 
> I hope I can postpone this modification later.

I don't feel strongly about this one - otoh it's not really
complicated.

> > > +#ifdef CONFIG_SUPPORT_MFG
> > 
> > This Kconfig variable doesn't exist.
> > 
> 
> The compile variable is used privately by Marvell and our customers
> in production line.

Yeah, still. Make it a proper Kconfig variable, defaulting to off and
hidden under something, or remove it. It's extremely misleading to have
something called CONFIG_* when it's not a Kconfig variable.

> Mac80211 does not support mesh AMSDU now, we implement this function
> in mwlwifi driver for the time being. Except for mesh AMSDU, we still
> leverage mac80211 to handle data AMSDU.

You're allowed to modify mac80211.

> > What's going on here? Why are you modifying the action frames on
> > the fly??!
> > 
> 
> Due to mwlwifi supports Tx/Rx AMSDU, these frames are modified to
> inform client that we support AMSDU.
> 

Ditto - you're allowed to modify mac80211. We actually have a patch
like this already:
https://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211-
next.git/commit/?id=99e7ca44bb910f0cbfda5d9008e8517df0ebc939

johannes
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Lin Nov. 27, 2015, 2 a.m. UTC | #6
> On November 26, 2015 5:40 PM, Johannes Berg wrote:

> 

> On Thu, 2015-11-26 at 08:27 +0000, David Lin wrote:

> 

> > > Since you do this in .probe, you should consider loading the

> > > firmware asynchronously.

> > >

> >

> > I hope I can postpone this modification later.

> 

> I don't feel strongly about this one - otoh it's not really complicated.

> 


Thanks.

> > > > +#ifdef CONFIG_SUPPORT_MFG

> > >

> > > This Kconfig variable doesn't exist.

> > >

> >

> > The compile variable is used privately by Marvell and our customers in

> > production line.

> 

> Yeah, still. Make it a proper Kconfig variable, defaulting to off and hidden

> under something, or remove it. It's extremely misleading to have something

> called CONFIG_* when it's not a Kconfig variable.

> 


I will change this compile variable from "CONFIG_SUPPORT_MFG" to "SUPPORT_MFG".

> > Mac80211 does not support mesh AMSDU now, we implement this function

> > in mwlwifi driver for the time being. Except for mesh AMSDU, we still

> > leverage mac80211 to handle data AMSDU.

> 

> You're allowed to modify mac80211.

> 

> > > What's going on here? Why are you modifying the action frames on the

> > > fly??!

> > >

> >

> > Due to mwlwifi supports Tx/Rx AMSDU, these frames are modified to

> > inform client that we support AMSDU.

> >

> 

> Ditto - you're allowed to modify mac80211. We actually have a patch like this

> already:

> https://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211-

> next.git/commit/?id=99e7ca44bb910f0cbfda5d9008e8517df0ebc939

> 


Once if this patch is ready, I will modify mwlwifi to use it. Thanks.

> johannes

> --

> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the

> body of a message to majordomo@vger.kernel.org More majordomo info at

> http://vger.kernel.org/majordomo-info.html
Johannes Berg Nov. 27, 2015, 8:27 a.m. UTC | #7
On Fri, 2015-11-27 at 02:00 +0000, David Lin wrote:
> > Ditto - you're allowed to modify mac80211. We actually have a patch
> > like this
> > already:
> > https://git.kernel.org/cgit/linux/kernel/git/jberg/mac80211-
> > next.git/commit/?id=99e7ca44bb910f0cbfda5d9008e8517df0ebc939
> > 
> 
> Once if this patch is ready, I will modify mwlwifi to use it. Thanks.

The patch is ready now and in the tree, but that wasn't my point. My
point is that you shouldn't try to hack around mac80211 like with the
action frame interception and the mesh-amsdu-reimplementation, but
actually go fix it.

If you have issues with mac80211 like this you can ask how to fix them,
or just fix them and suggest changes/patches, but just working around
it in the driver isn't the right approach.

johannes
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kalle Valo Dec. 10, 2015, 2:03 p.m. UTC | #8
David Lin <dlin@marvell.com> writes:

> This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
> chipsets.
> This driver was developed as part of the openwrt.org project to support
> Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwifi.
>
> The mwlwifi driver differs from existing mwifiex driver:
> o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem
> to provide full AP/Wireless Bridge functionality (routers).
> o mwifiex is a "fullmac driver" which provides a comprehensive set of
> client functions (laptops/embedded devices)
> o only mwlwifi supports Marvell AP chip 886X series
>
> NOTE: Users with Marvell 88W8897 chipsets currently should enable
> (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT BOTH.
>
> mwlwifi driver leveraged code from existing MWL8K driver in the
> following areas:
> - 802.11n setting for mac80211
> - Functions needed to hook up to mac80211
> - Interactions with mac80211 to establish BA streams
> - Partial firmware APIs, some data fields
> - Method to pass Rx packets to mac80211 except 11ac rates
>
> In addition, mwlwifi driver supports:
> - future scalability and future development (refactored source code)
> - Marvell 802.11ac chipsets, including combo BT devices
> - 802.11ac related settings and functions
> - concurrent AP+STA functionalities with single firmware per chip
> - firmware APIs for the supported chipset
> - communicating new mac80211 settings to firmware
> - Different TX/RX datapath where applicable
> - A-MSDU and A-MPDU
> - mac80211-based MESH (work in progress)
> - Refined the code to establish BA streams
>
> NOTE: MWLWIFI will be organized under new vendor specific folder/marvell,
> as per request of the gate keeper and community.
>
> Signed-off-by: David Lin <dlin@marvell.com>

I don't see any changelog, please always provide one. Makes review a lot
easier.

Also I hope you are not adding new features anymore. It's better to
freeze the driver and focus on getting the driver accepted. Once that is
done you can submit new features as separate patches. Otherwise we go
back to square one after every review.
Kalle Valo Dec. 10, 2015, 2:06 p.m. UTC | #9
David Lin <dlin@marvell.com> writes:

>> > +#define MWL_DRV_NAME     KBUILD_MODNAME
>> > +#define MWL_DRV_VERSION	 "10.3.0.14"
>> 
>> versions like that are generally fairly useless since you don't update them for
>> every commit ...
>> 
>
> We still need version information for formal release.

Then use git commit ids for tracking the driver version. Using manual
version strings for drivers don't work, it has been tried multiple times
and they are always out of sync.
Kalle Valo Dec. 10, 2015, 2:39 p.m. UTC | #10
David Lin <dlin@marvell.com> writes:

> This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
> chipsets.
> This driver was developed as part of the openwrt.org project to support
> Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwifi.
>
> The mwlwifi driver differs from existing mwifiex driver:
> o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem
> to provide full AP/Wireless Bridge functionality (routers).
> o mwifiex is a "fullmac driver" which provides a comprehensive set of
> client functions (laptops/embedded devices)
> o only mwlwifi supports Marvell AP chip 886X series
>
> NOTE: Users with Marvell 88W8897 chipsets currently should enable
> (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT BOTH.
>
> mwlwifi driver leveraged code from existing MWL8K driver in the
> following areas:
> - 802.11n setting for mac80211
> - Functions needed to hook up to mac80211
> - Interactions with mac80211 to establish BA streams
> - Partial firmware APIs, some data fields
> - Method to pass Rx packets to mac80211 except 11ac rates
>
> In addition, mwlwifi driver supports:
> - future scalability and future development (refactored source code)
> - Marvell 802.11ac chipsets, including combo BT devices
> - 802.11ac related settings and functions
> - concurrent AP+STA functionalities with single firmware per chip
> - firmware APIs for the supported chipset
> - communicating new mac80211 settings to firmware
> - Different TX/RX datapath where applicable
> - A-MSDU and A-MPDU
> - mac80211-based MESH (work in progress)
> - Refined the code to establish BA streams
>
> NOTE: MWLWIFI will be organized under new vendor specific folder/marvell,
> as per request of the gate keeper and community.
>
> Signed-off-by: David Lin <dlin@marvell.com>

This seems to use base64 encoding, how did you submit this? 'git
send-email' tool is strongly preferred.

Content-Transfer-Encoding: base64

I applied this to pending branch and saw few easy conflicts with Kconfig
files and Makefiles, I guess you submitted this patch before I added the
vendor directories. Let's see what kbuild finds but I already saw two
warnings:

drivers/net/wireless/marvell/mwlwifi/main.c:160:20: warning: incorrect type in argument 1 (different address spaces)
drivers/net/wireless/marvell/mwlwifi/main.c:160:20:    expected void const *ptr
drivers/net/wireless/marvell/mwlwifi/main.c:160:20:    got void [noderef] <asn:2>*[assigned] addr
drivers/net/wireless/marvell/mwlwifi/main.c:171:20: warning: incorrect type in argument 1 (different address spaces)
drivers/net/wireless/marvell/mwlwifi/main.c:171:20:    expected void const *ptr
drivers/net/wireless/marvell/mwlwifi/main.c:171:20:    got void [noderef] <asn:2>*[assigned] addr
Kalle Valo Dec. 10, 2015, 2:57 p.m. UTC | #11
David Lin <dlin@marvell.com> writes:

> On November 26, 2015 5:40 PM, Johannes Berg wrote:
>> On Thu, 2015-11-26 at 08:27 +0000, David Lin wrote:
>> 
>> > > > +#ifdef CONFIG_SUPPORT_MFG
>> > >
>> > > This Kconfig variable doesn't exist.
>> > >
>> >
>> > The compile variable is used privately by Marvell and our customers in
>> > production line.
>> 
>> Yeah, still. Make it a proper Kconfig variable, defaulting to off and hidden
>> under something, or remove it. It's extremely misleading to have something
>> called CONFIG_* when it's not a Kconfig variable.
>> 
>
> I will change this compile variable from "CONFIG_SUPPORT_MFG" to
> "SUPPORT_MFG".

Then it's still dead code which won't ever get compiled in upstream.
Please follow what Johannes suggested.
Grant Grundler Dec. 12, 2015, 12:47 a.m. UTC | #12
[resending as plain text....gmail...grrrh.]

On Fri, Dec 11, 2015 at 4:46 PM, Grant Grundler <grundler@google.com> wrote:
> Sorry for the late response...just one point below
>
> On Fri, Nov 20, 2015 at 3:22 AM, Johannes Berg <johannes@sipsolutions.net>
> wrote:
>>
>> > +#define MWL_DRV_NAME     KBUILD_MODNAME
>> > +#define MWL_DRV_VERSION       "10.3.0.14"
>>
>> versions like that are generally fairly useless since you don't update
>> them for every commit ...
>
>
> When backporting to other kernel trees, it's extremely handy to have a
> DRV_VERSION.
>
> Checking if a particular kernel build has the expected driver version
> trivial.  "mod_info" dumps the driver version for modules (which this will
> usually be too).
>
> And yes, it won't get updated for every commit only because other version
> control might encapsulate those changes. Not every distro makes their build
> versioning accessible to outsiders. So it's still helpful even if not
> perfect.
>
> Marvell can update this every time they resync with their own internal
> versions.
>
> cheers,
> grant
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kalle Valo Dec. 14, 2015, 11:47 a.m. UTC | #13
Grant Grundler <grundler@google.com> writes:

> Sorry for the late response...just one point below
>
> On Fri, Nov 20, 2015 at 3:22 AM, Johannes Berg <johannes@sipsolutions.net>
> wrote:
>
>     > +#define MWL_DRV_NAME     KBUILD_MODNAME
>     > +#define MWL_DRV_VERSION       "10.3.0.14"
>
>     versions like that are generally fairly useless since you don't update
>     them for every commit ...
>
>
> When backporting to other kernel trees, it's extremely handy to have a
> DRV_VERSION.
>
> Checking if a particular kernel build has the expected driver version trivial.
>  "mod_info" dumps the driver version for modules (which this will usually be
> too).
>
> And yes, it won't get updated for every commit only because other version
> control might encapsulate those changes. Not every distro makes their build
> versioning accessible to outsiders. So it's still helpful even if not perfect.
>
> Marvell can update this every time they resync with their own internal
> versions.

But the problem is that you cannot really trust this driver version
string. I found a perfect example from another Marvell driver:

5e6e3a92b9a4c drivers/net/wireless/mwifiex/main.c (Bing Zhao 2011-03-21 18:00:50 -0700   25) #define VERSION   "1.0"

So nobody has updated that version string since the initial commit from
2011 and that driver has had over 900 non-merge commits after that.
Because of bad cases like this I think having a driver specific versions
in in-tree drivers are just noise and unnecessary extra work. When
porting a driver to another tree git commit ids, release tag names or
git-describe output are much better ways to identify the driver version.
Kan Yan Dec. 14, 2015, 9:32 p.m. UTC | #14
David Lin <dlin@...> writes:

> +static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv,
> +					      int desc_num,
> ...
> +		amsdu_pkts = (struct sk_buff_head *)
> +			kmalloc(sizeof(*amsdu_pkts), GFP_KERNEL);
> +		if (!amsdu_pkts) {

Should GFP_ATOMIC be used here instead of GFP_KERNEL? This function could be
called in interrupt context.

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Lin Dec. 15, 2015, 5:47 a.m. UTC | #15
> Kan Yan writes:
> 
> David Lin <dlin@...> writes:
> 
> > +static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv,
> > +					      int desc_num,
> > ...
> > +		amsdu_pkts = (struct sk_buff_head *)
> > +			kmalloc(sizeof(*amsdu_pkts), GFP_KERNEL);
> > +		if (!amsdu_pkts) {
> 
> Should GFP_ATOMIC be used here instead of GFP_KERNEL? This function could
> be called in interrupt context.
> 

Thanks. I will fix it.

> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the
> body of a message to majordomo@vger.kernel.org More majordomo info at
> http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Emmanuel Grumbach Dec. 15, 2015, 7:40 a.m. UTC | #16
Hi,

I was interested in the checking the A-MSDU implementation only, so I
reviewed only that.

I am not sure I really followed your implementation though.
In iwlwifi I chose the LSO mechanism to implement A-MSDU. I understand that for
an AP it makes less sense since you don't really have locally generated traffic
and LSO won't work well with packets forwarded by the IP layer.
So you seem to have added a timeout mechanism inside the driver that accumulates
the packet for a certain period of time and send them all when the max number of
packets has been reached?
But I didn't see any timer there, so that if you start an A-MSDU and
then suddenly
stop receiving packets for transmission, the packet already in the
buffer will stay
stale?

A few more comments below.

On Thu, Nov 12, 2015 at 5:51 AM, David Lin <dlin@marvell.com> wrote:
> This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
> chipsets.
> This driver was developed as part of the openwrt.org project to support
> Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwifi.
>
> The mwlwifi driver differs from existing mwifiex driver:
> o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem
> to provide full AP/Wireless Bridge functionality (routers).
> o mwifiex is a "fullmac driver" which provides a comprehensive set of
> client functions (laptops/embedded devices)
> o only mwlwifi supports Marvell AP chip 886X series
>

[snip]

> +
> +static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw,
> +                                    struct ieee80211_vif *vif,
> +                                    enum ieee80211_ampdu_mlme_action action,
> +                                    struct ieee80211_sta *sta,
> +                                    u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
> +{
> +       int rc = 0;
> +       struct mwl_priv *priv = hw->priv;
> +       struct mwl_ampdu_stream *stream;
> +       u8 *addr = sta->addr, idx;
> +       struct mwl_sta *sta_info;
> +
> +       sta_info = mwl_dev_get_sta(sta);
> +
> +       spin_lock_bh(&priv->stream_lock);
> +
> +       stream = mwl_fwcmd_lookup_stream(hw, addr, tid);
> +
> +       switch (action) {
> +       case IEEE80211_AMPDU_RX_START:
> +       case IEEE80211_AMPDU_RX_STOP:
> +               break;
> +       case IEEE80211_AMPDU_TX_START:
> +               if (!sta_info->is_ampdu_allowed) {
> +                       wiphy_warn(hw->wiphy, "ampdu not allowed\n");
> +                       rc = -EPERM;
> +                       break;
> +               }
> +
> +               if (!stream) {
> +                       stream = mwl_fwcmd_add_stream(hw, sta, tid);
> +                       if (!stream) {
> +                               wiphy_warn(hw->wiphy, "no stream found\n");
> +                               rc = -EPERM;
> +                               break;
> +                       }
> +               }
> +
> +               spin_unlock_bh(&priv->stream_lock);
> +               rc = mwl_fwcmd_check_ba(hw, stream, vif);
> +               spin_lock_bh(&priv->stream_lock);
> +               if (rc) {
> +                       mwl_fwcmd_remove_stream(hw, stream);
> +                       wiphy_err(hw->wiphy,
> +                                 "ampdu start error code: %d\n", rc);
> +                       rc = -EPERM;
> +                       break;
> +               }
> +               stream->state = AMPDU_STREAM_IN_PROGRESS;
> +               *ssn = 0;
> +               ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);

This is why you have the race you mention below. What you should really
be doing is to wait until the all the packets for that RA / TID are sent
from all the HW Tx queues (only one realistically), and only then call the
_cb. This will allow you to send the ADDBA req on the VO queue as it should
be according to spec.

[snip]

> +
> +void mwl_rx_recv(unsigned long data)
> +{

[snip]

> +
> +                       if (unlikely(ieee80211_is_action(wh->frame_control) &&
> +                                    mgmt->u.action.category ==
> +                                    WLAN_CATEGORY_BACK &&
> +                                    mgmt->u.action.u.addba_resp.action_code ==
> +                                    WLAN_ACTION_ADDBA_RESP)) {
> +                               capab = mgmt->u.action.u.addba_resp.capab;
> +                               if (le16_to_cpu(capab) & 1)
> +                                       mwl_rx_enable_sta_amsdu(priv, mgmt->sa);

err... no. mac80211 knows how to do that today. Johannes already
reported a similar issue somewhere else in your code.

> +                       }
> +               }
> +
> +               if (ieee80211_is_data_qos(wh->frame_control) &&
> +                   ieee80211_has_a4(wh->frame_control)) {
> +                       u8 *qc = ieee80211_get_qos_ctl(wh);
> +
> +                       if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
> +                               if (mwl_rx_process_mesh_amsdu(priv, prx_skb,
> +                                                             &status))

This sounds to be something you want to teach mac80211 about.

[snip]

> +
> +#define SYSADPT_AMSDU_FW_MAX_SIZE      3300
> +
> +#define SYSADPT_AMSDU_4K_MAX_SIZE      SYSADPT_AMSDU_FW_MAX_SIZE
> +
> +#define SYSADPT_AMSDU_8K_MAX_SIZE      SYSADPT_AMSDU_FW_MAX_SIZE

In case the FW will grow later?

> +
> +#define SYSADPT_AMSDU_ALLOW_SIZE       1600
> +
> +#define SYSADPT_AMSDU_FLUSH_TIME       500
> +
> +#define SYSADPT_AMSDU_PACKET_THRESHOLD 10
> +
> +#define SYSADPT_MAX_TID                8
> +
> +#endif /* _mwl_sysadpt_h_ */
> diff --git a/drivers/net/wireless/marvell/mwlwifi/tx.c b/drivers/net/wireless/marvell/mwlwifi/tx.c
> new file mode 100644
> index 0000000..68a994d
> --- /dev/null
> +++ b/drivers/net/wireless/marvell/mwlwifi/tx.c
> @@ -0,0 +1,1251 @@
> +/*
> + * Copyright (C) 2006-2015, Marvell International Ltd.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License").  You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available by writing to the Free Software Foundation, Inc.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +/* Description:  This file implements transmit related functions. */
> +
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +
> +#include "sysadpt.h"
> +#include "dev.h"
> +#include "fwcmd.h"
> +#include "tx.h"
> +
> +#define MAX_NUM_TX_RING_BYTES  (SYSADPT_MAX_NUM_TX_DESC * \
> +                               sizeof(struct mwl_tx_desc))
> +
> +#define MAX_NUM_TX_HNDL_BYTES   (SYSADPT_MAX_NUM_TX_DESC * \
> +                               sizeof(struct mwl_tx_hndl))
> +
> +#define EAGLE_TXD_XMITCTRL_USE_MC_RATE     0x8     /* Use multicast data rate */
> +
> +#define MWL_QOS_ACK_POLICY_MASK                   0x0060
> +#define MWL_QOS_ACK_POLICY_NORMAL          0x0000
> +#define MWL_QOS_ACK_POLICY_BLOCKACK        0x0060
> +
> +#define EXT_IV                             0x20
> +#define INCREASE_IV(iv16, iv32) \
> +{ \
> +       (iv16)++; \
> +       if ((iv16) == 0) \
> +               (iv32)++; \
> +}
> +
> +/* Transmit rate information constants */
> +#define TX_RATE_FORMAT_LEGACY         0
> +#define TX_RATE_FORMAT_11N            1
> +#define TX_RATE_FORMAT_11AC           2
> +
> +#define TX_RATE_BANDWIDTH_20          0
> +#define TX_RATE_BANDWIDTH_40          1
> +#define TX_RATE_BANDWIDTH_80          2
> +
> +#define TX_RATE_INFO_STD_GI           0
> +#define TX_RATE_INFO_SHORT_GI         1
> +
> +enum {
> +       IEEE_TYPE_MANAGEMENT = 0,
> +       IEEE_TYPE_CONTROL,
> +       IEEE_TYPE_DATA
> +};
> +
> +struct ccmp_hdr {
> +       __le16 iv16;
> +       u8 rsvd;
> +       u8 key_id;
> +       __le32 iv32;
> +} __packed;
> +
> +static int mwl_tx_ring_alloc(struct mwl_priv *priv)
> +{
> +       struct mwl_desc_data *desc;
> +       int num;
> +       u8 *mem;
> +
> +       desc = &priv->desc_data[0];
> +
> +       mem = dma_alloc_coherent(&priv->pdev->dev,
> +                                MAX_NUM_TX_RING_BYTES *
> +                                SYSADPT_NUM_OF_DESC_DATA,
> +                                &desc->pphys_tx_ring,
> +                                GFP_KERNEL);
> +
> +       if (!mem) {
> +               wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
> +               return -ENOMEM;
> +       }
> +
> +       for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
> +               desc = &priv->desc_data[num];
> +
> +               desc->ptx_ring = (struct mwl_tx_desc *)
> +                       (mem + num * MAX_NUM_TX_RING_BYTES);
> +
> +               desc->pphys_tx_ring = (dma_addr_t)
> +                       ((u32)priv->desc_data[0].pphys_tx_ring +
> +                       num * MAX_NUM_TX_RING_BYTES);
> +
> +               memset(desc->ptx_ring, 0x00,
> +                      MAX_NUM_TX_RING_BYTES);
> +       }
> +
> +       mem = kmalloc(MAX_NUM_TX_HNDL_BYTES * SYSADPT_NUM_OF_DESC_DATA,
> +                     GFP_KERNEL);
> +
> +       if (!mem) {
> +               wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
> +               dma_free_coherent(&priv->pdev->dev,
> +                                 MAX_NUM_TX_RING_BYTES *
> +                                 SYSADPT_NUM_OF_DESC_DATA,
> +                                 priv->desc_data[0].ptx_ring,
> +                                 priv->desc_data[0].pphys_tx_ring);
> +               return -ENOMEM;
> +       }
> +
> +       for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
> +               desc = &priv->desc_data[num];
> +
> +               desc->tx_hndl = (struct mwl_tx_hndl *)
> +                       (mem + num * MAX_NUM_TX_HNDL_BYTES);
> +
> +               memset(desc->tx_hndl, 0x00,
> +                      MAX_NUM_TX_HNDL_BYTES);
> +       }
> +
> +       return 0;
> +}
> +
> +static int mwl_tx_ring_init(struct mwl_priv *priv)
> +{
> +       int num, i;
> +       struct mwl_desc_data *desc;
> +
> +       for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
> +               skb_queue_head_init(&priv->txq[num]);
> +               priv->fw_desc_cnt[num] = 0;
> +
> +               desc = &priv->desc_data[num];
> +
> +               if (desc->ptx_ring) {
> +                       for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
> +                               desc->ptx_ring[i].status =
> +                                       cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
> +                               desc->ptx_ring[i].pphys_next =
> +                                       cpu_to_le32((u32)desc->pphys_tx_ring +
> +                                       ((i + 1) * sizeof(struct mwl_tx_desc)));
> +                               desc->tx_hndl[i].pdesc =
> +                                       &desc->ptx_ring[i];
> +                               if (i < SYSADPT_MAX_NUM_TX_DESC - 1)
> +                                       desc->tx_hndl[i].pnext =
> +                                               &desc->tx_hndl[i + 1];
> +                       }
> +                       desc->ptx_ring[SYSADPT_MAX_NUM_TX_DESC - 1].pphys_next =
> +                               cpu_to_le32((u32)desc->pphys_tx_ring);
> +                       desc->tx_hndl[SYSADPT_MAX_NUM_TX_DESC - 1].pnext =
> +                               &desc->tx_hndl[0];
> +
> +                       desc->pstale_tx_hndl = &desc->tx_hndl[0];
> +                       desc->pnext_tx_hndl  = &desc->tx_hndl[0];
> +               } else {
> +                       wiphy_err(priv->hw->wiphy, "no valid TX mem\n");
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static void mwl_tx_ring_cleanup(struct mwl_priv *priv)
> +{
> +       int cleaned_tx_desc = 0;
> +       int num, i;
> +       struct mwl_desc_data *desc;
> +
> +       for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
> +               skb_queue_purge(&priv->txq[num]);
> +               priv->fw_desc_cnt[num] = 0;
> +
> +               desc = &priv->desc_data[num];
> +
> +               if (desc->ptx_ring) {
> +                       for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
> +                               if (!desc->tx_hndl[i].psk_buff)
> +                                       continue;
> +
> +                               wiphy_info(priv->hw->wiphy,
> +                                          "unmapped and free'd %i 0x%p 0x%x\n",
> +                                          i,
> +                                          desc->tx_hndl[i].psk_buff->data,
> +                                          le32_to_cpu(
> +                                          desc->ptx_ring[i].pkt_ptr));
> +                               pci_unmap_single(priv->pdev,
> +                                                le32_to_cpu(
> +                                                desc->ptx_ring[i].pkt_ptr),
> +                                                desc->tx_hndl[i].psk_buff->len,
> +                                                PCI_DMA_TODEVICE);
> +                               dev_kfree_skb_any(desc->tx_hndl[i].psk_buff);
> +                               desc->ptx_ring[i].status =
> +                                       cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
> +                               desc->ptx_ring[i].pkt_ptr = 0;
> +                               desc->ptx_ring[i].pkt_len = 0;
> +                               desc->tx_hndl[i].psk_buff = NULL;
> +                               cleaned_tx_desc++;
> +                       }
> +               }
> +       }
> +
> +       wiphy_info(priv->hw->wiphy, "cleaned %i TX descr\n", cleaned_tx_desc);
> +}
> +
> +static void mwl_tx_ring_free(struct mwl_priv *priv)
> +{
> +       int num;
> +
> +       if (priv->desc_data[0].ptx_ring) {
> +               dma_free_coherent(&priv->pdev->dev,
> +                                 MAX_NUM_TX_RING_BYTES *
> +                                 SYSADPT_NUM_OF_DESC_DATA,
> +                                 priv->desc_data[0].ptx_ring,
> +                                 priv->desc_data[0].pphys_tx_ring);
> +       }
> +
> +       for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
> +               if (priv->desc_data[num].ptx_ring)
> +                       priv->desc_data[num].ptx_ring = NULL;
> +               priv->desc_data[num].pstale_tx_hndl = NULL;
> +               priv->desc_data[num].pnext_tx_hndl = NULL;
> +       }
> +
> +       kfree(priv->desc_data[0].tx_hndl);
> +}
> +
> +static inline void mwl_tx_add_dma_header(struct mwl_priv *priv,
> +                                        struct sk_buff *skb,
> +                                        int head_pad,
> +                                        int tail_pad)
> +{
> +       struct ieee80211_hdr *wh;
> +       int hdrlen;
> +       int reqd_hdrlen;
> +       struct mwl_dma_data *tr;
> +
> +       /* Add a firmware DMA header; the firmware requires that we
> +        * present a 2-byte payload length followed by a 4-address
> +        * header (without QoS field), followed (optionally) by any
> +        * WEP/ExtIV header (but only filled in for CCMP).
> +        */
> +       wh = (struct ieee80211_hdr *)skb->data;
> +
> +       hdrlen = ieee80211_hdrlen(wh->frame_control);
> +
> +       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 -= IEEE80211_QOS_CTL_LEN;
> +
> +       tr = (struct mwl_dma_data *)skb->data;
> +
> +       if (wh != &tr->wh)
> +               memmove(&tr->wh, wh, hdrlen);
> +
> +       if (hdrlen != sizeof(tr->wh))
> +               memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
> +
> +       /* Firmware length is the length of the fully formed "802.11
> +        * 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) + tail_pad);
> +}
> +
> +static inline void mwl_tx_encapsulate_frame(struct mwl_priv *priv,
> +                                           struct sk_buff *skb,
> +                                           struct ieee80211_key_conf *k_conf,
> +                                           bool *ccmp)
> +{
> +       int head_pad = 0;
> +       int data_pad = 0;
> +
> +       /* 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)
> +        */
> +
> +       if (k_conf) {
> +               head_pad = k_conf->iv_len;
> +
> +               switch (k_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;
> +                       *ccmp = true;
> +                       break;
> +               }
> +       }
> +
> +       mwl_tx_add_dma_header(priv, skb, head_pad, data_pad);
> +}
> +
> +static inline void mwl_tx_insert_ccmp_hdr(u8 *pccmp_hdr,
> +                                         u8 key_id, u16 iv16, u32 iv32)
> +{
> +       struct ccmp_hdr *ccmp_h = (struct ccmp_hdr *)pccmp_hdr;
> +
> +       ccmp_h->iv16 = cpu_to_le16(iv16);
> +       ccmp_h->rsvd = 0;
> +       ccmp_h->key_id = EXT_IV | (key_id << 6);
> +       ccmp_h->iv32 = cpu_to_le32(iv32);
> +}
> +
> +static inline int mwl_tx_tid_queue_mapping(u8 tid)
> +{
> +       switch (tid) {
> +       case 0:
> +       case 3:
> +               return IEEE80211_AC_BE;
> +       case 1:
> +       case 2:
> +               return IEEE80211_AC_BK;
> +       case 4:
> +       case 5:
> +               return IEEE80211_AC_VI;
> +       case 6:
> +       case 7:
> +               return IEEE80211_AC_VO;
> +       default:
> +               break;
> +       }
> +
> +       return -1;
> +}
> +
> +static inline void mwl_tx_count_packet(struct ieee80211_sta *sta, u8 tid)
> +{
> +       struct mwl_sta *sta_info;
> +       struct mwl_tx_info *tx_stats;
> +
> +       if (WARN_ON(tid >= SYSADPT_MAX_TID))
> +               return;
> +
> +       sta_info = mwl_dev_get_sta(sta);
> +
> +       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++;
> +       }
> +}
> +
> +static inline bool mwl_tx_available(struct mwl_priv *priv, int desc_num)
> +{
> +       struct mwl_tx_hndl *tx_hndl;
> +
> +       tx_hndl = priv->desc_data[desc_num].pnext_tx_hndl;
> +
> +       if (!tx_hndl->pdesc)
> +               return false;
> +
> +       if (tx_hndl->pdesc->status != EAGLE_TXD_STATUS_IDLE) {
> +               /* Interrupt F/W anyway */
> +               if (tx_hndl->pdesc->status &
> +                   cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED))
> +                       writel(MACREG_H2ARIC_BIT_PPA_READY,
> +                              priv->iobase1 +
> +                              MACREG_REG_H2A_INTERRUPT_EVENTS);
> +               return false;
> +       }
> +
> +       return true;
> +}
> +
> +static inline void mwl_tx_skb(struct mwl_priv *priv, int desc_num,
> +                             struct sk_buff *tx_skb)
> +{
> +       struct ieee80211_tx_info *tx_info;
> +       struct mwl_tx_ctrl *tx_ctrl;
> +       struct mwl_tx_hndl *tx_hndl;
> +       struct mwl_tx_desc *tx_desc;
> +       struct ieee80211_sta *sta;
> +       struct ieee80211_vif *vif;
> +       struct mwl_vif *mwl_vif;
> +       struct ieee80211_key_conf *k_conf;
> +       bool ccmp = false;
> +       struct mwl_dma_data *dma_data;
> +       struct ieee80211_hdr *wh;
> +       dma_addr_t dma;
> +
> +       if (WARN_ON(!tx_skb))
> +               return;
> +
> +       tx_info = IEEE80211_SKB_CB(tx_skb);
> +       tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
> +       sta = (struct ieee80211_sta *)tx_ctrl->sta;
> +       vif = (struct ieee80211_vif *)tx_ctrl->vif;
> +       mwl_vif = mwl_dev_get_vif(vif);
> +       k_conf = (struct ieee80211_key_conf *)tx_ctrl->k_conf;
> +
> +       mwl_tx_encapsulate_frame(priv, tx_skb, k_conf, &ccmp);
> +
> +       dma_data = (struct mwl_dma_data *)tx_skb->data;
> +       wh = &dma_data->wh;
> +
> +       if (ieee80211_is_data(wh->frame_control)) {
> +               if (is_multicast_ether_addr(wh->addr1)) {
> +                       if (ccmp) {
> +                               mwl_tx_insert_ccmp_hdr(dma_data->data,
> +                                                      mwl_vif->keyidx,
> +                                                      mwl_vif->iv16,
> +                                                      mwl_vif->iv32);
> +                               INCREASE_IV(mwl_vif->iv16, mwl_vif->iv32);
> +                       }
> +               } else {
> +                       if (ccmp) {
> +                               if (vif->type == NL80211_IFTYPE_STATION) {
> +                                       mwl_tx_insert_ccmp_hdr(dma_data->data,
> +                                                              mwl_vif->keyidx,
> +                                                              mwl_vif->iv16,
> +                                                              mwl_vif->iv32);
> +                                       INCREASE_IV(mwl_vif->iv16,
> +                                                   mwl_vif->iv32);
> +                               } else {
> +                                       struct mwl_sta *sta_info;
> +
> +                                       sta_info = mwl_dev_get_sta(sta);
> +
> +                                       mwl_tx_insert_ccmp_hdr(dma_data->data,
> +                                                              0,
> +                                                              sta_info->iv16,
> +                                                              sta_info->iv32);
> +                                       INCREASE_IV(sta_info->iv16,
> +                                                   sta_info->iv32);
> +                               }
> +                       }
> +               }
> +       }
> +
> +       tx_hndl = priv->desc_data[desc_num].pnext_tx_hndl;
> +       tx_hndl->psk_buff = tx_skb;
> +       tx_desc = tx_hndl->pdesc;
> +       tx_desc->tx_priority = tx_ctrl->tx_priority;
> +       tx_desc->qos_ctrl = cpu_to_le16(tx_ctrl->qos_ctrl);
> +       tx_desc->pkt_len = cpu_to_le16(tx_skb->len);
> +       tx_desc->packet_info = 0;
> +       tx_desc->data_rate = 0;
> +       tx_desc->type = tx_ctrl->type;
> +       tx_desc->xmit_control = tx_ctrl->xmit_control;
> +       tx_desc->sap_pkt_info = 0;
> +       dma = pci_map_single(priv->pdev, tx_skb->data,
> +                            tx_skb->len, PCI_DMA_TODEVICE);
> +       if (pci_dma_mapping_error(priv->pdev, dma)) {
> +               dev_kfree_skb_any(tx_skb);
> +               wiphy_err(priv->hw->wiphy,
> +                         "failed to map pci memory!\n");
> +               return;
> +       }
> +       tx_desc->pkt_ptr = cpu_to_le32(dma);
> +       tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED);
> +       /* make sure all the memory transactions done by cpu were completed */
> +       wmb();  /*Data Memory Barrier*/
> +       writel(MACREG_H2ARIC_BIT_PPA_READY,
> +              priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
> +       priv->desc_data[desc_num].pnext_tx_hndl = tx_hndl->pnext;
> +       priv->fw_desc_cnt[desc_num]++;
> +}
> +
> +static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv,
> +                                             int desc_num,
> +                                             struct sk_buff *tx_skb,
> +                                             struct ieee80211_tx_info *tx_info)
> +{
> +       struct ieee80211_sta *sta;
> +       struct mwl_sta *sta_info;
> +       struct mwl_tx_ctrl *tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
> +       struct ieee80211_tx_info *amsdu_info;
> +       struct sk_buff_head *amsdu_pkts;
> +       struct mwl_amsdu_frag *amsdu;
> +       int amsdu_allow_size;
> +       struct ieee80211_hdr *wh;
> +       int wh_len;
> +       u16 len;
> +       u8 *data;
> +
> +       sta = (struct ieee80211_sta *)tx_ctrl->sta;
> +       sta_info = mwl_dev_get_sta(sta);
> +
> +       if (!sta_info->is_amsdu_allowed)
> +               return tx_skb;
> +
> +       wh = (struct ieee80211_hdr *)tx_skb->data;
> +       if (sta_info->is_mesh_node && is_multicast_ether_addr(wh->addr3))
> +               return tx_skb;
> +
> +       if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_4K)
> +               amsdu_allow_size = SYSADPT_AMSDU_4K_MAX_SIZE;
> +       else if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_8K)
> +               amsdu_allow_size = SYSADPT_AMSDU_8K_MAX_SIZE;
> +       else
> +               return tx_skb;
> +
> +       spin_lock_bh(&sta_info->amsdu_lock);
> +       amsdu = &sta_info->amsdu_ctrl.frag[desc_num];
> +
> +       if (tx_skb->len > SYSADPT_AMSDU_ALLOW_SIZE) {
> +               if (amsdu->num) {
> +                       mwl_tx_skb(priv, desc_num, amsdu->skb);
> +                       amsdu->num = 0;
> +                       amsdu->cur_pos = NULL;
> +
> +                       if (!mwl_tx_available(priv, desc_num)) {
> +                               skb_queue_head(&priv->txq[desc_num], tx_skb);
> +                               spin_unlock_bh(&sta_info->amsdu_lock);
> +                               return NULL;
> +                       }
> +               }
> +               spin_unlock_bh(&sta_info->amsdu_lock);
> +               return tx_skb;
> +       }
> +
> +       /* potential amsdu size, should add amsdu header 14 bytes +
> +        * maximum padding 3.
> +        */
> +       wh_len = ieee80211_hdrlen(wh->frame_control);
> +       len = tx_skb->len - wh_len + 17;
> +
> +       if (amsdu->num) {
> +               if ((amsdu->skb->len + len) > amsdu_allow_size) {
> +                       mwl_tx_skb(priv, desc_num, amsdu->skb);
> +                       amsdu->num = 0;
> +                       amsdu->cur_pos = NULL;
> +               }
> +       }
> +
> +       amsdu->jiffies = jiffies;
> +       len = tx_skb->len - wh_len;
> +
> +       if (amsdu->num == 0) {
> +               struct sk_buff *newskb;
> +
> +               amsdu_pkts = (struct sk_buff_head *)
> +                       kmalloc(sizeof(*amsdu_pkts), GFP_KERNEL);
> +               if (!amsdu_pkts) {
> +                       spin_unlock_bh(&sta_info->amsdu_lock);
> +                       return tx_skb;
> +               }
> +               newskb = dev_alloc_skb(amsdu_allow_size +
> +                                      SYSADPT_MIN_BYTES_HEADROOM);
> +               if (!newskb) {
> +                       spin_unlock_bh(&sta_info->amsdu_lock);
> +                       kfree(amsdu_pkts);
> +                       return tx_skb;
> +               }
> +
> +               data = newskb->data;
> +               memcpy(data, tx_skb->data, wh_len);
> +               if (sta_info->is_mesh_node) {
> +                       ether_addr_copy(data + wh_len, wh->addr3);
> +                       ether_addr_copy(data + wh_len + ETH_ALEN, wh->addr4);
> +               } else {
> +                       ether_addr_copy(data + wh_len,
> +                                       ieee80211_get_DA(wh));
> +                       ether_addr_copy(data + wh_len + ETH_ALEN,
> +                                       ieee80211_get_SA(wh));
> +               }
> +               *(u8 *)(data + wh_len + ETH_HLEN - 1) = len & 0xff;
> +               *(u8 *)(data + wh_len + ETH_HLEN - 2) = (len >> 8) & 0xff;

These bit oeprations are not needed. The pointer must be WORD aligned, right?
This is why you have padding after all, unless you have some weird DMA games?
You seem to be assuming this alignment anyway because you use
ether_addr_copy above.

> +
> +       /* 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.
> +        */

Here is the race you are facing and that race can be avoided by the proper usage
of the _cb

> +       if (mgmtframe) {
> +               u16 capab;
> +
> +               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)) {
> +                       capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
> +                       tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
> +                       index = mwl_tx_tid_queue_mapping(tid);
> +                       capab |= 1;
> +                       mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
> +               }
> +
> +               if (unlikely(ieee80211_is_action(wh->frame_control) &&
> +                            mgmt->u.action.category == WLAN_CATEGORY_BACK &&
> +                            mgmt->u.action.u.addba_resp.action_code ==
> +                            WLAN_ACTION_ADDBA_RESP)) {
> +                       capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
> +                       capab |= 1;
> +                       mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
> +               }
> +       }
> +
> +       index = SYSADPT_TX_WMM_QUEUES - index - 1;
> +       txpriority = index;
> +
> +       if (sta && sta->ht_cap.ht_supported && !eapol_frame &&
> +           ieee80211_is_data_qos(wh->frame_control)) {
> +               tid = qos & 0xf;
> +               mwl_tx_count_packet(sta, tid);
> +
> +               spin_lock_bh(&priv->stream_lock);
> +               stream = mwl_fwcmd_lookup_stream(hw, sta->addr, tid);
> +
> +               if (stream) {
> +                       if (stream->state == AMPDU_STREAM_ACTIVE) {
> +                               if (WARN_ON(!(qos &
> +                                           MWL_QOS_ACK_POLICY_BLOCKACK))) {
> +                                       spin_unlock_bh(&priv->stream_lock);
> +                                       dev_kfree_skb_any(skb);
> +                                       return;
> +                               }
> +
> +                               txpriority =
> +                                       (SYSADPT_TX_WMM_QUEUES + stream->idx) %
> +                                       SYSADPT_TOTAL_HW_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.
> +                                */

Well... I think you can really avoid that.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Lin Dec. 15, 2015, 7:53 a.m. UTC | #17
PiBFbW1hbnVlbCBHcnVtYmFjaCBbbWFpbHRvOmVncnVtYmFjaEBnbWFpbC5jb21dIHdyb3RlOg0K
PiANCj4gSGksDQo+IA0KPiBJIHdhcyBpbnRlcmVzdGVkIGluIHRoZSBjaGVja2luZyB0aGUgQS1N
U0RVIGltcGxlbWVudGF0aW9uIG9ubHksIHNvIEkNCj4gcmV2aWV3ZWQgb25seSB0aGF0Lg0KPiAN
Cj4gSSBhbSBub3Qgc3VyZSBJIHJlYWxseSBmb2xsb3dlZCB5b3VyIGltcGxlbWVudGF0aW9uIHRo
b3VnaC4NCj4gSW4gaXdsd2lmaSBJIGNob3NlIHRoZSBMU08gbWVjaGFuaXNtIHRvIGltcGxlbWVu
dCBBLU1TRFUuIEkgdW5kZXJzdGFuZCB0aGF0DQo+IGZvciBhbiBBUCBpdCBtYWtlcyBsZXNzIHNl
bnNlIHNpbmNlIHlvdSBkb24ndCByZWFsbHkgaGF2ZSBsb2NhbGx5IGdlbmVyYXRlZA0KPiB0cmFm
ZmljIGFuZCBMU08gd29uJ3Qgd29yayB3ZWxsIHdpdGggcGFja2V0cyBmb3J3YXJkZWQgYnkgdGhl
IElQIGxheWVyLg0KPiBTbyB5b3Ugc2VlbSB0byBoYXZlIGFkZGVkIGEgdGltZW91dCBtZWNoYW5p
c20gaW5zaWRlIHRoZSBkcml2ZXIgdGhhdA0KPiBhY2N1bXVsYXRlcyB0aGUgcGFja2V0IGZvciBh
IGNlcnRhaW4gcGVyaW9kIG9mIHRpbWUgYW5kIHNlbmQgdGhlbSBhbGwgd2hlbg0KPiB0aGUgbWF4
IG51bWJlciBvZiBwYWNrZXRzIGhhcyBiZWVuIHJlYWNoZWQ/DQo+IEJ1dCBJIGRpZG4ndCBzZWUg
YW55IHRpbWVyIHRoZXJlLCBzbyB0aGF0IGlmIHlvdSBzdGFydCBhbiBBLU1TRFUgYW5kIHRoZW4N
Cj4gc3VkZGVubHkgc3RvcCByZWNlaXZpbmcgcGFja2V0cyBmb3IgdHJhbnNtaXNzaW9uLCB0aGUg
cGFja2V0IGFscmVhZHkgaW4gdGhlDQo+IGJ1ZmZlciB3aWxsIHN0YXkgc3RhbGU/DQo+DQoNClRo
ZSBmbHVzaCBtZWNoYW5pc20gaXMgaG9va2VkIG9uIHF1ZXVlIGVtcHR5IGludGVycnVwdC4NCg0K
PiBBIGZldyBtb3JlIGNvbW1lbnRzIGJlbG93Lg0KPiANCj4gT24gVGh1LCBOb3YgMTIsIDIwMTUg
YXQgNTo1MSBBTSwgRGF2aWQgTGluIDxkbGluQG1hcnZlbGwuY29tPiB3cm90ZToNCj4gPiBUaGlz
IHBhdGNoIHByb3ZpZGVzIHRoZSBtd2x3aWZpIGRyaXZlciBmb3IgTWFydmVsbCA4ODYzLCA4ODY0
IGFuZCA4ODk3DQo+ID4gY2hpcHNldHMuDQo+ID4gVGhpcyBkcml2ZXIgd2FzIGRldmVsb3BlZCBh
cyBwYXJ0IG9mIHRoZSBvcGVud3J0Lm9yZyBwcm9qZWN0IHRvDQo+ID4gc3VwcG9ydCBMaW5rc3lz
IFdSVDE5MDBBQyBhbmQgaXMgbWFpbnRhaW5lZCBvbg0KPiBodHRwczovL2dpdGh1Yi5jb20va2Fs
b3ovbXdsd2lmaS4NCj4gPg0KPiA+IFRoZSBtd2x3aWZpIGRyaXZlciBkaWZmZXJzIGZyb20gZXhp
c3RpbmcgbXdpZmlleCBkcml2ZXI6DQo+ID4gbyBtd2x3aWZpIGlzIGEgInNvZnRtYWMgZHJpdmVy
IiB1c2luZyB0aGUga2VybmVsP+6ogyBtYWM4MDIuMTEgc3Vic3lzdGVtDQo+ID4gdG8gcHJvdmlk
ZSBmdWxsIEFQL1dpcmVsZXNzIEJyaWRnZSBmdW5jdGlvbmFsaXR5IChyb3V0ZXJzKS4NCj4gPiBv
IG13aWZpZXggaXMgYSAiZnVsbG1hYyBkcml2ZXIiIHdoaWNoIHByb3ZpZGVzIGEgY29tcHJlaGVu
c2l2ZSBzZXQgb2YNCj4gPiBjbGllbnQgZnVuY3Rpb25zIChsYXB0b3BzL2VtYmVkZGVkIGRldmlj
ZXMpIG8gb25seSBtd2x3aWZpIHN1cHBvcnRzDQo+ID4gTWFydmVsbCBBUCBjaGlwIDg4Nlggc2Vy
aWVzDQo+ID4NCj4gDQo+IFtzbmlwXQ0KPiANCj4gPiArDQo+ID4gK3N0YXRpYyBpbnQgbXdsX21h
YzgwMjExX2FtcGR1X2FjdGlvbihzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodywNCj4gPiArICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RydWN0IGllZWU4MDIxMV92aWYgKnZpZiwN
Cj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW51bQ0KPiBpZWVlODAy
MTFfYW1wZHVfbWxtZV9hY3Rpb24gYWN0aW9uLA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICBzdHJ1Y3QgaWVlZTgwMjExX3N0YSAqc3RhLA0KPiA+ICsgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICB1MTYgdGlkLCB1MTYgKnNzbiwgdTggYnVmX3NpemUs
DQo+ID4gK2Jvb2wgYW1zZHUpIHsNCj4gPiArICAgICAgIGludCByYyA9IDA7DQo+ID4gKyAgICAg
ICBzdHJ1Y3QgbXdsX3ByaXYgKnByaXYgPSBody0+cHJpdjsNCj4gPiArICAgICAgIHN0cnVjdCBt
d2xfYW1wZHVfc3RyZWFtICpzdHJlYW07DQo+ID4gKyAgICAgICB1OCAqYWRkciA9IHN0YS0+YWRk
ciwgaWR4Ow0KPiA+ICsgICAgICAgc3RydWN0IG13bF9zdGEgKnN0YV9pbmZvOw0KPiA+ICsNCj4g
PiArICAgICAgIHN0YV9pbmZvID0gbXdsX2Rldl9nZXRfc3RhKHN0YSk7DQo+ID4gKw0KPiA+ICsg
ICAgICAgc3Bpbl9sb2NrX2JoKCZwcml2LT5zdHJlYW1fbG9jayk7DQo+ID4gKw0KPiA+ICsgICAg
ICAgc3RyZWFtID0gbXdsX2Z3Y21kX2xvb2t1cF9zdHJlYW0oaHcsIGFkZHIsIHRpZCk7DQo+ID4g
Kw0KPiA+ICsgICAgICAgc3dpdGNoIChhY3Rpb24pIHsNCj4gPiArICAgICAgIGNhc2UgSUVFRTgw
MjExX0FNUERVX1JYX1NUQVJUOg0KPiA+ICsgICAgICAgY2FzZSBJRUVFODAyMTFfQU1QRFVfUlhf
U1RPUDoNCj4gPiArICAgICAgICAgICAgICAgYnJlYWs7DQo+ID4gKyAgICAgICBjYXNlIElFRUU4
MDIxMV9BTVBEVV9UWF9TVEFSVDoNCj4gPiArICAgICAgICAgICAgICAgaWYgKCFzdGFfaW5mby0+
aXNfYW1wZHVfYWxsb3dlZCkgew0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIHdpcGh5X3dh
cm4oaHctPndpcGh5LCAiYW1wZHUgbm90DQo+IGFsbG93ZWRcbiIpOw0KPiA+ICsgICAgICAgICAg
ICAgICAgICAgICAgIHJjID0gLUVQRVJNOw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIGJy
ZWFrOw0KPiA+ICsgICAgICAgICAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICBp
ZiAoIXN0cmVhbSkgew0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIHN0cmVhbSA9IG13bF9m
d2NtZF9hZGRfc3RyZWFtKGh3LCBzdGEsDQo+IHRpZCk7DQo+ID4gKyAgICAgICAgICAgICAgICAg
ICAgICAgaWYgKCFzdHJlYW0pIHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
IHdpcGh5X3dhcm4oaHctPndpcGh5LCAibm8gc3RyZWFtDQo+IGZvdW5kXG4iKTsNCj4gPiArICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJjID0gLUVQRVJNOw0KPiA+ICsgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAg
fQ0KPiA+ICsgICAgICAgICAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICBzcGlu
X3VubG9ja19iaCgmcHJpdi0+c3RyZWFtX2xvY2spOw0KPiA+ICsgICAgICAgICAgICAgICByYyA9
IG13bF9md2NtZF9jaGVja19iYShodywgc3RyZWFtLCB2aWYpOw0KPiA+ICsgICAgICAgICAgICAg
ICBzcGluX2xvY2tfYmgoJnByaXYtPnN0cmVhbV9sb2NrKTsNCj4gPiArICAgICAgICAgICAgICAg
aWYgKHJjKSB7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgbXdsX2Z3Y21kX3JlbW92ZV9z
dHJlYW0oaHcsIHN0cmVhbSk7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgd2lwaHlfZXJy
KGh3LT53aXBoeSwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFtcGR1
IHN0YXJ0IGVycm9yIGNvZGU6ICVkXG4iLA0KPiByYyk7DQo+ID4gKyAgICAgICAgICAgICAgICAg
ICAgICAgcmMgPSAtRVBFUk07DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQo+
ID4gKyAgICAgICAgICAgICAgIH0NCj4gPiArICAgICAgICAgICAgICAgc3RyZWFtLT5zdGF0ZSA9
IEFNUERVX1NUUkVBTV9JTl9QUk9HUkVTUzsNCj4gPiArICAgICAgICAgICAgICAgKnNzbiA9IDA7
DQo+ID4gKyAgICAgICAgICAgICAgIGllZWU4MDIxMV9zdGFydF90eF9iYV9jYl9pcnFzYWZlKHZp
ZiwgYWRkciwgdGlkKTsNCj4gDQo+IFRoaXMgaXMgd2h5IHlvdSBoYXZlIHRoZSByYWNlIHlvdSBt
ZW50aW9uIGJlbG93LiBXaGF0IHlvdSBzaG91bGQgcmVhbGx5IGJlDQo+IGRvaW5nIGlzIHRvIHdh
aXQgdW50aWwgdGhlIGFsbCB0aGUgcGFja2V0cyBmb3IgdGhhdCBSQSAvIFRJRCBhcmUgc2VudCBm
cm9tIGFsbCB0aGUNCj4gSFcgVHggcXVldWVzIChvbmx5IG9uZSByZWFsaXN0aWNhbGx5KSwgYW5k
IG9ubHkgdGhlbiBjYWxsIHRoZSBfY2IuIFRoaXMgd2lsbA0KPiBhbGxvdyB5b3UgdG8gc2VuZCB0
aGUgQUREQkEgcmVxIG9uIHRoZSBWTyBxdWV1ZSBhcyBpdCBzaG91bGQgYmUgYWNjb3JkaW5nIHRv
DQo+IHNwZWMuDQo+IA0KPiBbc25pcF0NCj4gDQo+ID4gKw0KPiA+ICt2b2lkIG13bF9yeF9yZWN2
KHVuc2lnbmVkIGxvbmcgZGF0YSkNCj4gPiArew0KPiANCj4gW3NuaXBdDQo+IA0KPiA+ICsNCj4g
PiArICAgICAgICAgICAgICAgICAgICAgICBpZg0KPiAodW5saWtlbHkoaWVlZTgwMjExX2lzX2Fj
dGlvbih3aC0+ZnJhbWVfY29udHJvbCkgJiYNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgbWdtdC0+dS5hY3Rpb24uY2F0ZWdvcnkgPT0NCj4gPiArICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgV0xBTl9DQVRFR09SWV9CQUNLICYmDQo+ID4gKw0KPiBt
Z210LT51LmFjdGlvbi51LmFkZGJhX3Jlc3AuYWN0aW9uX2NvZGUgPT0NCj4gPiArICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgV0xBTl9BQ1RJT05fQUREQkFfUkVTUCkpDQo+IHsN
Cj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhcGFiID0NCj4gbWdtdC0+dS5h
Y3Rpb24udS5hZGRiYV9yZXNwLmNhcGFiOw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgaWYgKGxlMTZfdG9fY3B1KGNhcGFiKSAmIDEpDQo+ID4gKw0KPiBtd2xfcnhfZW5hYmxl
X3N0YV9hbXNkdShwcml2LA0KPiA+ICsgbWdtdC0+c2EpOw0KPiANCj4gZXJyLi4uIG5vLiBtYWM4
MDIxMSBrbm93cyBob3cgdG8gZG8gdGhhdCB0b2RheS4gSm9oYW5uZXMgYWxyZWFkeSByZXBvcnRl
ZCBhDQo+IHNpbWlsYXIgaXNzdWUgc29tZXdoZXJlIGVsc2UgaW4geW91ciBjb2RlLg0KDQpZZXMu
IFRoZSBwYXRjaCBpcyByZWFkeSBvbiAyMDE1LzkuDQoNCj4gDQo+ID4gKyAgICAgICAgICAgICAg
ICAgICAgICAgfQ0KPiA+ICsgICAgICAgICAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgICAg
ICAgICBpZiAoaWVlZTgwMjExX2lzX2RhdGFfcW9zKHdoLT5mcmFtZV9jb250cm9sKSAmJg0KPiA+
ICsgICAgICAgICAgICAgICAgICAgaWVlZTgwMjExX2hhc19hNCh3aC0+ZnJhbWVfY29udHJvbCkp
IHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICB1OCAqcWMgPSBpZWVlODAyMTFfZ2V0X3Fv
c19jdGwod2gpOw0KPiA+ICsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBpZiAoKnFjICYN
Cj4gSUVFRTgwMjExX1FPU19DVExfQV9NU0RVX1BSRVNFTlQpDQo+ID4gKyAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICBpZg0KPiAobXdsX3J4X3Byb2Nlc3NfbWVzaF9hbXNkdShwcml2LCBw
cnhfc2tiLA0KPiA+ICsNCj4gPiArICZzdGF0dXMpKQ0KPiANCj4gVGhpcyBzb3VuZHMgdG8gYmUg
c29tZXRoaW5nIHlvdSB3YW50IHRvIHRlYWNoIG1hYzgwMjExIGFib3V0Lg0KPiANCj4gW3NuaXBd
DQo+IA0KPiA+ICsNCj4gPiArI2RlZmluZSBTWVNBRFBUX0FNU0RVX0ZXX01BWF9TSVpFICAgICAg
MzMwMA0KPiA+ICsNCj4gPiArI2RlZmluZSBTWVNBRFBUX0FNU0RVXzRLX01BWF9TSVpFDQo+IFNZ
U0FEUFRfQU1TRFVfRldfTUFYX1NJWkUNCj4gPiArDQo+ID4gKyNkZWZpbmUgU1lTQURQVF9BTVNE
VV84S19NQVhfU0laRQ0KPiBTWVNBRFBUX0FNU0RVX0ZXX01BWF9TSVpFDQo+IA0KPiBJbiBjYXNl
IHRoZSBGVyB3aWxsIGdyb3cgbGF0ZXI/DQo+IA0KPiA+ICsNCj4gPiArI2RlZmluZSBTWVNBRFBU
X0FNU0RVX0FMTE9XX1NJWkUgICAgICAgMTYwMA0KPiA+ICsNCj4gPiArI2RlZmluZSBTWVNBRFBU
X0FNU0RVX0ZMVVNIX1RJTUUgICAgICAgNTAwDQo+ID4gKw0KPiA+ICsjZGVmaW5lIFNZU0FEUFRf
QU1TRFVfUEFDS0VUX1RIUkVTSE9MRCAxMA0KPiA+ICsNCj4gPiArI2RlZmluZSBTWVNBRFBUX01B
WF9USUQgICAgICAgICAgICAgICAgOA0KPiA+ICsNCj4gPiArI2VuZGlmIC8qIF9td2xfc3lzYWRw
dF9oXyAqLw0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJzL25ldC93aXJlbGVzcy9tYXJ2ZWxsL213
bHdpZmkvdHguYw0KPiA+IGIvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFydmVsbC9td2x3aWZpL3R4
LmMNCj4gPiBuZXcgZmlsZSBtb2RlIDEwMDY0NA0KPiA+IGluZGV4IDAwMDAwMDAuLjY4YTk5NGQN
Cj4gPiAtLS0gL2Rldi9udWxsDQo+ID4gKysrIGIvZHJpdmVycy9uZXQvd2lyZWxlc3MvbWFydmVs
bC9td2x3aWZpL3R4LmMNCj4gPiBAQCAtMCwwICsxLDEyNTEgQEANCj4gPiArLyoNCj4gPiArICog
Q29weXJpZ2h0IChDKSAyMDA2LTIwMTUsIE1hcnZlbGwgSW50ZXJuYXRpb25hbCBMdGQuDQo+ID4g
KyAqDQo+ID4gKyAqIFRoaXMgc29mdHdhcmUgZmlsZSAodGhlICJGaWxlIikgaXMgZGlzdHJpYnV0
ZWQgYnkgTWFydmVsbA0KPiA+ICtJbnRlcm5hdGlvbmFsDQo+ID4gKyAqIEx0ZC4gdW5kZXIgdGhl
IHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBWZXJzaW9uIDIsDQo+ID4g
K0p1bmUgMTk5MQ0KPiA+ICsgKiAodGhlICJMaWNlbnNlIikuICBZb3UgbWF5IHVzZSwgcmVkaXN0
cmlidXRlIGFuZC9vciBtb2RpZnkgdGhpcw0KPiA+ICtGaWxlIGluDQo+ID4gKyAqIGFjY29yZGFu
Y2Ugd2l0aCB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdGhlIExpY2Vuc2UsIGEgY29weSBv
Zg0KPiA+ICt3aGljaA0KPiA+ICsgKiBpcyBhdmFpbGFibGUgYnkgd3JpdGluZyB0byB0aGUgRnJl
ZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBJbmMuDQo+ID4gKyAqDQo+ID4gKyAqIFRIRSBGSUxFIElT
IERJU1RSSUJVVEVEIEFTLUlTLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBBTkQNCj4g
PiArVEhFDQo+ID4gKyAqIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFkgT1Ig
RklUTkVTUyBGT1IgQQ0KPiBQQVJUSUNVTEFSDQo+ID4gK1BVUlBPU0UNCj4gPiArICogQVJFIEVY
UFJFU1NMWSBESVNDTEFJTUVELiAgVGhlIExpY2Vuc2UgcHJvdmlkZXMgYWRkaXRpb25hbCBkZXRh
aWxzDQo+ID4gK2Fib3V0DQo+ID4gKyAqIHRoaXMgd2FycmFudHkgZGlzY2xhaW1lci4NCj4gPiAr
ICovDQo+ID4gKw0KPiA+ICsvKiBEZXNjcmlwdGlvbjogIFRoaXMgZmlsZSBpbXBsZW1lbnRzIHRy
YW5zbWl0IHJlbGF0ZWQgZnVuY3Rpb25zLiAqLw0KPiA+ICsNCj4gPiArI2luY2x1ZGUgPGxpbnV4
L2V0aGVyZGV2aWNlLmg+DQo+ID4gKyNpbmNsdWRlIDxsaW51eC9za2J1ZmYuaD4NCj4gPiArDQo+
ID4gKyNpbmNsdWRlICJzeXNhZHB0LmgiDQo+ID4gKyNpbmNsdWRlICJkZXYuaCINCj4gPiArI2lu
Y2x1ZGUgImZ3Y21kLmgiDQo+ID4gKyNpbmNsdWRlICJ0eC5oIg0KPiA+ICsNCj4gPiArI2RlZmlu
ZSBNQVhfTlVNX1RYX1JJTkdfQllURVMgIChTWVNBRFBUX01BWF9OVU1fVFhfREVTQyAqIFwNCj4g
PiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemVvZihzdHJ1Y3QgbXdsX3R4X2Rl
c2MpKQ0KPiA+ICsNCj4gPiArI2RlZmluZSBNQVhfTlVNX1RYX0hORExfQllURVMgICAoU1lTQURQ
VF9NQVhfTlVNX1RYX0RFU0MgKg0KPiBcDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICBzaXplb2Yoc3RydWN0IG13bF90eF9obmRsKSkNCj4gPiArDQo+ID4gKyNkZWZpbmUgRUFH
TEVfVFhEX1hNSVRDVFJMX1VTRV9NQ19SQVRFICAgICAweDggICAgIC8qIFVzZQ0KPiBtdWx0aWNh
c3QgZGF0YSByYXRlICovDQo+ID4gKw0KPiA+ICsjZGVmaW5lIE1XTF9RT1NfQUNLX1BPTElDWV9N
QVNLICAgICAgICAgICAgICAgICAgIDB4MDA2MA0KPiA+ICsjZGVmaW5lIE1XTF9RT1NfQUNLX1BP
TElDWV9OT1JNQUwgICAgICAgICAgMHgwMDAwDQo+ID4gKyNkZWZpbmUgTVdMX1FPU19BQ0tfUE9M
SUNZX0JMT0NLQUNLICAgICAgICAweDAwNjANCj4gPiArDQo+ID4gKyNkZWZpbmUgRVhUX0lWICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAweDIwDQo+ID4gKyNkZWZpbmUgSU5DUkVBU0VfSVYo
aXYxNiwgaXYzMikgXA0KPiA+ICt7IFwNCj4gPiArICAgICAgIChpdjE2KSsrOyBcDQo+ID4gKyAg
ICAgICBpZiAoKGl2MTYpID09IDApIFwNCj4gPiArICAgICAgICAgICAgICAgKGl2MzIpKys7IFwN
Cj4gPiArfQ0KPiA+ICsNCj4gPiArLyogVHJhbnNtaXQgcmF0ZSBpbmZvcm1hdGlvbiBjb25zdGFu
dHMgKi8NCj4gPiArI2RlZmluZSBUWF9SQVRFX0ZPUk1BVF9MRUdBQ1kgICAgICAgICAwDQo+ID4g
KyNkZWZpbmUgVFhfUkFURV9GT1JNQVRfMTFOICAgICAgICAgICAgMQ0KPiA+ICsjZGVmaW5lIFRY
X1JBVEVfRk9STUFUXzExQUMgICAgICAgICAgIDINCj4gPiArDQo+ID4gKyNkZWZpbmUgVFhfUkFU
RV9CQU5EV0lEVEhfMjAgICAgICAgICAgMA0KPiA+ICsjZGVmaW5lIFRYX1JBVEVfQkFORFdJRFRI
XzQwICAgICAgICAgIDENCj4gPiArI2RlZmluZSBUWF9SQVRFX0JBTkRXSURUSF84MCAgICAgICAg
ICAyDQo+ID4gKw0KPiA+ICsjZGVmaW5lIFRYX1JBVEVfSU5GT19TVERfR0kgICAgICAgICAgIDAN
Cj4gPiArI2RlZmluZSBUWF9SQVRFX0lORk9fU0hPUlRfR0kgICAgICAgICAxDQo+ID4gKw0KPiA+
ICtlbnVtIHsNCj4gPiArICAgICAgIElFRUVfVFlQRV9NQU5BR0VNRU5UID0gMCwNCj4gPiArICAg
ICAgIElFRUVfVFlQRV9DT05UUk9MLA0KPiA+ICsgICAgICAgSUVFRV9UWVBFX0RBVEENCj4gPiAr
fTsNCj4gPiArDQo+ID4gK3N0cnVjdCBjY21wX2hkciB7DQo+ID4gKyAgICAgICBfX2xlMTYgaXYx
NjsNCj4gPiArICAgICAgIHU4IHJzdmQ7DQo+ID4gKyAgICAgICB1OCBrZXlfaWQ7DQo+ID4gKyAg
ICAgICBfX2xlMzIgaXYzMjsNCj4gPiArfSBfX3BhY2tlZDsNCj4gPiArDQo+ID4gK3N0YXRpYyBp
bnQgbXdsX3R4X3JpbmdfYWxsb2Moc3RydWN0IG13bF9wcml2ICpwcml2KSB7DQo+ID4gKyAgICAg
ICBzdHJ1Y3QgbXdsX2Rlc2NfZGF0YSAqZGVzYzsNCj4gPiArICAgICAgIGludCBudW07DQo+ID4g
KyAgICAgICB1OCAqbWVtOw0KPiA+ICsNCj4gPiArICAgICAgIGRlc2MgPSAmcHJpdi0+ZGVzY19k
YXRhWzBdOw0KPiA+ICsNCj4gPiArICAgICAgIG1lbSA9IGRtYV9hbGxvY19jb2hlcmVudCgmcHJp
di0+cGRldi0+ZGV2LA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1BWF9O
VU1fVFhfUklOR19CWVRFUyAqDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
U1lTQURQVF9OVU1fT0ZfREVTQ19EQVRBLA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICZkZXNjLT5wcGh5c190eF9yaW5nLA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgIEdGUF9LRVJORUwpOw0KPiA+ICsNCj4gPiArICAgICAgIGlmICghbWVtKSB7DQo+
ID4gKyAgICAgICAgICAgICAgIHdpcGh5X2Vycihwcml2LT5ody0+d2lwaHksICJjYW5ub3QgYWxs
b2MgbWVtXG4iKTsNCj4gPiArICAgICAgICAgICAgICAgcmV0dXJuIC1FTk9NRU07DQo+ID4gKyAg
ICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgZm9yIChudW0gPSAwOyBudW0gPCBTWVNBRFBUX05V
TV9PRl9ERVNDX0RBVEE7IG51bSsrKSB7DQo+ID4gKyAgICAgICAgICAgICAgIGRlc2MgPSAmcHJp
di0+ZGVzY19kYXRhW251bV07DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICBkZXNjLT5wdHhf
cmluZyA9IChzdHJ1Y3QgbXdsX3R4X2Rlc2MgKikNCj4gPiArICAgICAgICAgICAgICAgICAgICAg
ICAobWVtICsgbnVtICogTUFYX05VTV9UWF9SSU5HX0JZVEVTKTsNCj4gPiArDQo+ID4gKyAgICAg
ICAgICAgICAgIGRlc2MtPnBwaHlzX3R4X3JpbmcgPSAoZG1hX2FkZHJfdCkNCj4gPiArICAgICAg
ICAgICAgICAgICAgICAgICAoKHUzMilwcml2LT5kZXNjX2RhdGFbMF0ucHBoeXNfdHhfcmluZyAr
DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgbnVtICogTUFYX05VTV9UWF9SSU5HX0JZVEVT
KTsNCj4gPiArDQo+ID4gKyAgICAgICAgICAgICAgIG1lbXNldChkZXNjLT5wdHhfcmluZywgMHgw
MCwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgIE1BWF9OVU1fVFhfUklOR19CWVRFUyk7DQo+
ID4gKyAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgbWVtID0ga21hbGxvYyhNQVhfTlVNX1RY
X0hORExfQllURVMgKg0KPiBTWVNBRFBUX05VTV9PRl9ERVNDX0RBVEEsDQo+ID4gKyAgICAgICAg
ICAgICAgICAgICAgIEdGUF9LRVJORUwpOw0KPiA+ICsNCj4gPiArICAgICAgIGlmICghbWVtKSB7
DQo+ID4gKyAgICAgICAgICAgICAgIHdpcGh5X2Vycihwcml2LT5ody0+d2lwaHksICJjYW5ub3Qg
YWxsb2MgbWVtXG4iKTsNCj4gPiArICAgICAgICAgICAgICAgZG1hX2ZyZWVfY29oZXJlbnQoJnBy
aXYtPnBkZXYtPmRldiwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTUFY
X05VTV9UWF9SSU5HX0JZVEVTICoNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgU1lTQURQVF9OVU1fT0ZfREVTQ19EQVRBLA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICBwcml2LT5kZXNjX2RhdGFbMF0ucHR4X3JpbmcsDQo+ID4gKyAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgIHByaXYtPmRlc2NfZGF0YVswXS5wcGh5c190eF9yaW5nKTsN
Cj4gPiArICAgICAgICAgICAgICAgcmV0dXJuIC1FTk9NRU07DQo+ID4gKyAgICAgICB9DQo+ID4g
Kw0KPiA+ICsgICAgICAgZm9yIChudW0gPSAwOyBudW0gPCBTWVNBRFBUX05VTV9PRl9ERVNDX0RB
VEE7IG51bSsrKSB7DQo+ID4gKyAgICAgICAgICAgICAgIGRlc2MgPSAmcHJpdi0+ZGVzY19kYXRh
W251bV07DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICBkZXNjLT50eF9obmRsID0gKHN0cnVj
dCBtd2xfdHhfaG5kbCAqKQ0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIChtZW0gKyBudW0g
KiBNQVhfTlVNX1RYX0hORExfQllURVMpOw0KPiA+ICsNCj4gPiArICAgICAgICAgICAgICAgbWVt
c2V0KGRlc2MtPnR4X2huZGwsIDB4MDAsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICBNQVhf
TlVNX1RYX0hORExfQllURVMpOw0KPiA+ICsgICAgICAgfQ0KPiA+ICsNCj4gPiArICAgICAgIHJl
dHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IG13bF90eF9yaW5nX2luaXQo
c3RydWN0IG13bF9wcml2ICpwcml2KSB7DQo+ID4gKyAgICAgICBpbnQgbnVtLCBpOw0KPiA+ICsg
ICAgICAgc3RydWN0IG13bF9kZXNjX2RhdGEgKmRlc2M7DQo+ID4gKw0KPiA+ICsgICAgICAgZm9y
IChudW0gPSAwOyBudW0gPCBTWVNBRFBUX05VTV9PRl9ERVNDX0RBVEE7IG51bSsrKSB7DQo+ID4g
KyAgICAgICAgICAgICAgIHNrYl9xdWV1ZV9oZWFkX2luaXQoJnByaXYtPnR4cVtudW1dKTsNCj4g
PiArICAgICAgICAgICAgICAgcHJpdi0+ZndfZGVzY19jbnRbbnVtXSA9IDA7DQo+ID4gKw0KPiA+
ICsgICAgICAgICAgICAgICBkZXNjID0gJnByaXYtPmRlc2NfZGF0YVtudW1dOw0KPiA+ICsNCj4g
PiArICAgICAgICAgICAgICAgaWYgKGRlc2MtPnB0eF9yaW5nKSB7DQo+ID4gKyAgICAgICAgICAg
ICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IFNZU0FEUFRfTUFYX05VTV9UWF9ERVNDOw0KPiBp
KyspIHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2MtPnB0eF9yaW5n
W2ldLnN0YXR1cyA9DQo+ID4gKw0KPiBjcHVfdG9fbGUzMihFQUdMRV9UWERfU1RBVFVTX0lETEUp
Ow0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzYy0+cHR4X3JpbmdbaV0u
cHBoeXNfbmV4dCA9DQo+ID4gKw0KPiBjcHVfdG9fbGUzMigodTMyKWRlc2MtPnBwaHlzX3R4X3Jp
bmcgKw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoKGkgKyAx
KSAqIHNpemVvZihzdHJ1Y3QNCj4gbXdsX3R4X2Rlc2MpKSk7DQo+ID4gKyAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICBkZXNjLT50eF9obmRsW2ldLnBkZXNjID0NCj4gPiArICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJmRlc2MtPnB0eF9yaW5nW2ldOw0KPiA+ICsg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGkgPCBTWVNBRFBUX01BWF9OVU1fVFhf
REVTQw0KPiAtIDEpDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
IGRlc2MtPnR4X2huZGxbaV0ucG5leHQgPQ0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICZkZXNjLT50eF9obmRsW2kNCj4gKyAxXTsNCj4gPiArICAg
ICAgICAgICAgICAgICAgICAgICB9DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgZGVzYy0+
cHR4X3JpbmdbU1lTQURQVF9NQVhfTlVNX1RYX0RFU0MNCj4gLSAxXS5wcGh5c19uZXh0ID0NCj4g
PiArDQo+IGNwdV90b19sZTMyKCh1MzIpZGVzYy0+cHBoeXNfdHhfcmluZyk7DQo+ID4gKyAgICAg
ICAgICAgICAgICAgICAgICAgZGVzYy0+dHhfaG5kbFtTWVNBRFBUX01BWF9OVU1fVFhfREVTQyAt
DQo+IDFdLnBuZXh0ID0NCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICZkZXNj
LT50eF9obmRsWzBdOw0KPiA+ICsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBkZXNjLT5w
c3RhbGVfdHhfaG5kbCA9ICZkZXNjLT50eF9obmRsWzBdOw0KPiA+ICsgICAgICAgICAgICAgICAg
ICAgICAgIGRlc2MtPnBuZXh0X3R4X2huZGwgID0gJmRlc2MtPnR4X2huZGxbMF07DQo+ID4gKyAg
ICAgICAgICAgICAgIH0gZWxzZSB7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgd2lwaHlf
ZXJyKHByaXYtPmh3LT53aXBoeSwgIm5vIHZhbGlkIFRYDQo+IG1lbVxuIik7DQo+ID4gKyAgICAg
ICAgICAgICAgICAgICAgICAgcmV0dXJuIC1FTk9NRU07DQo+ID4gKyAgICAgICAgICAgICAgIH0N
Cj4gPiArICAgICAgIH0NCj4gPiArDQo+ID4gKyAgICAgICByZXR1cm4gMDsNCj4gPiArfQ0KPiA+
ICsNCj4gPiArc3RhdGljIHZvaWQgbXdsX3R4X3JpbmdfY2xlYW51cChzdHJ1Y3QgbXdsX3ByaXYg
KnByaXYpIHsNCj4gPiArICAgICAgIGludCBjbGVhbmVkX3R4X2Rlc2MgPSAwOw0KPiA+ICsgICAg
ICAgaW50IG51bSwgaTsNCj4gPiArICAgICAgIHN0cnVjdCBtd2xfZGVzY19kYXRhICpkZXNjOw0K
PiA+ICsNCj4gPiArICAgICAgIGZvciAobnVtID0gMDsgbnVtIDwgU1lTQURQVF9OVU1fT0ZfREVT
Q19EQVRBOyBudW0rKykgew0KPiA+ICsgICAgICAgICAgICAgICBza2JfcXVldWVfcHVyZ2UoJnBy
aXYtPnR4cVtudW1dKTsNCj4gPiArICAgICAgICAgICAgICAgcHJpdi0+ZndfZGVzY19jbnRbbnVt
XSA9IDA7DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICBkZXNjID0gJnByaXYtPmRlc2NfZGF0
YVtudW1dOw0KPiA+ICsNCj4gPiArICAgICAgICAgICAgICAgaWYgKGRlc2MtPnB0eF9yaW5nKSB7
DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgZm9yIChpID0gMDsgaSA8IFNZU0FEUFRfTUFY
X05VTV9UWF9ERVNDOw0KPiBpKyspIHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgIGlmICghZGVzYy0+dHhfaG5kbFtpXS5wc2tfYnVmZikNCj4gPiArICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWU7DQo+ID4gKw0KPiA+ICsgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgd2lwaHlfaW5mbyhwcml2LT5ody0+d2lwaHksDQo+ID4gKyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bm1hcHBlZCBhbmQgZnJl
ZSdkDQo+ICVpIDB4JXAgMHgleFxuIiwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgaSwNCj4gPiArDQo+IGRlc2MtPnR4X2huZGxbaV0ucHNrX2J1ZmYtPmRh
dGEsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlMzJf
dG9fY3B1KA0KPiA+ICsNCj4gZGVzYy0+cHR4X3JpbmdbaV0ucGt0X3B0cikpOw0KPiA+ICsgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgcGNpX3VubWFwX3NpbmdsZShwcml2LT5wZGV2LA0K
PiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZTMy
X3RvX2NwdSgNCj4gPiArDQo+IGRlc2MtPnB0eF9yaW5nW2ldLnBrdF9wdHIpLA0KPiA+ICsNCj4g
ZGVzYy0+dHhfaG5kbFtpXS5wc2tfYnVmZi0+bGVuLA0KPiA+ICsNCj4gUENJX0RNQV9UT0RFVklD
RSk7DQo+ID4gKw0KPiBkZXZfa2ZyZWVfc2tiX2FueShkZXNjLT50eF9obmRsW2ldLnBza19idWZm
KTsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2MtPnB0eF9yaW5nW2ld
LnN0YXR1cyA9DQo+ID4gKw0KPiBjcHVfdG9fbGUzMihFQUdMRV9UWERfU1RBVFVTX0lETEUpOw0K
PiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzYy0+cHR4X3JpbmdbaV0ucGt0
X3B0ciA9IDA7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjLT5wdHhf
cmluZ1tpXS5wa3RfbGVuID0gMDsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
IGRlc2MtPnR4X2huZGxbaV0ucHNrX2J1ZmYgPSBOVUxMOw0KPiA+ICsgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgY2xlYW5lZF90eF9kZXNjKys7DQo+ID4gKyAgICAgICAgICAgICAgICAg
ICAgICAgfQ0KPiA+ICsgICAgICAgICAgICAgICB9DQo+ID4gKyAgICAgICB9DQo+ID4gKw0KPiA+
ICsgICAgICAgd2lwaHlfaW5mbyhwcml2LT5ody0+d2lwaHksICJjbGVhbmVkICVpIFRYIGRlc2Ny
XG4iLA0KPiA+ICtjbGVhbmVkX3R4X2Rlc2MpOyB9DQo+ID4gKw0KPiA+ICtzdGF0aWMgdm9pZCBt
d2xfdHhfcmluZ19mcmVlKHN0cnVjdCBtd2xfcHJpdiAqcHJpdikgew0KPiA+ICsgICAgICAgaW50
IG51bTsNCj4gPiArDQo+ID4gKyAgICAgICBpZiAocHJpdi0+ZGVzY19kYXRhWzBdLnB0eF9yaW5n
KSB7DQo+ID4gKyAgICAgICAgICAgICAgIGRtYV9mcmVlX2NvaGVyZW50KCZwcml2LT5wZGV2LT5k
ZXYsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1BWF9OVU1fVFhfUklO
R19CWVRFUyAqDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNZU0FEUFRf
TlVNX09GX0RFU0NfREFUQSwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
cHJpdi0+ZGVzY19kYXRhWzBdLnB0eF9yaW5nLA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICBwcml2LT5kZXNjX2RhdGFbMF0ucHBoeXNfdHhfcmluZyk7DQo+ID4gKyAgICAg
ICB9DQo+ID4gKw0KPiA+ICsgICAgICAgZm9yIChudW0gPSAwOyBudW0gPCBTWVNBRFBUX05VTV9P
Rl9ERVNDX0RBVEE7IG51bSsrKSB7DQo+ID4gKyAgICAgICAgICAgICAgIGlmIChwcml2LT5kZXNj
X2RhdGFbbnVtXS5wdHhfcmluZykNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBwcml2LT5k
ZXNjX2RhdGFbbnVtXS5wdHhfcmluZyA9IE5VTEw7DQo+ID4gKyAgICAgICAgICAgICAgIHByaXYt
PmRlc2NfZGF0YVtudW1dLnBzdGFsZV90eF9obmRsID0gTlVMTDsNCj4gPiArICAgICAgICAgICAg
ICAgcHJpdi0+ZGVzY19kYXRhW251bV0ucG5leHRfdHhfaG5kbCA9IE5VTEw7DQo+ID4gKyAgICAg
ICB9DQo+ID4gKw0KPiA+ICsgICAgICAga2ZyZWUocHJpdi0+ZGVzY19kYXRhWzBdLnR4X2huZGwp
Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW5saW5lIHZvaWQgbXdsX3R4X2FkZF9kbWFf
aGVhZGVyKHN0cnVjdCBtd2xfcHJpdiAqcHJpdiwNCj4gPiArICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgIHN0cnVjdCBza19idWZmICpza2IsDQo+ID4gKyAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnQgaGVhZF9wYWQsDQo+ID4gKyAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbnQgdGFpbF9wYWQpIHsNCj4gPiArICAg
ICAgIHN0cnVjdCBpZWVlODAyMTFfaGRyICp3aDsNCj4gPiArICAgICAgIGludCBoZHJsZW47DQo+
ID4gKyAgICAgICBpbnQgcmVxZF9oZHJsZW47DQo+ID4gKyAgICAgICBzdHJ1Y3QgbXdsX2RtYV9k
YXRhICp0cjsNCj4gPiArDQo+ID4gKyAgICAgICAvKiBBZGQgYSBmaXJtd2FyZSBETUEgaGVhZGVy
OyB0aGUgZmlybXdhcmUgcmVxdWlyZXMgdGhhdCB3ZQ0KPiA+ICsgICAgICAgICogcHJlc2VudCBh
IDItYnl0ZSBwYXlsb2FkIGxlbmd0aCBmb2xsb3dlZCBieSBhIDQtYWRkcmVzcw0KPiA+ICsgICAg
ICAgICogaGVhZGVyICh3aXRob3V0IFFvUyBmaWVsZCksIGZvbGxvd2VkIChvcHRpb25hbGx5KSBi
eSBhbnkNCj4gPiArICAgICAgICAqIFdFUC9FeHRJViBoZWFkZXIgKGJ1dCBvbmx5IGZpbGxlZCBp
biBmb3IgQ0NNUCkuDQo+ID4gKyAgICAgICAgKi8NCj4gPiArICAgICAgIHdoID0gKHN0cnVjdCBp
ZWVlODAyMTFfaGRyICopc2tiLT5kYXRhOw0KPiA+ICsNCj4gPiArICAgICAgIGhkcmxlbiA9IGll
ZWU4MDIxMV9oZHJsZW4od2gtPmZyYW1lX2NvbnRyb2wpOw0KPiA+ICsNCj4gPiArICAgICAgIHJl
cWRfaGRybGVuID0gc2l6ZW9mKCp0cikgKyBoZWFkX3BhZDsNCj4gPiArDQo+ID4gKyAgICAgICBp
ZiAoaGRybGVuICE9IHJlcWRfaGRybGVuKQ0KPiA+ICsgICAgICAgICAgICAgICBza2JfcHVzaChz
a2IsIHJlcWRfaGRybGVuIC0gaGRybGVuKTsNCj4gPiArDQo+ID4gKyAgICAgICBpZiAoaWVlZTgw
MjExX2lzX2RhdGFfcW9zKHdoLT5mcmFtZV9jb250cm9sKSkNCj4gPiArICAgICAgICAgICAgICAg
aGRybGVuIC09IElFRUU4MDIxMV9RT1NfQ1RMX0xFTjsNCj4gPiArDQo+ID4gKyAgICAgICB0ciA9
IChzdHJ1Y3QgbXdsX2RtYV9kYXRhICopc2tiLT5kYXRhOw0KPiA+ICsNCj4gPiArICAgICAgIGlm
ICh3aCAhPSAmdHItPndoKQ0KPiA+ICsgICAgICAgICAgICAgICBtZW1tb3ZlKCZ0ci0+d2gsIHdo
LCBoZHJsZW4pOw0KPiA+ICsNCj4gPiArICAgICAgIGlmIChoZHJsZW4gIT0gc2l6ZW9mKHRyLT53
aCkpDQo+ID4gKyAgICAgICAgICAgICAgIG1lbXNldCgoKHZvaWQgKikmdHItPndoKSArIGhkcmxl
biwgMCwgc2l6ZW9mKHRyLT53aCkgLQ0KPiA+ICsgaGRybGVuKTsNCj4gPiArDQo+ID4gKyAgICAg
ICAvKiBGaXJtd2FyZSBsZW5ndGggaXMgdGhlIGxlbmd0aCBvZiB0aGUgZnVsbHkgZm9ybWVkICI4
MDIuMTENCj4gPiArICAgICAgICAqIHBheWxvYWQiLiAgVGhhdCBpcywgZXZlcnl0aGluZyBleGNl
cHQgZm9yIHRoZSA4MDIuMTEgaGVhZGVyLg0KPiA+ICsgICAgICAgICogVGhpcyBpbmNsdWRlcyBh
bGwgY3J5cHRvIG1hdGVyaWFsIGluY2x1ZGluZyB0aGUgTUlDLg0KPiA+ICsgICAgICAgICovDQo+
ID4gKyAgICAgICB0ci0+ZndsZW4gPSBjcHVfdG9fbGUxNihza2ItPmxlbiAtIHNpemVvZigqdHIp
ICsgdGFpbF9wYWQpOyB9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW5saW5lIHZvaWQgbXdsX3R4X2Vu
Y2Fwc3VsYXRlX2ZyYW1lKHN0cnVjdCBtd2xfcHJpdiAqcHJpdiwNCj4gPiArICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cnVjdCBza19idWZmICpza2IsDQo+ID4g
KyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJ1Y3QNCj4gaWVl
ZTgwMjExX2tleV9jb25mICprX2NvbmYsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICBib29sICpjY21wKSB7DQo+ID4gKyAgICAgICBpbnQgaGVhZF9wYWQg
PSAwOw0KPiA+ICsgICAgICAgaW50IGRhdGFfcGFkID0gMDsNCj4gPiArDQo+ID4gKyAgICAgICAv
KiBNYWtlIHN1cmUgdGhlIHBhY2tldCBoZWFkZXIgaXMgaW4gdGhlIERNQSBoZWFkZXIgZm9ybWF0
DQo+ICg0LWFkZHJlc3MNCj4gPiArICAgICAgICAqIHdpdGhvdXQgUW9TKSwgYW5kIGFkZCBoZWFk
ICYgdGFpbCBwYWRkaW5nIHdoZW4gSFcgY3J5cHRvIGlzDQo+IGVuYWJsZWQuDQo+ID4gKyAgICAg
ICAgKg0KPiA+ICsgICAgICAgICogV2UgaGF2ZSB0aGUgZm9sbG93aW5nIHRyYWlsZXIgcGFkZGlu
ZyByZXF1aXJlbWVudHM6DQo+ID4gKyAgICAgICAgKiAtIFdFUDogNCB0cmFpbGVyIGJ5dGVzIChJ
Q1YpDQo+ID4gKyAgICAgICAgKiAtIFRLSVA6IDEyIHRyYWlsZXIgYnl0ZXMgKDggTUlDICsgNCBJ
Q1YpDQo+ID4gKyAgICAgICAgKiAtIENDTVA6IDggdHJhaWxlciBieXRlcyAoTUlDKQ0KPiA+ICsg
ICAgICAgICovDQo+ID4gKw0KPiA+ICsgICAgICAgaWYgKGtfY29uZikgew0KPiA+ICsgICAgICAg
ICAgICAgICBoZWFkX3BhZCA9IGtfY29uZi0+aXZfbGVuOw0KPiA+ICsNCj4gPiArICAgICAgICAg
ICAgICAgc3dpdGNoIChrX2NvbmYtPmNpcGhlcikgew0KPiA+ICsgICAgICAgICAgICAgICBjYXNl
IFdMQU5fQ0lQSEVSX1NVSVRFX1dFUDQwOg0KPiA+ICsgICAgICAgICAgICAgICBjYXNlIFdMQU5f
Q0lQSEVSX1NVSVRFX1dFUDEwNDoNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBkYXRhX3Bh
ZCA9IDQ7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7DQo+ID4gKyAgICAgICAg
ICAgICAgIGNhc2UgV0xBTl9DSVBIRVJfU1VJVEVfVEtJUDoNCj4gPiArICAgICAgICAgICAgICAg
ICAgICAgICBkYXRhX3BhZCA9IDEyOw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIGJyZWFr
Ow0KPiA+ICsgICAgICAgICAgICAgICBjYXNlIFdMQU5fQ0lQSEVSX1NVSVRFX0NDTVA6DQo+ID4g
KyAgICAgICAgICAgICAgICAgICAgICAgZGF0YV9wYWQgPSA4Ow0KPiA+ICsgICAgICAgICAgICAg
ICAgICAgICAgICpjY21wID0gdHJ1ZTsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBicmVh
azsNCj4gPiArICAgICAgICAgICAgICAgfQ0KPiA+ICsgICAgICAgfQ0KPiA+ICsNCj4gPiArICAg
ICAgIG13bF90eF9hZGRfZG1hX2hlYWRlcihwcml2LCBza2IsIGhlYWRfcGFkLCBkYXRhX3BhZCk7
IH0NCj4gPiArDQo+ID4gK3N0YXRpYyBpbmxpbmUgdm9pZCBtd2xfdHhfaW5zZXJ0X2NjbXBfaGRy
KHU4ICpwY2NtcF9oZHIsDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgdTgga2V5X2lkLCB1MTYgaXYxNiwgdTMyDQo+ID4gK2l2MzIpIHsNCj4gPiArICAgICAg
IHN0cnVjdCBjY21wX2hkciAqY2NtcF9oID0gKHN0cnVjdCBjY21wX2hkciAqKXBjY21wX2hkcjsN
Cj4gPiArDQo+ID4gKyAgICAgICBjY21wX2gtPml2MTYgPSBjcHVfdG9fbGUxNihpdjE2KTsNCj4g
PiArICAgICAgIGNjbXBfaC0+cnN2ZCA9IDA7DQo+ID4gKyAgICAgICBjY21wX2gtPmtleV9pZCA9
IEVYVF9JViB8IChrZXlfaWQgPDwgNik7DQo+ID4gKyAgICAgICBjY21wX2gtPml2MzIgPSBjcHVf
dG9fbGUzMihpdjMyKTsgfQ0KPiA+ICsNCj4gPiArc3RhdGljIGlubGluZSBpbnQgbXdsX3R4X3Rp
ZF9xdWV1ZV9tYXBwaW5nKHU4IHRpZCkgew0KPiA+ICsgICAgICAgc3dpdGNoICh0aWQpIHsNCj4g
PiArICAgICAgIGNhc2UgMDoNCj4gPiArICAgICAgIGNhc2UgMzoNCj4gPiArICAgICAgICAgICAg
ICAgcmV0dXJuIElFRUU4MDIxMV9BQ19CRTsNCj4gPiArICAgICAgIGNhc2UgMToNCj4gPiArICAg
ICAgIGNhc2UgMjoNCj4gPiArICAgICAgICAgICAgICAgcmV0dXJuIElFRUU4MDIxMV9BQ19CSzsN
Cj4gPiArICAgICAgIGNhc2UgNDoNCj4gPiArICAgICAgIGNhc2UgNToNCj4gPiArICAgICAgICAg
ICAgICAgcmV0dXJuIElFRUU4MDIxMV9BQ19WSTsNCj4gPiArICAgICAgIGNhc2UgNjoNCj4gPiAr
ICAgICAgIGNhc2UgNzoNCj4gPiArICAgICAgICAgICAgICAgcmV0dXJuIElFRUU4MDIxMV9BQ19W
TzsNCj4gPiArICAgICAgIGRlZmF1bHQ6DQo+ID4gKyAgICAgICAgICAgICAgIGJyZWFrOw0KPiA+
ICsgICAgICAgfQ0KPiA+ICsNCj4gPiArICAgICAgIHJldHVybiAtMTsNCj4gPiArfQ0KPiA+ICsN
Cj4gPiArc3RhdGljIGlubGluZSB2b2lkIG13bF90eF9jb3VudF9wYWNrZXQoc3RydWN0IGllZWU4
MDIxMV9zdGEgKnN0YSwgdTgNCj4gPiArdGlkKSB7DQo+ID4gKyAgICAgICBzdHJ1Y3QgbXdsX3N0
YSAqc3RhX2luZm87DQo+ID4gKyAgICAgICBzdHJ1Y3QgbXdsX3R4X2luZm8gKnR4X3N0YXRzOw0K
PiA+ICsNCj4gPiArICAgICAgIGlmIChXQVJOX09OKHRpZCA+PSBTWVNBRFBUX01BWF9USUQpKQ0K
PiA+ICsgICAgICAgICAgICAgICByZXR1cm47DQo+ID4gKw0KPiA+ICsgICAgICAgc3RhX2luZm8g
PSBtd2xfZGV2X2dldF9zdGEoc3RhKTsNCj4gPiArDQo+ID4gKyAgICAgICB0eF9zdGF0cyA9ICZz
dGFfaW5mby0+dHhfc3RhdHNbdGlkXTsNCj4gPiArDQo+ID4gKyAgICAgICBpZiAodHhfc3RhdHMt
PnN0YXJ0X3RpbWUgPT0gMCkNCj4gPiArICAgICAgICAgICAgICAgdHhfc3RhdHMtPnN0YXJ0X3Rp
bWUgPSBqaWZmaWVzOw0KPiA+ICsNCj4gPiArICAgICAgIC8qIHJlc2V0IHRoZSBwYWNrZXQgY291
bnQgYWZ0ZXIgZWFjaCBzZWNvbmQgZWxhcHNlcy4gIElmIHRoZQ0KPiBudW1iZXIgb2YNCj4gPiAr
ICAgICAgICAqIHBhY2tldHMgZXZlciBleGNlZWRzIHRoZSBhbXBkdV9taW5fdHJhZmZpYyB0aHJl
c2hvbGQsIHdlIHdpbGwNCj4gYWxsb3cNCj4gPiArICAgICAgICAqIGFuIGFtcGR1IHN0cmVhbSB0
byBiZSBzdGFydGVkLg0KPiA+ICsgICAgICAgICovDQo+ID4gKyAgICAgICBpZiAoamlmZmllcyAt
IHR4X3N0YXRzLT5zdGFydF90aW1lID4gSFopIHsNCj4gPiArICAgICAgICAgICAgICAgdHhfc3Rh
dHMtPnBrdHMgPSAwOw0KPiA+ICsgICAgICAgICAgICAgICB0eF9zdGF0cy0+c3RhcnRfdGltZSA9
IDA7DQo+ID4gKyAgICAgICB9IGVsc2Ugew0KPiA+ICsgICAgICAgICAgICAgICB0eF9zdGF0cy0+
cGt0cysrOw0KPiA+ICsgICAgICAgfQ0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW5saW5l
IGJvb2wgbXdsX3R4X2F2YWlsYWJsZShzdHJ1Y3QgbXdsX3ByaXYgKnByaXYsIGludA0KPiA+ICtk
ZXNjX251bSkgew0KPiA+ICsgICAgICAgc3RydWN0IG13bF90eF9obmRsICp0eF9obmRsOw0KPiA+
ICsNCj4gPiArICAgICAgIHR4X2huZGwgPSBwcml2LT5kZXNjX2RhdGFbZGVzY19udW1dLnBuZXh0
X3R4X2huZGw7DQo+ID4gKw0KPiA+ICsgICAgICAgaWYgKCF0eF9obmRsLT5wZGVzYykNCj4gPiAr
ICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlOw0KPiA+ICsNCj4gPiArICAgICAgIGlmICh0eF9o
bmRsLT5wZGVzYy0+c3RhdHVzICE9IEVBR0xFX1RYRF9TVEFUVVNfSURMRSkgew0KPiA+ICsgICAg
ICAgICAgICAgICAvKiBJbnRlcnJ1cHQgRi9XIGFueXdheSAqLw0KPiA+ICsgICAgICAgICAgICAg
ICBpZiAodHhfaG5kbC0+cGRlc2MtPnN0YXR1cyAmDQo+ID4gKyAgICAgICAgICAgICAgICAgICBj
cHVfdG9fbGUzMihFQUdMRV9UWERfU1RBVFVTX0ZXX09XTkVEKSkNCj4gPiArICAgICAgICAgICAg
ICAgICAgICAgICB3cml0ZWwoTUFDUkVHX0gyQVJJQ19CSVRfUFBBX1JFQURZLA0KPiA+ICsgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICBwcml2LT5pb2Jhc2UxICsNCj4gPiArDQo+IE1BQ1JF
R19SRUdfSDJBX0lOVEVSUlVQVF9FVkVOVFMpOw0KPiA+ICsgICAgICAgICAgICAgICByZXR1cm4g
ZmFsc2U7DQo+ID4gKyAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgcmV0dXJuIHRydWU7DQo+
ID4gK30NCj4gPiArDQo+ID4gK3N0YXRpYyBpbmxpbmUgdm9pZCBtd2xfdHhfc2tiKHN0cnVjdCBt
d2xfcHJpdiAqcHJpdiwgaW50IGRlc2NfbnVtLA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgIHN0cnVjdCBza19idWZmICp0eF9za2IpIHsNCj4gPiArICAgICAgIHN0cnVjdCBpZWVl
ODAyMTFfdHhfaW5mbyAqdHhfaW5mbzsNCj4gPiArICAgICAgIHN0cnVjdCBtd2xfdHhfY3RybCAq
dHhfY3RybDsNCj4gPiArICAgICAgIHN0cnVjdCBtd2xfdHhfaG5kbCAqdHhfaG5kbDsNCj4gPiAr
ICAgICAgIHN0cnVjdCBtd2xfdHhfZGVzYyAqdHhfZGVzYzsNCj4gPiArICAgICAgIHN0cnVjdCBp
ZWVlODAyMTFfc3RhICpzdGE7DQo+ID4gKyAgICAgICBzdHJ1Y3QgaWVlZTgwMjExX3ZpZiAqdmlm
Ow0KPiA+ICsgICAgICAgc3RydWN0IG13bF92aWYgKm13bF92aWY7DQo+ID4gKyAgICAgICBzdHJ1
Y3QgaWVlZTgwMjExX2tleV9jb25mICprX2NvbmY7DQo+ID4gKyAgICAgICBib29sIGNjbXAgPSBm
YWxzZTsNCj4gPiArICAgICAgIHN0cnVjdCBtd2xfZG1hX2RhdGEgKmRtYV9kYXRhOw0KPiA+ICsg
ICAgICAgc3RydWN0IGllZWU4MDIxMV9oZHIgKndoOw0KPiA+ICsgICAgICAgZG1hX2FkZHJfdCBk
bWE7DQo+ID4gKw0KPiA+ICsgICAgICAgaWYgKFdBUk5fT04oIXR4X3NrYikpDQo+ID4gKyAgICAg
ICAgICAgICAgIHJldHVybjsNCj4gPiArDQo+ID4gKyAgICAgICB0eF9pbmZvID0gSUVFRTgwMjEx
X1NLQl9DQih0eF9za2IpOw0KPiA+ICsgICAgICAgdHhfY3RybCA9IChzdHJ1Y3QgbXdsX3R4X2N0
cmwgKikmdHhfaW5mby0+c3RhdHVzOw0KPiA+ICsgICAgICAgc3RhID0gKHN0cnVjdCBpZWVlODAy
MTFfc3RhICopdHhfY3RybC0+c3RhOw0KPiA+ICsgICAgICAgdmlmID0gKHN0cnVjdCBpZWVlODAy
MTFfdmlmICopdHhfY3RybC0+dmlmOw0KPiA+ICsgICAgICAgbXdsX3ZpZiA9IG13bF9kZXZfZ2V0
X3ZpZih2aWYpOw0KPiA+ICsgICAgICAga19jb25mID0gKHN0cnVjdCBpZWVlODAyMTFfa2V5X2Nv
bmYgKil0eF9jdHJsLT5rX2NvbmY7DQo+ID4gKw0KPiA+ICsgICAgICAgbXdsX3R4X2VuY2Fwc3Vs
YXRlX2ZyYW1lKHByaXYsIHR4X3NrYiwga19jb25mLCAmY2NtcCk7DQo+ID4gKw0KPiA+ICsgICAg
ICAgZG1hX2RhdGEgPSAoc3RydWN0IG13bF9kbWFfZGF0YSAqKXR4X3NrYi0+ZGF0YTsNCj4gPiAr
ICAgICAgIHdoID0gJmRtYV9kYXRhLT53aDsNCj4gPiArDQo+ID4gKyAgICAgICBpZiAoaWVlZTgw
MjExX2lzX2RhdGEod2gtPmZyYW1lX2NvbnRyb2wpKSB7DQo+ID4gKyAgICAgICAgICAgICAgIGlm
IChpc19tdWx0aWNhc3RfZXRoZXJfYWRkcih3aC0+YWRkcjEpKSB7DQo+ID4gKyAgICAgICAgICAg
ICAgICAgICAgICAgaWYgKGNjbXApIHsNCj4gPiArDQo+IG13bF90eF9pbnNlcnRfY2NtcF9oZHIo
ZG1hX2RhdGEtPmRhdGEsDQo+ID4gKw0KPiBtd2xfdmlmLT5rZXlpZHgsDQo+ID4gKw0KPiBtd2xf
dmlmLT5pdjE2LA0KPiA+ICsNCj4gbXdsX3ZpZi0+aXYzMik7DQo+ID4gKyAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICBJTkNSRUFTRV9JVihtd2xfdmlmLT5pdjE2LA0KPiBtd2xfdmlmLT5p
djMyKTsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICB9DQo+ID4gKyAgICAgICAgICAgICAg
IH0gZWxzZSB7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgaWYgKGNjbXApIHsNCj4gPiAr
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICh2aWYtPnR5cGUgPT0NCj4gTkw4MDIx
MV9JRlRZUEVfU1RBVElPTikgew0KPiA+ICsNCj4gbXdsX3R4X2luc2VydF9jY21wX2hkcihkbWFf
ZGF0YS0+ZGF0YSwNCj4gPiArDQo+IG13bF92aWYtPmtleWlkeCwNCj4gPiArDQo+IG13bF92aWYt
Pml2MTYsDQo+ID4gKw0KPiBtd2xfdmlmLT5pdjMyKTsNCj4gPiArDQo+IElOQ1JFQVNFX0lWKG13
bF92aWYtPml2MTYsDQo+ID4gKw0KPiBtd2xfdmlmLT5pdjMyKTsNCj4gPiArICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgIHN0cnVjdCBtd2xfc3RhICpzdGFfaW5mbzsNCj4gPiArDQo+ID4gKyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YV9pbmZvID0NCj4gPiArIG13
bF9kZXZfZ2V0X3N0YShzdGEpOw0KPiA+ICsNCj4gPiArDQo+IG13bF90eF9pbnNlcnRfY2NtcF9o
ZHIoZG1hX2RhdGEtPmRhdGEsDQo+ID4gKw0KPiAwLA0KPiA+ICsNCj4gc3RhX2luZm8tPml2MTYs
DQo+ID4gKw0KPiBzdGFfaW5mby0+aXYzMik7DQo+ID4gKw0KPiBJTkNSRUFTRV9JVihzdGFfaW5m
by0+aXYxNiwNCj4gPiArDQo+IHN0YV9pbmZvLT5pdjMyKTsNCj4gPiArICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgIH0NCj4gPiArICAgICAgICAgICAgICAgICAgICAgICB9DQo+ID4gKyAg
ICAgICAgICAgICAgIH0NCj4gPiArICAgICAgIH0NCj4gPiArDQo+ID4gKyAgICAgICB0eF9obmRs
ID0gcHJpdi0+ZGVzY19kYXRhW2Rlc2NfbnVtXS5wbmV4dF90eF9obmRsOw0KPiA+ICsgICAgICAg
dHhfaG5kbC0+cHNrX2J1ZmYgPSB0eF9za2I7DQo+ID4gKyAgICAgICB0eF9kZXNjID0gdHhfaG5k
bC0+cGRlc2M7DQo+ID4gKyAgICAgICB0eF9kZXNjLT50eF9wcmlvcml0eSA9IHR4X2N0cmwtPnR4
X3ByaW9yaXR5Ow0KPiA+ICsgICAgICAgdHhfZGVzYy0+cW9zX2N0cmwgPSBjcHVfdG9fbGUxNih0
eF9jdHJsLT5xb3NfY3RybCk7DQo+ID4gKyAgICAgICB0eF9kZXNjLT5wa3RfbGVuID0gY3B1X3Rv
X2xlMTYodHhfc2tiLT5sZW4pOw0KPiA+ICsgICAgICAgdHhfZGVzYy0+cGFja2V0X2luZm8gPSAw
Ow0KPiA+ICsgICAgICAgdHhfZGVzYy0+ZGF0YV9yYXRlID0gMDsNCj4gPiArICAgICAgIHR4X2Rl
c2MtPnR5cGUgPSB0eF9jdHJsLT50eXBlOw0KPiA+ICsgICAgICAgdHhfZGVzYy0+eG1pdF9jb250
cm9sID0gdHhfY3RybC0+eG1pdF9jb250cm9sOw0KPiA+ICsgICAgICAgdHhfZGVzYy0+c2FwX3Br
dF9pbmZvID0gMDsNCj4gPiArICAgICAgIGRtYSA9IHBjaV9tYXBfc2luZ2xlKHByaXYtPnBkZXYs
IHR4X3NrYi0+ZGF0YSwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR4X3NrYi0+
bGVuLCBQQ0lfRE1BX1RPREVWSUNFKTsNCj4gPiArICAgICAgIGlmIChwY2lfZG1hX21hcHBpbmdf
ZXJyb3IocHJpdi0+cGRldiwgZG1hKSkgew0KPiA+ICsgICAgICAgICAgICAgICBkZXZfa2ZyZWVf
c2tiX2FueSh0eF9za2IpOw0KPiA+ICsgICAgICAgICAgICAgICB3aXBoeV9lcnIocHJpdi0+aHct
PndpcGh5LA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgImZhaWxlZCB0byBtYXAgcGNp
IG1lbW9yeSFcbiIpOw0KPiA+ICsgICAgICAgICAgICAgICByZXR1cm47DQo+ID4gKyAgICAgICB9
DQo+ID4gKyAgICAgICB0eF9kZXNjLT5wa3RfcHRyID0gY3B1X3RvX2xlMzIoZG1hKTsNCj4gPiAr
ICAgICAgIHR4X2Rlc2MtPnN0YXR1cyA9IGNwdV90b19sZTMyKEVBR0xFX1RYRF9TVEFUVVNfRldf
T1dORUQpOw0KPiA+ICsgICAgICAgLyogbWFrZSBzdXJlIGFsbCB0aGUgbWVtb3J5IHRyYW5zYWN0
aW9ucyBkb25lIGJ5IGNwdSB3ZXJlDQo+IGNvbXBsZXRlZCAqLw0KPiA+ICsgICAgICAgd21iKCk7
ICAvKkRhdGEgTWVtb3J5IEJhcnJpZXIqLw0KPiA+ICsgICAgICAgd3JpdGVsKE1BQ1JFR19IMkFS
SUNfQklUX1BQQV9SRUFEWSwNCj4gPiArICAgICAgICAgICAgICBwcml2LT5pb2Jhc2UxICsNCj4g
TUFDUkVHX1JFR19IMkFfSU5URVJSVVBUX0VWRU5UUyk7DQo+ID4gKyAgICAgICBwcml2LT5kZXNj
X2RhdGFbZGVzY19udW1dLnBuZXh0X3R4X2huZGwgPSB0eF9obmRsLT5wbmV4dDsNCj4gPiArICAg
ICAgIHByaXYtPmZ3X2Rlc2NfY250W2Rlc2NfbnVtXSsrOyB9DQo+ID4gKw0KPiA+ICtzdGF0aWMg
aW5saW5lIHN0cnVjdCBza19idWZmICptd2xfdHhfZG9fYW1zZHUoc3RydWN0IG13bF9wcml2ICpw
cml2LA0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBp
bnQgZGVzY19udW0sDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgIHN0cnVjdCBza19idWZmDQo+ICp0eF9za2IsDQo+ID4gKyAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cnVjdA0KPiBpZWVlODAyMTFfdHhfaW5mbw0K
PiA+ICsqdHhfaW5mbykgew0KPiA+ICsgICAgICAgc3RydWN0IGllZWU4MDIxMV9zdGEgKnN0YTsN
Cj4gPiArICAgICAgIHN0cnVjdCBtd2xfc3RhICpzdGFfaW5mbzsNCj4gPiArICAgICAgIHN0cnVj
dCBtd2xfdHhfY3RybCAqdHhfY3RybCA9IChzdHJ1Y3QgbXdsX3R4X2N0cmwNCj4gKikmdHhfaW5m
by0+c3RhdHVzOw0KPiA+ICsgICAgICAgc3RydWN0IGllZWU4MDIxMV90eF9pbmZvICphbXNkdV9p
bmZvOw0KPiA+ICsgICAgICAgc3RydWN0IHNrX2J1ZmZfaGVhZCAqYW1zZHVfcGt0czsNCj4gPiAr
ICAgICAgIHN0cnVjdCBtd2xfYW1zZHVfZnJhZyAqYW1zZHU7DQo+ID4gKyAgICAgICBpbnQgYW1z
ZHVfYWxsb3dfc2l6ZTsNCj4gPiArICAgICAgIHN0cnVjdCBpZWVlODAyMTFfaGRyICp3aDsNCj4g
PiArICAgICAgIGludCB3aF9sZW47DQo+ID4gKyAgICAgICB1MTYgbGVuOw0KPiA+ICsgICAgICAg
dTggKmRhdGE7DQo+ID4gKw0KPiA+ICsgICAgICAgc3RhID0gKHN0cnVjdCBpZWVlODAyMTFfc3Rh
ICopdHhfY3RybC0+c3RhOw0KPiA+ICsgICAgICAgc3RhX2luZm8gPSBtd2xfZGV2X2dldF9zdGEo
c3RhKTsNCj4gPiArDQo+ID4gKyAgICAgICBpZiAoIXN0YV9pbmZvLT5pc19hbXNkdV9hbGxvd2Vk
KQ0KPiA+ICsgICAgICAgICAgICAgICByZXR1cm4gdHhfc2tiOw0KPiA+ICsNCj4gPiArICAgICAg
IHdoID0gKHN0cnVjdCBpZWVlODAyMTFfaGRyICopdHhfc2tiLT5kYXRhOw0KPiA+ICsgICAgICAg
aWYgKHN0YV9pbmZvLT5pc19tZXNoX25vZGUgJiYNCj4gaXNfbXVsdGljYXN0X2V0aGVyX2FkZHIo
d2gtPmFkZHIzKSkNCj4gPiArICAgICAgICAgICAgICAgcmV0dXJuIHR4X3NrYjsNCj4gPiArDQo+
ID4gKyAgICAgICBpZiAoc3RhX2luZm8tPmFtc2R1X2N0cmwuY2FwID09IE1XTF9BTVNEVV9TSVpF
XzRLKQ0KPiA+ICsgICAgICAgICAgICAgICBhbXNkdV9hbGxvd19zaXplID0gU1lTQURQVF9BTVNE
VV80S19NQVhfU0laRTsNCj4gPiArICAgICAgIGVsc2UgaWYgKHN0YV9pbmZvLT5hbXNkdV9jdHJs
LmNhcCA9PSBNV0xfQU1TRFVfU0laRV84SykNCj4gPiArICAgICAgICAgICAgICAgYW1zZHVfYWxs
b3dfc2l6ZSA9IFNZU0FEUFRfQU1TRFVfOEtfTUFYX1NJWkU7DQo+ID4gKyAgICAgICBlbHNlDQo+
ID4gKyAgICAgICAgICAgICAgIHJldHVybiB0eF9za2I7DQo+ID4gKw0KPiA+ICsgICAgICAgc3Bp
bl9sb2NrX2JoKCZzdGFfaW5mby0+YW1zZHVfbG9jayk7DQo+ID4gKyAgICAgICBhbXNkdSA9ICZz
dGFfaW5mby0+YW1zZHVfY3RybC5mcmFnW2Rlc2NfbnVtXTsNCj4gPiArDQo+ID4gKyAgICAgICBp
ZiAodHhfc2tiLT5sZW4gPiBTWVNBRFBUX0FNU0RVX0FMTE9XX1NJWkUpIHsNCj4gPiArICAgICAg
ICAgICAgICAgaWYgKGFtc2R1LT5udW0pIHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBt
d2xfdHhfc2tiKHByaXYsIGRlc2NfbnVtLCBhbXNkdS0+c2tiKTsNCj4gPiArICAgICAgICAgICAg
ICAgICAgICAgICBhbXNkdS0+bnVtID0gMDsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBh
bXNkdS0+Y3VyX3BvcyA9IE5VTEw7DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAg
IGlmICghbXdsX3R4X2F2YWlsYWJsZShwcml2LCBkZXNjX251bSkpIHsNCj4gPiArDQo+IHNrYl9x
dWV1ZV9oZWFkKCZwcml2LT50eHFbZGVzY19udW1dLCB0eF9za2IpOw0KPiA+ICsNCj4gc3Bpbl91
bmxvY2tfYmgoJnN0YV9pbmZvLT5hbXNkdV9sb2NrKTsNCj4gPiArICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgIHJldHVybiBOVUxMOw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIH0N
Cj4gPiArICAgICAgICAgICAgICAgfQ0KPiA+ICsgICAgICAgICAgICAgICBzcGluX3VubG9ja19i
aCgmc3RhX2luZm8tPmFtc2R1X2xvY2spOw0KPiA+ICsgICAgICAgICAgICAgICByZXR1cm4gdHhf
c2tiOw0KPiA+ICsgICAgICAgfQ0KPiA+ICsNCj4gPiArICAgICAgIC8qIHBvdGVudGlhbCBhbXNk
dSBzaXplLCBzaG91bGQgYWRkIGFtc2R1IGhlYWRlciAxNCBieXRlcyArDQo+ID4gKyAgICAgICAg
KiBtYXhpbXVtIHBhZGRpbmcgMy4NCj4gPiArICAgICAgICAqLw0KPiA+ICsgICAgICAgd2hfbGVu
ID0gaWVlZTgwMjExX2hkcmxlbih3aC0+ZnJhbWVfY29udHJvbCk7DQo+ID4gKyAgICAgICBsZW4g
PSB0eF9za2ItPmxlbiAtIHdoX2xlbiArIDE3Ow0KPiA+ICsNCj4gPiArICAgICAgIGlmIChhbXNk
dS0+bnVtKSB7DQo+ID4gKyAgICAgICAgICAgICAgIGlmICgoYW1zZHUtPnNrYi0+bGVuICsgbGVu
KSA+IGFtc2R1X2FsbG93X3NpemUpIHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBtd2xf
dHhfc2tiKHByaXYsIGRlc2NfbnVtLCBhbXNkdS0+c2tiKTsNCj4gPiArICAgICAgICAgICAgICAg
ICAgICAgICBhbXNkdS0+bnVtID0gMDsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBhbXNk
dS0+Y3VyX3BvcyA9IE5VTEw7DQo+ID4gKyAgICAgICAgICAgICAgIH0NCj4gPiArICAgICAgIH0N
Cj4gPiArDQo+ID4gKyAgICAgICBhbXNkdS0+amlmZmllcyA9IGppZmZpZXM7DQo+ID4gKyAgICAg
ICBsZW4gPSB0eF9za2ItPmxlbiAtIHdoX2xlbjsNCj4gPiArDQo+ID4gKyAgICAgICBpZiAoYW1z
ZHUtPm51bSA9PSAwKSB7DQo+ID4gKyAgICAgICAgICAgICAgIHN0cnVjdCBza19idWZmICpuZXdz
a2I7DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICBhbXNkdV9wa3RzID0gKHN0cnVjdCBza19i
dWZmX2hlYWQgKikNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBrbWFsbG9jKHNpemVvZigq
YW1zZHVfcGt0cyksIEdGUF9LRVJORUwpOw0KPiA+ICsgICAgICAgICAgICAgICBpZiAoIWFtc2R1
X3BrdHMpIHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBzcGluX3VubG9ja19iaCgmc3Rh
X2luZm8tPmFtc2R1X2xvY2spOw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0
eF9za2I7DQo+ID4gKyAgICAgICAgICAgICAgIH0NCj4gPiArICAgICAgICAgICAgICAgbmV3c2ti
ID0gZGV2X2FsbG9jX3NrYihhbXNkdV9hbGxvd19zaXplICsNCj4gPiArDQo+IFNZU0FEUFRfTUlO
X0JZVEVTX0hFQURST09NKTsNCj4gPiArICAgICAgICAgICAgICAgaWYgKCFuZXdza2IpIHsNCj4g
PiArICAgICAgICAgICAgICAgICAgICAgICBzcGluX3VubG9ja19iaCgmc3RhX2luZm8tPmFtc2R1
X2xvY2spOw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIGtmcmVlKGFtc2R1X3BrdHMpOw0K
PiA+ICsgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0eF9za2I7DQo+ID4gKyAgICAgICAg
ICAgICAgIH0NCj4gPiArDQo+ID4gKyAgICAgICAgICAgICAgIGRhdGEgPSBuZXdza2ItPmRhdGE7
DQo+ID4gKyAgICAgICAgICAgICAgIG1lbWNweShkYXRhLCB0eF9za2ItPmRhdGEsIHdoX2xlbik7
DQo+ID4gKyAgICAgICAgICAgICAgIGlmIChzdGFfaW5mby0+aXNfbWVzaF9ub2RlKSB7DQo+ID4g
KyAgICAgICAgICAgICAgICAgICAgICAgZXRoZXJfYWRkcl9jb3B5KGRhdGEgKyB3aF9sZW4sIHdo
LT5hZGRyMyk7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgZXRoZXJfYWRkcl9jb3B5KGRh
dGEgKyB3aF9sZW4gKyBFVEhfQUxFTiwNCj4gd2gtPmFkZHI0KTsNCj4gPiArICAgICAgICAgICAg
ICAgfSBlbHNlIHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBldGhlcl9hZGRyX2NvcHko
ZGF0YSArIHdoX2xlbiwNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgaWVlZTgwMjExX2dldF9EQSh3aCkpOw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIGV0
aGVyX2FkZHJfY29weShkYXRhICsgd2hfbGVuICsgRVRIX0FMRU4sDQo+ID4gKyAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgIGllZWU4MDIxMV9nZXRfU0Eod2gpKTsNCj4gPiAr
ICAgICAgICAgICAgICAgfQ0KPiA+ICsgICAgICAgICAgICAgICAqKHU4ICopKGRhdGEgKyB3aF9s
ZW4gKyBFVEhfSExFTiAtIDEpID0gbGVuICYgMHhmZjsNCj4gPiArICAgICAgICAgICAgICAgKih1
OCAqKShkYXRhICsgd2hfbGVuICsgRVRIX0hMRU4gLSAyKSA9IChsZW4gPj4gOCkgJg0KPiA+ICsg
MHhmZjsNCj4gDQo+IFRoZXNlIGJpdCBvZXByYXRpb25zIGFyZSBub3QgbmVlZGVkLiBUaGUgcG9p
bnRlciBtdXN0IGJlIFdPUkQgYWxpZ25lZCwNCj4gcmlnaHQ/DQo+IFRoaXMgaXMgd2h5IHlvdSBo
YXZlIHBhZGRpbmcgYWZ0ZXIgYWxsLCB1bmxlc3MgeW91IGhhdmUgc29tZSB3ZWlyZCBETUENCj4g
Z2FtZXM/DQo+IFlvdSBzZWVtIHRvIGJlIGFzc3VtaW5nIHRoaXMgYWxpZ25tZW50IGFueXdheSBi
ZWNhdXNlIHlvdSB1c2UNCj4gZXRoZXJfYWRkcl9jb3B5IGFib3ZlLg0KPiANCj4gPiArDQo+ID4g
KyAgICAgICAvKiBRdWV1ZSBBRERCQSByZXF1ZXN0IGluIHRoZSByZXNwZWN0aXZlIGRhdGEgcXVl
dWUuICBXaGlsZQ0KPiBzZXR0aW5nIHVwDQo+ID4gKyAgICAgICAgKiB0aGUgYW1wZHUgc3RyZWFt
LCBtYWM4MDIxMSBxdWV1ZXMgZnVydGhlciBwYWNrZXRzIGZvciB0aGF0DQo+ID4gKyAgICAgICAg
KiBwYXJ0aWN1bGFyIHJhL3RpZCBwYWlyLiAgSG93ZXZlciwgcGFja2V0cyBwaWxlZCB1cCBpbiB0
aGUNCj4gaGFyZHdhcmUNCj4gPiArICAgICAgICAqIGZvciB0aGF0IHJhL3RpZCBwYWlyIHdpbGwg
c3RpbGwgZ28gb3V0LiBBRERCQSByZXF1ZXN0IGFuZCB0aGUNCj4gPiArICAgICAgICAqIHJlbGF0
ZWQgZGF0YSBwYWNrZXRzIGdvaW5nIG91dCBmcm9tIGRpZmZlcmVudCBxdWV1ZXMNCj4gYXN5bmNo
cm9ub3VzbHkNCj4gPiArICAgICAgICAqIHdpbGwgY2F1c2UgYSBzaGlmdCBpbiB0aGUgcmVjZWl2
ZXIgd2luZG93IHdoaWNoIG1pZ2h0IHJlc3VsdCBpbg0KPiA+ICsgICAgICAgICogYW1wZHUgcGFj
a2V0cyBnZXR0aW5nIGRyb3BwZWQgYXQgdGhlIHJlY2VpdmVyIGFmdGVyIHRoZSBzdHJlYW0NCj4g
aGFzDQo+ID4gKyAgICAgICAgKiBiZWVuIHNldHVwLg0KPiA+ICsgICAgICAgICovDQo+IA0KPiBI
ZXJlIGlzIHRoZSByYWNlIHlvdSBhcmUgZmFjaW5nIGFuZCB0aGF0IHJhY2UgY2FuIGJlIGF2b2lk
ZWQgYnkgdGhlIHByb3Blcg0KPiB1c2FnZSBvZiB0aGUgX2NiDQo+IA0KPiA+ICsgICAgICAgaWYg
KG1nbXRmcmFtZSkgew0KPiA+ICsgICAgICAgICAgICAgICB1MTYgY2FwYWI7DQo+ID4gKw0KPiA+
ICsgICAgICAgICAgICAgICBpZiAodW5saWtlbHkoaWVlZTgwMjExX2lzX2FjdGlvbih3aC0+ZnJh
bWVfY29udHJvbCkgJiYNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1nbXQtPnUu
YWN0aW9uLmNhdGVnb3J5ID09DQo+IFdMQU5fQ0FURUdPUllfQkFDSyAmJg0KPiA+ICsNCj4gbWdt
dC0+dS5hY3Rpb24udS5hZGRiYV9yZXEuYWN0aW9uX2NvZGUgPT0NCj4gPiArICAgICAgICAgICAg
ICAgICAgICAgICAgICAgIFdMQU5fQUNUSU9OX0FEREJBX1JFUSkpIHsNCj4gPiArICAgICAgICAg
ICAgICAgICAgICAgICBjYXBhYiA9DQo+IGxlMTZfdG9fY3B1KG1nbXQtPnUuYWN0aW9uLnUuYWRk
YmFfcmVxLmNhcGFiKTsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICB0aWQgPSAoY2FwYWIg
Jg0KPiBJRUVFODAyMTFfQUREQkFfUEFSQU1fVElEX01BU0spID4+IDI7DQo+ID4gKyAgICAgICAg
ICAgICAgICAgICAgICAgaW5kZXggPSBtd2xfdHhfdGlkX3F1ZXVlX21hcHBpbmcodGlkKTsNCj4g
PiArICAgICAgICAgICAgICAgICAgICAgICBjYXBhYiB8PSAxOw0KPiA+ICsgICAgICAgICAgICAg
ICAgICAgICAgIG1nbXQtPnUuYWN0aW9uLnUuYWRkYmFfcmVxLmNhcGFiID0NCj4gY3B1X3RvX2xl
MTYoY2FwYWIpOw0KPiA+ICsgICAgICAgICAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgICAg
ICAgICBpZiAodW5saWtlbHkoaWVlZTgwMjExX2lzX2FjdGlvbih3aC0+ZnJhbWVfY29udHJvbCkg
JiYNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1nbXQtPnUuYWN0aW9uLmNhdGVn
b3J5ID09DQo+IFdMQU5fQ0FURUdPUllfQkFDSyAmJg0KPiA+ICsNCj4gbWdtdC0+dS5hY3Rpb24u
dS5hZGRiYV9yZXNwLmFjdGlvbl9jb2RlID09DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAg
ICAgICBXTEFOX0FDVElPTl9BRERCQV9SRVNQKSkgew0KPiA+ICsgICAgICAgICAgICAgICAgICAg
ICAgIGNhcGFiID0NCj4gbGUxNl90b19jcHUobWdtdC0+dS5hY3Rpb24udS5hZGRiYV9yZXNwLmNh
cGFiKTsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICBjYXBhYiB8PSAxOw0KPiA+ICsgICAg
ICAgICAgICAgICAgICAgICAgIG1nbXQtPnUuYWN0aW9uLnUuYWRkYmFfcmVzcC5jYXBhYiA9DQo+
IGNwdV90b19sZTE2KGNhcGFiKTsNCj4gPiArICAgICAgICAgICAgICAgfQ0KPiA+ICsgICAgICAg
fQ0KPiA+ICsNCj4gPiArICAgICAgIGluZGV4ID0gU1lTQURQVF9UWF9XTU1fUVVFVUVTIC0gaW5k
ZXggLSAxOw0KPiA+ICsgICAgICAgdHhwcmlvcml0eSA9IGluZGV4Ow0KPiA+ICsNCj4gPiArICAg
ICAgIGlmIChzdGEgJiYgc3RhLT5odF9jYXAuaHRfc3VwcG9ydGVkICYmICFlYXBvbF9mcmFtZSAm
Jg0KPiA+ICsgICAgICAgICAgIGllZWU4MDIxMV9pc19kYXRhX3Fvcyh3aC0+ZnJhbWVfY29udHJv
bCkpIHsNCj4gPiArICAgICAgICAgICAgICAgdGlkID0gcW9zICYgMHhmOw0KPiA+ICsgICAgICAg
ICAgICAgICBtd2xfdHhfY291bnRfcGFja2V0KHN0YSwgdGlkKTsNCj4gPiArDQo+ID4gKyAgICAg
ICAgICAgICAgIHNwaW5fbG9ja19iaCgmcHJpdi0+c3RyZWFtX2xvY2spOw0KPiA+ICsgICAgICAg
ICAgICAgICBzdHJlYW0gPSBtd2xfZndjbWRfbG9va3VwX3N0cmVhbShodywgc3RhLT5hZGRyLCB0
aWQpOw0KPiA+ICsNCj4gPiArICAgICAgICAgICAgICAgaWYgKHN0cmVhbSkgew0KPiA+ICsgICAg
ICAgICAgICAgICAgICAgICAgIGlmIChzdHJlYW0tPnN0YXRlID09IEFNUERVX1NUUkVBTV9BQ1RJ
VkUpDQo+IHsNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChXQVJOX09O
KCEocW9zICYNCj4gPiArDQo+IE1XTF9RT1NfQUNLX1BPTElDWV9CTE9DS0FDSykpKSB7DQo+ID4g
Kw0KPiBzcGluX3VubG9ja19iaCgmcHJpdi0+c3RyZWFtX2xvY2spOw0KPiA+ICsgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXZfa2ZyZWVfc2tiX2FueShza2IpOw0KPiA+
ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47DQo+ID4gKyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgdHhwcmlvcml0eSA9DQo+ID4gKw0KPiAoU1lTQURQVF9UWF9XTU1f
UVVFVUVTICsgc3RyZWFtLT5pZHgpICUNCj4gPiArDQo+IFNZU0FEUFRfVE9UQUxfSFdfUVVFVUVT
Ow0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoc3RyZWFtLT5zdGF0ZSA9
PQ0KPiBBTVBEVV9TVFJFQU1fTkVXKSB7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAvKiBXZSBnZXQgaGVyZSBpZiB0aGUgZHJpdmVyIHNlbmRzIHVzDQo+IHBhY2tldHMNCj4g
PiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqIGFmdGVyIHdlJ3ZlIGluaXRpYXRl
ZCBhIHN0cmVhbSwgYnV0DQo+IGJlZm9yZQ0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICogb3VyIGFtcGR1X2FjdGlvbiByb3V0aW5lIGhhcw0KPiBiZWVuIGNhbGxlZA0KPiA+
ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICogd2l0aA0KPiBJRUVFODAyMTFfQU1Q
RFVfVFhfU1RBUlQgdG8gZ2V0IHRoZSBTU04NCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAqIGZvciB0aGUgQUREQkEgcmVxdWVzdC4gIFNvIHRoaXMNCj4gcGFja2V0IGNhbg0K
PiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICogZ28gb3V0IHdpdGggbm8gcmlz
ayBvZiBzZXF1ZW5jZQ0KPiBudW1iZXINCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAqIG1pc21hdGNoLiAgTm8gc3BlY2lhbCBoYW5kbGluZyBpcw0KPiByZXF1aXJlZC4NCj4g
PiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqLw0KPiA+ICsgICAgICAgICAgICAg
ICAgICAgICAgIH0gZWxzZSB7DQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAv
KiBEcm9wIHBhY2tldHMgdGhhdCB3b3VsZCBnbyBvdXQNCj4gYWZ0ZXIgdGhlDQo+ID4gKyAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBBRERCQSByZXF1ZXN0IHdhcyBzZW50IGJ1dA0K
PiBiZWZvcmUgdGhlIEFEREJBDQo+ID4gKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
KiByZXNwb25zZSBpcyByZWNlaXZlZC4gIElmIHdlIGRvbid0DQo+IGRvIHRoaXMsDQo+ID4gKyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiB0aGUgcmVjaXBpZW50IHdvdWxkIHByb2Jh
Ymx5DQo+IHJlY2VpdmUgaXQNCj4gPiArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAq
IGFmdGVyIHRoZSBBRERCQSByZXF1ZXN0IHdpdGggU1NODQo+IDAuICBUaGlzDQo+ID4gKyAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgKiB3aWxsIGNhdXNlIHRoZSByZWNpcGllbnQncyBC
QQ0KPiByZWNlaXZlIHdpbmRvdw0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICogdG8gc2hpZnQsIHdoaWNoIHdvdWxkIGNhdXNlIHRoZQ0KPiBzdWJzZXF1ZW50DQo+ID4gKyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBwYWNrZXRzIGluIHRoZSBCQSBzdHJlYW0g
dG8gYmUNCj4gZGlzY2FyZGVkLg0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICogbWFjODAyMTEgcXVldWVzIG91ciBwYWNrZXRzIGZvcg0KPiB1cyBpbiB0aGlzDQo+ID4gKyAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBjYXNlLCBzbyB0aGlzIGlzIHJlYWxseSBq
dXN0IGEgc2FmZXR5DQo+IGNoZWNrLg0KPiA+ICsgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICovDQo+IA0KPiBXZWxsLi4uIEkgdGhpbmsgeW91IGNhbiByZWFsbHkgYXZvaWQgdGhhdC4N
Cg0KQU1QRFUgcmVsYXRlZCBjb2RlIHdpbGwgYmUgY2hlY2tlZCBiYXNlZCBvbiB5b3VyIHN1Z2dl
c3Rpb25zIGxhdGVyLg0K
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Lin Dec. 15, 2015, 7:54 a.m. UTC | #18
> Kalle Valo [mailto:kvalo@codeaurora.org] wrote:
> 
> David Lin <dlin@marvell.com> writes:
> 
> > On November 26, 2015 5:40 PM, Johannes Berg wrote:
> >> On Thu, 2015-11-26 at 08:27 +0000, David Lin wrote:
> >>
> >> > > > +#ifdef CONFIG_SUPPORT_MFG
> >> > >
> >> > > This Kconfig variable doesn't exist.
> >> > >
> >> >
> >> > The compile variable is used privately by Marvell and our customers
> >> > in production line.
> >>
> >> Yeah, still. Make it a proper Kconfig variable, defaulting to off and
> >> hidden under something, or remove it. It's extremely misleading to
> >> have something called CONFIG_* when it's not a Kconfig variable.
> >>
> >
> > I will change this compile variable from "CONFIG_SUPPORT_MFG" to
> > "SUPPORT_MFG".
> 
> Then it's still dead code which won't ever get compiled in upstream.
> Please follow what Johannes suggested.
> 

The code will be removed.

> --
> Kalle Valo
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Lin Dec. 15, 2015, 7:55 a.m. UTC | #19
> Kalle Valo [mailto:kvalo@codeaurora.org] wrote:

> 

> David Lin <dlin@marvell.com> writes:

> 

> > This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897

> > chipsets.

> > This driver was developed as part of the openwrt.org project to

> > support Linksys WRT1900AC and is maintained on

> https://github.com/kaloz/mwlwifi.

> >

> > The mwlwifi driver differs from existing mwifiex driver:

> > o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem

> > to provide full AP/Wireless Bridge functionality (routers).

> > o mwifiex is a "fullmac driver" which provides a comprehensive set of

> > client functions (laptops/embedded devices) o only mwlwifi supports

> > Marvell AP chip 886X series

> >

> > NOTE: Users with Marvell 88W8897 chipsets currently should enable

> > (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT

> BOTH.

> >

> > mwlwifi driver leveraged code from existing MWL8K driver in the

> > following areas:

> > - 802.11n setting for mac80211

> > - Functions needed to hook up to mac80211

> > - Interactions with mac80211 to establish BA streams

> > - Partial firmware APIs, some data fields

> > - Method to pass Rx packets to mac80211 except 11ac rates

> >

> > In addition, mwlwifi driver supports:

> > - future scalability and future development (refactored source code)

> > - Marvell 802.11ac chipsets, including combo BT devices

> > - 802.11ac related settings and functions

> > - concurrent AP+STA functionalities with single firmware per chip

> > - firmware APIs for the supported chipset

> > - communicating new mac80211 settings to firmware

> > - Different TX/RX datapath where applicable

> > - A-MSDU and A-MPDU

> > - mac80211-based MESH (work in progress)

> > - Refined the code to establish BA streams

> >

> > NOTE: MWLWIFI will be organized under new vendor specific

> > folder/marvell, as per request of the gate keeper and community.

> >

> > Signed-off-by: David Lin <dlin@marvell.com>

> 

> This seems to use base64 encoding, how did you submit this? 'git send-email'

> tool is strongly preferred.

> 

> Content-Transfer-Encoding: base64

> 

> I applied this to pending branch and saw few easy conflicts with Kconfig files

> and Makefiles, I guess you submitted this patch before I added the vendor

> directories. Let's see what kbuild finds but I already saw two

> warnings:

> 

> drivers/net/wireless/marvell/mwlwifi/main.c:160:20: warning: incorrect type

> in argument 1 (different address spaces)

> drivers/net/wireless/marvell/mwlwifi/main.c:160:20:    expected void const

> *ptr

> drivers/net/wireless/marvell/mwlwifi/main.c:160:20:    got void [noderef]

> <asn:2>*[assigned] addr

> drivers/net/wireless/marvell/mwlwifi/main.c:171:20: warning: incorrect type

> in argument 1 (different address spaces)

> drivers/net/wireless/marvell/mwlwifi/main.c:171:20:    expected void const

> *ptr

> drivers/net/wireless/marvell/mwlwifi/main.c:171:20:    got void [noderef]

> <asn:2>*[assigned] addr

>


I will fix it.

> --

> Kalle Valo
Julian Calaby March 16, 2016, 11:24 p.m. UTC | #20
Hi David,

On Thu, Nov 12, 2015 at 2:51 PM, David Lin <dlin@marvell.com> wrote:
> This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
> chipsets.
> This driver was developed as part of the openwrt.org project to support
> Linksys WRT1900AC and is maintained on https://github.com/kaloz/mwlwifi.
>
> The mwlwifi driver differs from existing mwifiex driver:
> o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem
> to provide full AP/Wireless Bridge functionality (routers).
> o mwifiex is a "fullmac driver" which provides a comprehensive set of
> client functions (laptops/embedded devices)
> o only mwlwifi supports Marvell AP chip 886X series
>
> NOTE: Users with Marvell 88W8897 chipsets currently should enable
> (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT BOTH.
>
> mwlwifi driver leveraged code from existing MWL8K driver in the
> following areas:
> - 802.11n setting for mac80211
> - Functions needed to hook up to mac80211
> - Interactions with mac80211 to establish BA streams
> - Partial firmware APIs, some data fields
> - Method to pass Rx packets to mac80211 except 11ac rates
>
> In addition, mwlwifi driver supports:
> - future scalability and future development (refactored source code)
> - Marvell 802.11ac chipsets, including combo BT devices
> - 802.11ac related settings and functions
> - concurrent AP+STA functionalities with single firmware per chip
> - firmware APIs for the supported chipset
> - communicating new mac80211 settings to firmware
> - Different TX/RX datapath where applicable
> - A-MSDU and A-MPDU
> - mac80211-based MESH (work in progress)
> - Refined the code to establish BA streams
>
> NOTE: MWLWIFI will be organized under new vendor specific folder/marvell,
> as per request of the gate keeper and community.
>
> Signed-off-by: David Lin <dlin@marvell.com>

It's been quite a while since this was reviewed. Do you have a new
version of the driver?

Thanks,
David Lin March 17, 2016, 1:23 a.m. UTC | #21
PiBGcm9tOiBKdWxpYW4gQ2FsYWJ5IFttYWlsdG86anVsaWFuLmNhbGFieUBnbWFpbC5jb21dIFRo
dXJzZGF5LCBNYXJjaCAxNywgMjAxNiA3OjI0IEFNDQo+IA0KPiBIaSBEYXZpZCwNCj4gDQo+IE9u
IFRodSwgTm92IDEyLCAyMDE1IGF0IDI6NTEgUE0sIERhdmlkIExpbiA8ZGxpbkBtYXJ2ZWxsLmNv
bT4gd3JvdGU6DQo+ID4gVGhpcyBwYXRjaCBwcm92aWRlcyB0aGUgbXdsd2lmaSBkcml2ZXIgZm9y
IE1hcnZlbGwgODg2MywgODg2NCBhbmQgODg5Nw0KPiA+IGNoaXBzZXRzLg0KPiA+IFRoaXMgZHJp
dmVyIHdhcyBkZXZlbG9wZWQgYXMgcGFydCBvZiB0aGUgb3BlbndydC5vcmcgcHJvamVjdCB0bw0K
PiA+IHN1cHBvcnQgTGlua3N5cyBXUlQxOTAwQUMgYW5kIGlzIG1haW50YWluZWQgb24NCj4gaHR0
cHM6Ly9naXRodWIuY29tL2thbG96L213bHdpZmkuDQo+ID4NCj4gPiBUaGUgbXdsd2lmaSBkcml2
ZXIgZGlmZmVycyBmcm9tIGV4aXN0aW5nIG13aWZpZXggZHJpdmVyOg0KPiA+IG8gbXdsd2lmaSBp
cyBhICJzb2Z0bWFjIGRyaXZlciIgdXNpbmcgdGhlIGtlcm5lbD/uqIMgbWFjODAyLjExIHN1YnN5
c3RlbQ0KPiA+IHRvIHByb3ZpZGUgZnVsbCBBUC9XaXJlbGVzcyBCcmlkZ2UgZnVuY3Rpb25hbGl0
eSAocm91dGVycykuDQo+ID4gbyBtd2lmaWV4IGlzIGEgImZ1bGxtYWMgZHJpdmVyIiB3aGljaCBw
cm92aWRlcyBhIGNvbXByZWhlbnNpdmUgc2V0IG9mDQo+ID4gY2xpZW50IGZ1bmN0aW9ucyAobGFw
dG9wcy9lbWJlZGRlZCBkZXZpY2VzKSBvIG9ubHkgbXdsd2lmaSBzdXBwb3J0cw0KPiA+IE1hcnZl
bGwgQVAgY2hpcCA4ODZYIHNlcmllcw0KPiA+DQo+ID4gTk9URTogVXNlcnMgd2l0aCBNYXJ2ZWxs
IDg4Vzg4OTcgY2hpcHNldHMgY3VycmVudGx5IHNob3VsZCBlbmFibGUNCj4gPiAoQ09ORklHPVkg
b3IgTSkgZWl0aGVyIENPTkZJR19NV0lGSUVYIG9yIENPTkZJR19NV0xXSUZJLCBOT1QNCj4gQk9U
SC4NCj4gPg0KPiA+IG13bHdpZmkgZHJpdmVyIGxldmVyYWdlZCBjb2RlIGZyb20gZXhpc3Rpbmcg
TVdMOEsgZHJpdmVyIGluIHRoZQ0KPiA+IGZvbGxvd2luZyBhcmVhczoNCj4gPiAtIDgwMi4xMW4g
c2V0dGluZyBmb3IgbWFjODAyMTENCj4gPiAtIEZ1bmN0aW9ucyBuZWVkZWQgdG8gaG9vayB1cCB0
byBtYWM4MDIxMQ0KPiA+IC0gSW50ZXJhY3Rpb25zIHdpdGggbWFjODAyMTEgdG8gZXN0YWJsaXNo
IEJBIHN0cmVhbXMNCj4gPiAtIFBhcnRpYWwgZmlybXdhcmUgQVBJcywgc29tZSBkYXRhIGZpZWxk
cw0KPiA+IC0gTWV0aG9kIHRvIHBhc3MgUnggcGFja2V0cyB0byBtYWM4MDIxMSBleGNlcHQgMTFh
YyByYXRlcw0KPiA+DQo+ID4gSW4gYWRkaXRpb24sIG13bHdpZmkgZHJpdmVyIHN1cHBvcnRzOg0K
PiA+IC0gZnV0dXJlIHNjYWxhYmlsaXR5IGFuZCBmdXR1cmUgZGV2ZWxvcG1lbnQgKHJlZmFjdG9y
ZWQgc291cmNlIGNvZGUpDQo+ID4gLSBNYXJ2ZWxsIDgwMi4xMWFjIGNoaXBzZXRzLCBpbmNsdWRp
bmcgY29tYm8gQlQgZGV2aWNlcw0KPiA+IC0gODAyLjExYWMgcmVsYXRlZCBzZXR0aW5ncyBhbmQg
ZnVuY3Rpb25zDQo+ID4gLSBjb25jdXJyZW50IEFQK1NUQSBmdW5jdGlvbmFsaXRpZXMgd2l0aCBz
aW5nbGUgZmlybXdhcmUgcGVyIGNoaXANCj4gPiAtIGZpcm13YXJlIEFQSXMgZm9yIHRoZSBzdXBw
b3J0ZWQgY2hpcHNldA0KPiA+IC0gY29tbXVuaWNhdGluZyBuZXcgbWFjODAyMTEgc2V0dGluZ3Mg
dG8gZmlybXdhcmUNCj4gPiAtIERpZmZlcmVudCBUWC9SWCBkYXRhcGF0aCB3aGVyZSBhcHBsaWNh
YmxlDQo+ID4gLSBBLU1TRFUgYW5kIEEtTVBEVQ0KPiA+IC0gbWFjODAyMTEtYmFzZWQgTUVTSCAo
d29yayBpbiBwcm9ncmVzcykNCj4gPiAtIFJlZmluZWQgdGhlIGNvZGUgdG8gZXN0YWJsaXNoIEJB
IHN0cmVhbXMNCj4gPg0KPiA+IE5PVEU6IE1XTFdJRkkgd2lsbCBiZSBvcmdhbml6ZWQgdW5kZXIg
bmV3IHZlbmRvciBzcGVjaWZpYw0KPiA+IGZvbGRlci9tYXJ2ZWxsLCBhcyBwZXIgcmVxdWVzdCBv
ZiB0aGUgZ2F0ZSBrZWVwZXIgYW5kIGNvbW11bml0eS4NCj4gPg0KPiA+IFNpZ25lZC1vZmYtYnk6
IERhdmlkIExpbiA8ZGxpbkBtYXJ2ZWxsLmNvbT4NCj4gDQo+IEl0J3MgYmVlbiBxdWl0ZSBhIHdo
aWxlIHNpbmNlIHRoaXMgd2FzIHJldmlld2VkLiBEbyB5b3UgaGF2ZSBhIG5ldyB2ZXJzaW9uIG9m
DQo+IHRoZSBkcml2ZXI/DQo+IA0KDQpZZXMgd2UgaGF2ZSBzb21lIHVwZGF0ZXMsIGFuZCBjb250
aW51ZSB3b3JraW5nIG9uIGl0LiBCdXQgd2UgZmVsdCB0aGF0IGNvbW11bml0eSBkb2VzIG5vdCBz
ZWVtIHRvIGJlIHZlcnkgaW50ZXJlc3RlZCBpbiB0aGlzIGRyaXZlciwgc28gd2UgaGF2ZSBub3Qg
YmVlbiB1cGRhdGluZyB0aGUgd2lyZWxlc3Mgb3JnLiBMZXQgdXMga25vdyBpZiB5b3UgYXJlIHN1
cHBvcnRpdmUgYW5kIHdhaXRpbmcgZm9yIGl0Lg0KDQpUaGFua3MsDQpEYXZpZA0KDQo+DQo+IFRo
YW5rcywNCj4NCj4gLS0NCj4gSnVsaWFuIENhbGFieQ0KPiANCj4gRW1haWw6IGp1bGlhbi5jYWxh
YnlAZ21haWwuY29tDQo+IFByb2ZpbGU6IGh0dHA6Ly93d3cuZ29vZ2xlLmNvbS9wcm9maWxlcy9q
dWxpYW4uY2FsYWJ5Lw0K
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Julian Calaby March 17, 2016, 1:32 a.m. UTC | #22
Hi David,

On Thu, Mar 17, 2016 at 12:23 PM, David Lin <dlin@marvell.com> wrote:
>> From: Julian Calaby [mailto:julian.calaby@gmail.com] Thursday, March 17, 2016 7:24 AM
>>
>> Hi David,
>>
>> On Thu, Nov 12, 2015 at 2:51 PM, David Lin <dlin@marvell.com> wrote:
>> > This patch provides the mwlwifi driver for Marvell 8863, 8864 and 8897
>> > chipsets.
>> > This driver was developed as part of the openwrt.org project to
>> > support Linksys WRT1900AC and is maintained on
>> https://github.com/kaloz/mwlwifi.
>> >
>> > The mwlwifi driver differs from existing mwifiex driver:
>> > o mwlwifi is a "softmac driver" using the kernel?? mac802.11 subsystem
>> > to provide full AP/Wireless Bridge functionality (routers).
>> > o mwifiex is a "fullmac driver" which provides a comprehensive set of
>> > client functions (laptops/embedded devices) o only mwlwifi supports
>> > Marvell AP chip 886X series
>> >
>> > NOTE: Users with Marvell 88W8897 chipsets currently should enable
>> > (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT
>> BOTH.
>> >
>> > mwlwifi driver leveraged code from existing MWL8K driver in the
>> > following areas:
>> > - 802.11n setting for mac80211
>> > - Functions needed to hook up to mac80211
>> > - Interactions with mac80211 to establish BA streams
>> > - Partial firmware APIs, some data fields
>> > - Method to pass Rx packets to mac80211 except 11ac rates
>> >
>> > In addition, mwlwifi driver supports:
>> > - future scalability and future development (refactored source code)
>> > - Marvell 802.11ac chipsets, including combo BT devices
>> > - 802.11ac related settings and functions
>> > - concurrent AP+STA functionalities with single firmware per chip
>> > - firmware APIs for the supported chipset
>> > - communicating new mac80211 settings to firmware
>> > - Different TX/RX datapath where applicable
>> > - A-MSDU and A-MPDU
>> > - mac80211-based MESH (work in progress)
>> > - Refined the code to establish BA streams
>> >
>> > NOTE: MWLWIFI will be organized under new vendor specific
>> > folder/marvell, as per request of the gate keeper and community.
>> >
>> > Signed-off-by: David Lin <dlin@marvell.com>
>>
>> It's been quite a while since this was reviewed. Do you have a new version of
>> the driver?
>>
>
> Yes we have some updates, and continue working on it. But we felt that community does not seem to be very interested in this driver, so we have not been updating the wireless org. Let us know if you are supportive and waiting for it.

My understanding was that we'd very much like the driver to be sent
upstream. From my perspective, it looked like you'd lost interest, not
us.

Thanks,
David Lin March 17, 2016, 2:10 a.m. UTC | #23
> From: Julian Calaby [mailto:julian.calaby@gmail.com] Thursday, March 17, 2016 9:32 AM> 

> Hi David,

> 

> On Thu, Mar 17, 2016 at 12:23 PM, David Lin <dlin@marvell.com> wrote:

> >> From: Julian Calaby [mailto:julian.calaby@gmail.com] Thursday, March

> >> 17, 2016 7:24 AM

> >>

> >> Hi David,

> >>

> >> On Thu, Nov 12, 2015 at 2:51 PM, David Lin <dlin@marvell.com> wrote:

> >> > This patch provides the mwlwifi driver for Marvell 8863, 8864 and

> >> > 8897 chipsets.

> >> > This driver was developed as part of the openwrt.org project to

> >> > support Linksys WRT1900AC and is maintained on

> >> https://github.com/kaloz/mwlwifi.

> >> >

> >> > The mwlwifi driver differs from existing mwifiex driver:

> >> > o mwlwifi is a "softmac driver" using the kernel?? mac802.11

> >> > subsystem to provide full AP/Wireless Bridge functionality (routers).

> >> > o mwifiex is a "fullmac driver" which provides a comprehensive set

> >> > of client functions (laptops/embedded devices) o only mwlwifi

> >> > supports Marvell AP chip 886X series

> >> >

> >> > NOTE: Users with Marvell 88W8897 chipsets currently should enable

> >> > (CONFIG=Y or M) either CONFIG_MWIFIEX or CONFIG_MWLWIFI, NOT

> >> BOTH.

> >> >

> >> > mwlwifi driver leveraged code from existing MWL8K driver in the

> >> > following areas:

> >> > - 802.11n setting for mac80211

> >> > - Functions needed to hook up to mac80211

> >> > - Interactions with mac80211 to establish BA streams

> >> > - Partial firmware APIs, some data fields

> >> > - Method to pass Rx packets to mac80211 except 11ac rates

> >> >

> >> > In addition, mwlwifi driver supports:

> >> > - future scalability and future development (refactored source

> >> > code)

> >> > - Marvell 802.11ac chipsets, including combo BT devices

> >> > - 802.11ac related settings and functions

> >> > - concurrent AP+STA functionalities with single firmware per chip

> >> > - firmware APIs for the supported chipset

> >> > - communicating new mac80211 settings to firmware

> >> > - Different TX/RX datapath where applicable

> >> > - A-MSDU and A-MPDU

> >> > - mac80211-based MESH (work in progress)

> >> > - Refined the code to establish BA streams

> >> >

> >> > NOTE: MWLWIFI will be organized under new vendor specific

> >> > folder/marvell, as per request of the gate keeper and community.

> >> >

> >> > Signed-off-by: David Lin <dlin@marvell.com>

> >>

> >> It's been quite a while since this was reviewed. Do you have a new

> >> version of the driver?

> >>

> >

> > Yes we have some updates, and continue working on it. But we felt that

> community does not seem to be very interested in this driver, so we have not

> been updating the wireless org. Let us know if you are supportive and waiting

> for it.

> 

> My understanding was that we'd very much like the driver to be sent upstream.

> From my perspective, it looked like you'd lost interest, not us.

> 


We felt some resistance from various people, and get discouraged. Though we continue to work on it internally, we were just keeping further submission at the back of our mind. We do keep the feedback from community and will work on compliancy where applicable and possible.

Next, we will prepare a patch8 for submission later.

>

> Thanks,

> 

> --

> Julian Calaby

> 

> Email: julian.calaby@gmail.com

> Profile: http://www.google.com/profiles/julian.calaby/
Bjørn Mork April 3, 2016, 12:05 p.m. UTC | #24
David Lin <dlin@marvell.com> writes:

> We felt some resistance from various people, and get discouraged.

Sorry to hear that.  I believe this is a misunderstanding.  I am
convinced that *everyone* who commented on the driver did so with the
intention to help getting it merged.

I for one would very much like to see this driver in mainline.  But for
that to happen, you wil have to:
 1) create a feature frozen branch for mainline submission
 2) respond relatively quickly to comments received, posting new
    versions of the patch set if necessary

The main issue preventing this driver from being merged so far, in my
view, is that you have continued to add features between each attemt to
get it merged.  Adding new features is of course not bad, but you should
do that in another branch *until* the initial version is accepted.

Let me know if there is anything I can do to help getting this into mainline

Bjørn
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kalle Valo April 15, 2016, 12:40 p.m. UTC | #25
Bjørn Mork <bjorn@mork.no> writes:

> David Lin <dlin@marvell.com> writes:
>
>> We felt some resistance from various people, and get discouraged.
>
> Sorry to hear that.  I believe this is a misunderstanding.  I am
> convinced that *everyone* who commented on the driver did so with the
> intention to help getting it merged.

Exactly. Why would we otherwise spend so much time reviewing the driver
if we would not want it included?

> I for one would very much like to see this driver in mainline.  But for
> that to happen, you wil have to:
>  1) create a feature frozen branch for mainline submission
>  2) respond relatively quickly to comments received, posting new
>     versions of the patch set if necessary
>
> The main issue preventing this driver from being merged so far, in my
> view, is that you have continued to add features between each attemt to
> get it merged.  Adding new features is of course not bad, but you should
> do that in another branch *until* the initial version is accepted.

Thanks for the good summary, I think we should even add that to our wiki
as a guideline for new drivers. Then submitting a new driver (or normal
patches for that matter) you really want to optimise the use of
reviewer's time. Just fix the issues pointed out in the review, don't do
anything extra as the chances are that it just slows down everything.
diff mbox

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 27b27c0..7c32f0a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6629,6 +6629,12 @@  L:	linux-wireless@vger.kernel.org
 S:	Odd Fixes
 F:	drivers/net/wireless/mwl8k.c
 
+MARVELL MWLWIFI WIRELESS DRIVER
+M:	David Lin <dlin@marvell.com>
+L:	linux-wireless@vger.kernel.org
+S:	Maintained
+F:	drivers/net/wireless/marvell/mwlwifi
+
 MARVELL SOC MMC/SD/SDIO CONTROLLER DRIVER
 M:	Nicolas Pitre <nico@fluxnic.net>
 S:	Odd Fixes
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f9f9422..d599b35 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -285,5 +285,6 @@  source "drivers/net/wireless/zd1211rw/Kconfig"
 source "drivers/net/wireless/mwifiex/Kconfig"
 source "drivers/net/wireless/cw1200/Kconfig"
 source "drivers/net/wireless/rsi/Kconfig"
+source "drivers/net/wireless/marvell/Kconfig"
 
 endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 740fdd3..71504a7 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -60,3 +60,5 @@  obj-$(CONFIG_BRCMSMAC)	+= brcm80211/
 
 obj-$(CONFIG_CW1200)	+= cw1200/
 obj-$(CONFIG_RSI_91X)	+= rsi/
+
+obj-$(CONFIG_WL_MARVELL)	+= marvell/
diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig
new file mode 100644
index 0000000..d73e642
--- /dev/null
+++ b/drivers/net/wireless/marvell/Kconfig
@@ -0,0 +1,8 @@ 
+menuconfig WL_MARVELL
+	bool "Marvell Wireless LAN support"
+	---help---
+	  Enable community drivers for Marvell Wi-Fi devices.
+
+if WL_MARVELL
+source "drivers/net/wireless/marvell/mwlwifi/Kconfig"
+endif # WL_MARVELL
diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile
new file mode 100644
index 0000000..70d1b3f
--- /dev/null
+++ b/drivers/net/wireless/marvell/Makefile
@@ -0,0 +1 @@ 
+obj-$(CONFIG_MWLWIFI)	+= mwlwifi/
diff --git a/drivers/net/wireless/marvell/mwlwifi/Kconfig b/drivers/net/wireless/marvell/mwlwifi/Kconfig
new file mode 100644
index 0000000..a9bcb9c
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/Kconfig
@@ -0,0 +1,23 @@ 
+config MWLWIFI
+	tristate "Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211 compatible)"
+	depends on PCI && MAC80211
+	select FW_LOADER
+	---help---
+		Select to build the driver supporting the:
+
+		Marvell Wireless Wi-Fi 88W8864 modules
+		Marvell Wireless Wi-Fi 88W8897 modules
+
+		This driver uses the kernel's mac80211 subsystem.
+
+		If you want to compile the driver as a module (= code which can be
+		inserted in and removed from the running kernel whenever you want),
+		say M here and read <file:Documentation/kbuild/modules.txt>.  The
+		module will be called mwlwifi.
+
+		NOTE: Selecting this driver may cause conflict with MWIFIEX driver
+		that also operates on the same part number 88W8897. Users should
+		select either MWIFIEX or MWLWIFI, not both. MWIFIEX is fullmac,
+		supporting more comprehensive client functions for laptops/embedded
+		devices. MWLWIFI is mac80211-based for full AP/Wireless Bridge.
+
diff --git a/drivers/net/wireless/marvell/mwlwifi/Makefile b/drivers/net/wireless/marvell/mwlwifi/Makefile
new file mode 100644
index 0000000..88f7efd
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/Makefile
@@ -0,0 +1,12 @@ 
+obj-$(CONFIG_MWLWIFI)	+= mwlwifi.o
+
+mwlwifi-objs		+= main.o
+mwlwifi-objs		+= mac80211.o
+mwlwifi-objs		+= fwdl.o
+mwlwifi-objs		+= fwcmd.o
+mwlwifi-objs		+= tx.o
+mwlwifi-objs		+= rx.o
+mwlwifi-objs		+= isr.o
+mwlwifi-$(CONFIG_DEBUG_FS) += debugfs.o
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.c b/drivers/net/wireless/marvell/mwlwifi/debugfs.c
new file mode 100644
index 0000000..997461a
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.c
@@ -0,0 +1,433 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements debug fs related functions. */
+
+#include <linux/debugfs.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "hostcmd.h"
+#include "fwcmd.h"
+#include "debugfs.h"
+
+#define MWLWIFI_DEBUGFS_ADD_FILE(name) do { \
+	if (!debugfs_create_file(#name, 0644, priv->debugfs_phy, \
+				 priv, &mwl_debugfs_##name##_fops)) \
+		return; \
+} while (0)
+
+#define MWLWIFI_DEBUGFS_FILE_OPS(name) \
+static const struct file_operations mwl_debugfs_##name##_fops = { \
+	.read = mwl_debugfs_##name##_read, \
+	.write = mwl_debugfs_##name##_write, \
+	.open = simple_open, \
+}
+
+#define MWLWIFI_DEBUGFS_FILE_READ_OPS(name) \
+static const struct file_operations mwl_debugfs_##name##_fops = { \
+	.read = mwl_debugfs_##name##_read, \
+	.open = simple_open, \
+}
+
+#define MWLWIFI_DEBUGFS_FILE_WRITE_OPS(name) \
+static const struct file_operations mwl_debugfs_##name##_fops = { \
+	.write = mwl_debugfs_##name##_write, \
+	.open = simple_open, \
+}
+
+static int print_mac_addr(char *p, u8 *mac_addr)
+{
+	int i;
+	char *str = p;
+
+	str += sprintf(str, "mac address: %02x", mac_addr[0]);
+	for (i = 1; i < ETH_ALEN; i++)
+		str += sprintf(str, ":%02x", mac_addr[i]);
+	str += sprintf(str, "\n");
+
+	return (str - p);
+}
+
+static int dump_data(char *p, u8 *data, int len, char *title)
+{
+	char *str = p;
+	int cur_byte = 0;
+	int i;
+
+	str += sprintf(str, "%s\n", title);
+	for (cur_byte = 0; cur_byte < len; cur_byte += 8) {
+		if ((cur_byte + 8) < len) {
+			for (i = 0; i < 8; i++)
+				str += sprintf(str, "0x%02x ",
+					       *(data + cur_byte + i));
+			str += sprintf(str, "\n");
+		} else {
+			for (i = 0; i < (len - cur_byte); i++)
+				str += sprintf(str, "0x%02x ",
+					       *(data + cur_byte + i));
+			str += sprintf(str, "\n");
+			break;
+		}
+	}
+
+	return (str - p);
+}
+
+static ssize_t mwl_debugfs_info_read(struct file *file, char __user *ubuf,
+				     size_t count, loff_t *ppos)
+{
+	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	char *p = (char *)page;
+	ssize_t ret;
+
+	if (!p)
+		return -ENOMEM;
+
+	p += sprintf(p, "\n");
+	p += sprintf(p, "driver name: %s\n", MWL_DRV_NAME);
+	p += sprintf(p, "chip type: %s\n",
+		     (priv->chip_type == MWL8864) ? "88W8864" : "88W8897");
+	p += sprintf(p, "hw version: %X\n", priv->hw_data.hw_version);
+	p += sprintf(p, "driver version: %s\n", MWL_DRV_VERSION);
+	p += sprintf(p, "firmware version: 0x%08x\n",
+		     priv->hw_data.fw_release_num);
+	p += print_mac_addr(p, priv->hw_data.mac_addr);
+	p += sprintf(p, "2g: %s\n", priv->disable_2g ? "disable" : "enable");
+	p += sprintf(p, "5g: %s\n", priv->disable_5g ? "disable" : "enable");
+	p += sprintf(p, "antenna: %d %d\n",
+		     (priv->antenna_tx == ANTENNA_TX_4_AUTO) ? 4 : 2,
+		     (priv->antenna_rx == ANTENNA_TX_4_AUTO) ? 4 : 2);
+	p += sprintf(p, "irq number: %d\n", priv->irq);
+	p += sprintf(p, "iobase0: %p\n", priv->iobase0);
+	p += sprintf(p, "iobase1: %p\n", priv->iobase1);
+	p += sprintf(p, "tx limit: %d\n", priv->txq_limit);
+	p += sprintf(p, "rx limit: %d\n", priv->recv_limit);
+	p += sprintf(p, "ap macid support: %08x\n",
+		     priv->ap_macids_supported);
+	p += sprintf(p, "sta macid support: %08x\n",
+		     priv->sta_macids_supported);
+	p += sprintf(p, "macid used: %08x\n", priv->macids_used);
+	p += sprintf(p, "mfg mode: %s\n", priv->mfg_mode ? "true" : "false");
+	p += sprintf(p, "\n");
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+				      (unsigned long)p - page);
+	free_page(page);
+
+	return ret;
+}
+
+static ssize_t mwl_debugfs_vif_read(struct file *file, char __user *ubuf,
+				    size_t count, loff_t *ppos)
+{
+	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	char *p = (char *)page;
+	struct mwl_vif *mwl_vif;
+	struct ieee80211_vif *vif;
+	char ssid[IEEE80211_MAX_SSID_LEN + 1];
+	ssize_t ret;
+
+	if (!p)
+		return -ENOMEM;
+
+	p += sprintf(p, "\n");
+	spin_lock_bh(&priv->vif_lock);
+	list_for_each_entry(mwl_vif, &priv->vif_list, list) {
+		vif = container_of((char *)mwl_vif, struct ieee80211_vif,
+				   drv_priv[0]);
+		p += sprintf(p, "macid: %d\n", mwl_vif->macid);
+		switch (vif->type) {
+		case NL80211_IFTYPE_AP:
+			p += sprintf(p, "type: ap\n");
+			memcpy(ssid, vif->bss_conf.ssid,
+			       vif->bss_conf.ssid_len);
+			ssid[vif->bss_conf.ssid_len] = 0;
+			p += sprintf(p, "ssid: %s\n", ssid);
+			p += print_mac_addr(p, mwl_vif->bssid);
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			p += sprintf(p, "type: mesh\n");
+			p += print_mac_addr(p, mwl_vif->bssid);
+			break;
+		case NL80211_IFTYPE_STATION:
+			p += sprintf(p, "type: sta\n");
+			p += print_mac_addr(p, mwl_vif->sta_mac);
+			break;
+		default:
+			p += sprintf(p, "type: unknown\n");
+			break;
+		}
+		p += sprintf(p, "hw_crypto_enabled: %s\n",
+			     mwl_vif->is_hw_crypto_enabled ? "true" : "false");
+		p += sprintf(p, "key idx: %d\n", mwl_vif->keyidx);
+		p += sprintf(p, "IV: %08x%04x\n", mwl_vif->iv32, mwl_vif->iv16);
+		p += dump_data(p, mwl_vif->beacon_info.ie_wmm_ptr,
+			       mwl_vif->beacon_info.ie_wmm_len, "WMM:");
+		p += dump_data(p, mwl_vif->beacon_info.ie_rsn_ptr,
+			       mwl_vif->beacon_info.ie_rsn_len, "RSN:");
+		p += dump_data(p, mwl_vif->beacon_info.ie_rsn48_ptr,
+			       mwl_vif->beacon_info.ie_rsn48_len, "RSN48:");
+		p += dump_data(p, mwl_vif->beacon_info.ie_ht_ptr,
+			       mwl_vif->beacon_info.ie_ht_len, "HT:");
+		p += dump_data(p, mwl_vif->beacon_info.ie_vht_ptr,
+			       mwl_vif->beacon_info.ie_vht_len, "VHT:");
+		p += sprintf(p, "\n");
+	}
+	spin_unlock_bh(&priv->vif_lock);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+				      (unsigned long)p - page);
+	free_page(page);
+
+	return ret;
+}
+
+static ssize_t mwl_debugfs_sta_read(struct file *file, char __user *ubuf,
+				    size_t count, loff_t *ppos)
+{
+	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	char *p = (char *)page;
+	struct mwl_sta *sta_info;
+	struct ieee80211_sta *sta;
+	ssize_t ret;
+
+	if (!p)
+		return -ENOMEM;
+
+	p += sprintf(p, "\n");
+	spin_lock_bh(&priv->sta_lock);
+	list_for_each_entry(sta_info, &priv->sta_list, list) {
+		sta = container_of((char *)sta_info, struct ieee80211_sta,
+				   drv_priv[0]);
+		p += print_mac_addr(p, sta->addr);
+		p += sprintf(p, "aid: %u\n", sta->aid);
+		p += sprintf(p, "ampdu: %s\n",
+			     sta_info->is_ampdu_allowed ? "true" : "false");
+		p += sprintf(p, "amsdu: %s\n",
+			     sta_info->is_amsdu_allowed ? "true" : "false");
+		if (sta_info->is_amsdu_allowed) {
+			p += sprintf(p, "amsdu cap: 0x%02x\n",
+				     sta_info->amsdu_ctrl.cap);
+		}
+		p += sprintf(p, "IV: %08x%04x\n",
+			     sta_info->iv32, sta_info->iv16);
+		p += sprintf(p, "\n");
+	}
+	spin_unlock_bh(&priv->sta_lock);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+				      (unsigned long)p - page);
+	free_page(page);
+
+	return ret;
+}
+
+static ssize_t mwl_debugfs_ampdu_read(struct file *file, char __user *ubuf,
+				      size_t count, loff_t *ppos)
+{
+	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+	unsigned long page = get_zeroed_page(GFP_KERNEL);
+	char *p = (char *)page;
+	struct mwl_ampdu_stream *stream;
+	int i;
+	ssize_t ret;
+
+	if (!p)
+		return -ENOMEM;
+
+	p += sprintf(p, "\n");
+	spin_lock_bh(&priv->stream_lock);
+	for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+		stream = &priv->ampdu[i];
+		p += sprintf(p, "stream: %d\n", i);
+		p += sprintf(p, "idx: %u\n", stream->idx);
+		p += sprintf(p, "state: %u\n", stream->state);
+		if (stream->sta) {
+			p += print_mac_addr(p, stream->sta->addr);
+			p += sprintf(p, "tid: %u\n", stream->tid);
+		}
+	}
+	spin_unlock_bh(&priv->stream_lock);
+	p += sprintf(p, "\n");
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page,
+				      (unsigned long)p - page);
+	free_page(page);
+
+	return ret;
+}
+
+static int mwl_debugfs_reg_access(struct mwl_priv *priv, bool write)
+{
+	struct ieee80211_hw *hw = priv->hw;
+	u8 set;
+	u32 *addr_val;
+	int ret = 0;
+
+	set = write ? WL_SET : WL_GET;
+
+	switch (priv->reg_type) {
+	case MWL_ACCESS_ADDR0:
+		if (set == WL_GET)
+			priv->reg_value =
+				readl(priv->iobase0 + priv->reg_offset);
+		else
+			writel(priv->reg_value,
+			       priv->iobase0 + priv->reg_offset);
+		break;
+	case MWL_ACCESS_ADDR1:
+		if (set == WL_GET)
+			priv->reg_value =
+				readl(priv->iobase1 + priv->reg_offset);
+		else
+			writel(priv->reg_value,
+			       priv->iobase1 + priv->reg_offset);
+		break;
+	case MWL_ACCESS_ADDR:
+		addr_val = kmalloc(64 * sizeof(u32), GFP_KERNEL);
+		if (addr_val) {
+			memset(addr_val, 0, 64 * sizeof(u32));
+			addr_val[0] = priv->reg_value;
+			ret = mwl_fwcmd_get_addr_value(hw, priv->reg_offset,
+						       4, addr_val, set);
+			if ((!ret) && (set == WL_GET))
+				priv->reg_value = addr_val[0];
+			kfree(addr_val);
+		} else {
+			ret = -ENOMEM;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static ssize_t mwl_debugfs_regrdwr_read(struct file *file, char __user *ubuf,
+					size_t count, loff_t *ppos)
+{
+	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+	int pos = 0, ret = 0;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (!priv->reg_type) {
+		/* No command has been given */
+		pos += snprintf(buf, PAGE_SIZE, "0");
+		goto none;
+	}
+
+	/* Set command has been given */
+	if (priv->reg_value != UINT_MAX) {
+		ret = mwl_debugfs_reg_access(priv, true);
+		goto done;
+	}
+	/* Get command has been given */
+	ret = mwl_debugfs_reg_access(priv, false);
+
+done:
+	if (!ret)
+		pos += snprintf(buf, PAGE_SIZE, "%u 0x%08x 0x%08x\n",
+				priv->reg_type, priv->reg_offset,
+				priv->reg_value);
+	else
+		pos += snprintf(buf, PAGE_SIZE, "error: %d(%u 0x%08x 0x%08x)\n",
+				ret, priv->reg_type, priv->reg_offset,
+				priv->reg_value);
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+none:
+
+	free_page(addr);
+	return ret;
+}
+
+static ssize_t mwl_debugfs_regrdwr_write(struct file *file,
+					 const char __user *ubuf,
+					 size_t count, loff_t *ppos)
+{
+	struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+	size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+	int ret;
+	u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (copy_from_user(buf, ubuf, buf_size)) {
+		ret = -EFAULT;
+		goto done;
+	}
+
+	ret = sscanf(buf, "%u %x %x", &reg_type, &reg_offset, &reg_value);
+
+	if (!reg_type) {
+		ret = -EINVAL;
+		goto done;
+	} else {
+		priv->reg_type = reg_type;
+		priv->reg_offset = reg_offset;
+		priv->reg_value = reg_value;
+		ret = count;
+	}
+done:
+
+	free_page(addr);
+	return ret;
+}
+
+MWLWIFI_DEBUGFS_FILE_READ_OPS(info);
+MWLWIFI_DEBUGFS_FILE_READ_OPS(vif);
+MWLWIFI_DEBUGFS_FILE_READ_OPS(sta);
+MWLWIFI_DEBUGFS_FILE_READ_OPS(ampdu);
+MWLWIFI_DEBUGFS_FILE_OPS(regrdwr);
+
+void mwl_debugfs_init(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	if (!priv->debugfs_phy)
+		priv->debugfs_phy = debugfs_create_dir("mwlwifi",
+						       hw->wiphy->debugfsdir);
+
+	if (!priv->debugfs_phy)
+		return;
+
+	MWLWIFI_DEBUGFS_ADD_FILE(info);
+	MWLWIFI_DEBUGFS_ADD_FILE(vif);
+	MWLWIFI_DEBUGFS_ADD_FILE(sta);
+	MWLWIFI_DEBUGFS_ADD_FILE(ampdu);
+	MWLWIFI_DEBUGFS_ADD_FILE(regrdwr);
+}
+
+void mwl_debugfs_remove(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	debugfs_remove(priv->debugfs_phy);
+	priv->debugfs_phy = NULL;
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/debugfs.h b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
new file mode 100644
index 0000000..dfc2a3c
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/debugfs.h
@@ -0,0 +1,24 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines debug fs related functions. */
+
+#ifndef _debugfs_h_
+#define _debugfs_h_
+
+void mwl_debugfs_init(struct ieee80211_hw *hw);
+void mwl_debugfs_remove(struct ieee80211_hw *hw);
+
+#endif /* _debugfs_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/dev.h b/drivers/net/wireless/marvell/mwlwifi/dev.h
new file mode 100644
index 0000000..370d3c0
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/dev.h
@@ -0,0 +1,477 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines device related information. */
+
+#ifndef _dev_h_
+#define _dev_h_
+
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <net/mac80211.h>
+
+#define MWL_DRV_NAME     KBUILD_MODNAME
+#define MWL_DRV_VERSION	 "10.3.0.14"
+
+/* Map to 0x80000000 (Bus control) on BAR0 */
+#define MACREG_REG_H2A_INTERRUPT_EVENTS      0x00000C18 /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_CAUSE       0x00000C1C /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_MASK        0x00000C20 /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_CLEAR_SEL   0x00000C24 /* (From host to ARM) */
+#define MACREG_REG_H2A_INTERRUPT_STATUS_MASK 0x00000C28 /* (From host to ARM) */
+
+#define MACREG_REG_A2H_INTERRUPT_EVENTS      0x00000C2C /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_CAUSE       0x00000C30 /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_MASK        0x00000C34 /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_CLEAR_SEL   0x00000C38 /* (From ARM to host) */
+#define MACREG_REG_A2H_INTERRUPT_STATUS_MASK 0x00000C3C /* (From ARM to host) */
+
+/* Map to 0x80000000 on BAR1 */
+#define MACREG_REG_GEN_PTR                  0x00000C10
+#define MACREG_REG_INT_CODE                 0x00000C14
+
+/* Bit definition for MACREG_REG_A2H_INTERRUPT_CAUSE (A2HRIC) */
+#define MACREG_A2HRIC_BIT_TX_DONE           BIT(0)
+#define MACREG_A2HRIC_BIT_RX_RDY            BIT(1)
+#define MACREG_A2HRIC_BIT_OPC_DONE          BIT(2)
+#define MACREG_A2HRIC_BIT_MAC_EVENT         BIT(3)
+#define MACREG_A2HRIC_BIT_RX_PROBLEM        BIT(4)
+#define MACREG_A2HRIC_BIT_RADIO_OFF         BIT(5)
+#define MACREG_A2HRIC_BIT_RADIO_ON          BIT(6)
+#define MACREG_A2HRIC_BIT_RADAR_DETECT      BIT(7)
+#define MACREG_A2HRIC_BIT_ICV_ERROR         BIT(8)
+#define MACREG_A2HRIC_BIT_WEAKIV_ERROR      BIT(9)
+#define MACREG_A2HRIC_BIT_QUE_EMPTY         BIT(10)
+#define MACREG_A2HRIC_BIT_QUE_FULL          BIT(11)
+#define MACREG_A2HRIC_BIT_CHAN_SWITCH       BIT(12)
+#define MACREG_A2HRIC_BIT_TX_WATCHDOG       BIT(13)
+#define MACREG_A2HRIC_BA_WATCHDOG           BIT(14)
+/* 15 taken by ISR_TXACK */
+#define MACREG_A2HRIC_BIT_SSU_DONE          BIT(16)
+#define MACREG_A2HRIC_CONSEC_TXFAIL         BIT(17)
+
+#define ISR_SRC_BITS        (MACREG_A2HRIC_BIT_RX_RDY | \
+			     MACREG_A2HRIC_BIT_TX_DONE | \
+			     MACREG_A2HRIC_BIT_OPC_DONE | \
+			     MACREG_A2HRIC_BIT_MAC_EVENT | \
+			     MACREG_A2HRIC_BIT_WEAKIV_ERROR | \
+			     MACREG_A2HRIC_BIT_ICV_ERROR | \
+			     MACREG_A2HRIC_BIT_SSU_DONE | \
+			     MACREG_A2HRIC_BIT_RADAR_DETECT | \
+			     MACREG_A2HRIC_BIT_CHAN_SWITCH | \
+			     MACREG_A2HRIC_BIT_TX_WATCHDOG | \
+			     MACREG_A2HRIC_BIT_QUE_EMPTY | \
+			     MACREG_A2HRIC_BA_WATCHDOG | \
+			     MACREG_A2HRIC_CONSEC_TXFAIL)
+
+#define MACREG_A2HRIC_BIT_MASK      ISR_SRC_BITS
+
+/* Bit definition for MACREG_REG_H2A_INTERRUPT_CAUSE (H2ARIC) */
+#define MACREG_H2ARIC_BIT_PPA_READY         BIT(0)
+#define MACREG_H2ARIC_BIT_DOOR_BELL         BIT(1)
+#define MACREG_H2ARIC_BIT_PS                BIT(2)
+#define MACREG_H2ARIC_BIT_PSPOLL            BIT(3)
+#define ISR_RESET                           BIT(15)
+#define ISR_RESET_AP33                      BIT(26)
+
+/* Data descriptor related constants */
+#define EAGLE_RXD_CTRL_DRIVER_OWN           0x00
+#define EAGLE_RXD_CTRL_DMA_OWN              0x80
+
+#define EAGLE_RXD_STATUS_OK                 0x01
+
+#define EAGLE_TXD_STATUS_IDLE               0x00000000
+#define EAGLE_TXD_STATUS_OK                 0x00000001
+#define EAGLE_TXD_STATUS_FW_OWNED           0x80000000
+
+/* Antenna control */
+#define ANTENNA_TX_4_AUTO                   0
+#define ANTENNA_TX_2                        3
+#define ANTENNA_RX_4_AUTO                   0
+#define ANTENNA_RX_2                        2
+
+/* Band related constants */
+#define BAND_24_CHANNEL_NUM                 14
+#define BAND_24_RATE_NUM                    13
+#define BAND_50_CHANNEL_NUM                 24
+#define BAND_50_RATE_NUM                    8
+
+/* vif and station */
+#define NUM_WEP_KEYS                        4
+#define MWL_MAX_TID                         8
+#define MWL_AMSDU_SIZE_4K                   0
+#define MWL_AMSDU_SIZE_8K                   1
+
+/* power init */
+#define MWL_POWER_INIT_1                    1
+#define MWL_POWER_INIT_2                    2
+
+enum {
+	MWL8864 = 0,
+	MWL8897,
+	MWLUNKNOWN,
+};
+
+enum {
+	AP_MODE_11AC = 0x10,         /* generic 11ac indication mode */
+	AP_MODE_2_4GHZ_11AC_MIXED = 0x17,
+};
+
+enum {
+	AMPDU_NO_STREAM,
+	AMPDU_STREAM_NEW,
+	AMPDU_STREAM_IN_PROGRESS,
+	AMPDU_STREAM_ACTIVE,
+};
+
+struct mwl_chip_info {
+	const char *part_name;
+	const char *fw_image;
+	int antenna_tx;
+	int antenna_rx;
+};
+
+struct mwl_tx_pwr_tbl {
+	u8 channel;
+	u8 setcap;
+	u16 txantenna2;
+	u16 tx_power[SYSADPT_TX_POWER_LEVEL_TOTAL];
+	bool cdd;
+};
+
+struct mwl_hw_data {
+	u32 fw_release_num;          /* MajNbr:MinNbr:SubMin:PatchLevel */
+	u8 hw_version;               /* plain number indicating version */
+	u8 host_interface;           /* plain number of interface       */
+	u16 max_num_tx_desc;         /* max number of TX descriptors    */
+	u16 max_num_mc_addr;         /* max number multicast addresses  */
+	u16 num_antennas;            /* number antennas used            */
+	u16 region_code;             /* region (eg. 0x10 for USA FCC)   */
+	unsigned char mac_addr[ETH_ALEN]; /* well known -> AA:BB:CC:DD:EE:FF */
+};
+
+#define MWL_TX_RATE_FORMAT_MASK       0x00000003
+#define MWL_TX_RATE_BANDWIDTH_MASK    0x00000030
+#define MWL_TX_RATE_BANDWIDTH_SHIFT   4
+#define MWL_TX_RATE_SHORTGI_MASK      0x00000040
+#define MWL_TX_RATE_SHORTGI_SHIFT     6
+#define MWL_TX_RATE_RATEIDMCS_MASK    0x00007F00
+#define MWL_TX_RATE_RATEIDMCS_SHIFT   8
+
+struct mwl_tx_desc {
+	u8 data_rate;
+	u8 tx_priority;
+	__le16 qos_ctrl;
+	__le32 pkt_ptr;
+	__le16 pkt_len;
+	u8 dest_addr[ETH_ALEN];
+	__le32 pphys_next;
+	__le32 sap_pkt_info;
+	__le32 rate_info;
+	u8 type;
+	u8 xmit_control;     /* bit 0: use rateinfo, bit 1: disable ampdu */
+	__le16 reserved;
+	__le32 tcpack_sn;
+	__le32 tcpack_src_dst;
+	__le32 reserved1;
+	__le32 reserved2;
+	u8 reserved3[2];
+	u8 packet_info;
+	u8 packet_id;
+	__le16 packet_len_and_retry;
+	__le16 packet_rate_info;
+	__le32 reserved4;
+	__le32 status;
+} __packed;
+
+struct mwl_tx_hndl {
+	struct sk_buff *psk_buff;
+	struct mwl_tx_desc *pdesc;
+	struct mwl_tx_hndl *pnext;
+};
+
+#define MWL_RX_RATE_FORMAT_MASK       0x0007
+#define MWL_RX_RATE_NSS_MASK          0x0018
+#define MWL_RX_RATE_NSS_SHIFT         3
+#define MWL_RX_RATE_BW_MASK           0x0060
+#define MWL_RX_RATE_BW_SHIFT          5
+#define MWL_RX_RATE_GI_MASK           0x0080
+#define MWL_RX_RATE_GI_SHIFT          7
+#define MWL_RX_RATE_RT_MASK           0xFF00
+#define MWL_RX_RATE_RT_SHIFT          8
+
+struct mwl_rx_desc {
+	__le16 pkt_len;              /* total length of received data      */
+	__le16 rate;                 /* receive rate information           */
+	__le32 pphys_buff_data;      /* physical address of payload data   */
+	__le32 pphys_next;           /* physical address of next RX desc   */
+	__le16 qos_ctrl;             /* received QosCtrl field variable    */
+	__le16 ht_sig2;              /* like name states                   */
+	__le32 hw_rssi_info;
+	__le32 hw_noise_floor_info;
+	u8 noise_floor;
+	u8 reserved[3];
+	u8 rssi;                     /* received signal strengt indication */
+	u8 status;                   /* status field containing USED bit   */
+	u8 channel;                  /* channel this pkt was received on   */
+	u8 rx_control;               /* the control element of the desc    */
+	__le32 reserved1[3];
+} __packed;
+
+struct mwl_rx_hndl {
+	struct sk_buff *psk_buff;    /* associated sk_buff for Linux       */
+	struct mwl_rx_desc *pdesc;
+	struct mwl_rx_hndl *pnext;
+};
+
+struct mwl_desc_data {
+	dma_addr_t pphys_tx_ring;          /* ptr to first TX desc (phys.)    */
+	struct mwl_tx_desc *ptx_ring;      /* ptr to first TX desc (virt.)    */
+	struct mwl_tx_hndl *tx_hndl;
+	struct mwl_tx_hndl *pnext_tx_hndl; /* next TX handle that can be used */
+	struct mwl_tx_hndl *pstale_tx_hndl;/* the staled TX handle            */
+	dma_addr_t pphys_rx_ring;          /* ptr to first RX desc (phys.)    */
+	struct mwl_rx_desc *prx_ring;      /* ptr to first RX desc (virt.)    */
+	struct mwl_rx_hndl *rx_hndl;
+	struct mwl_rx_hndl *pnext_rx_hndl; /* next RX handle that can be used */
+	u32 wcb_base;                      /* FW base offset for registers    */
+	u32 rx_desc_write;                 /* FW descriptor write position    */
+	u32 rx_desc_read;                  /* FW descriptor read position     */
+	u32 rx_buf_size;                   /* length of the RX buffers        */
+};
+
+struct mwl_ampdu_stream {
+	struct ieee80211_sta *sta;
+	u8 tid;
+	u8 state;
+	u8 idx;
+};
+
+#ifdef CONFIG_DEBUG_FS
+#define MAC_REG_ADDR_PCI(offset)      ((priv->iobase1 + 0xA000) + offset)
+
+#define MWL_ACCESS_MAC                1
+#define MWL_ACCESS_RF                 2
+#define MWL_ACCESS_BBP                3
+#define MWL_ACCESS_CAU                4
+#define MWL_ACCESS_ADDR0              5
+#define MWL_ACCESS_ADDR1              6
+#define MWL_ACCESS_ADDR               7
+#endif
+
+struct mwl_priv {
+	struct ieee80211_hw *hw;
+	struct firmware *fw_ucode;
+	int chip_type;
+
+	struct device_node *dt_node;
+	struct device_node *pwr_node;
+	bool disable_2g;
+	bool disable_5g;
+	int antenna_tx;
+	int antenna_rx;
+
+	struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
+	bool cdd;
+	u16 txantenna2;
+	u8 powinited;
+	u16 max_tx_pow[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* max tx power (dBm) */
+	u16 target_powers[SYSADPT_TX_POWER_LEVEL_TOTAL]; /* target powers   */
+	u8 cal_tbl[200];
+
+	struct pci_dev *pdev;
+	void __iomem *iobase0; /* MEM Base Address Register 0  */
+	void __iomem *iobase1; /* MEM Base Address Register 1  */
+	u32 next_bar_num;
+
+	spinlock_t fwcmd_lock;       /* for firmware command         */
+	unsigned short *pcmd_buf;    /* pointer to CmdBuf (virtual)  */
+	dma_addr_t pphys_cmd_buf;    /* pointer to CmdBuf (physical) */
+	bool in_send_cmd;
+
+	int irq;
+	struct mwl_hw_data hw_data;  /* Adapter HW specific info     */
+
+	/* various descriptor data */
+	spinlock_t tx_desc_lock;     /* for tx descriptor data       */
+	spinlock_t rx_desc_lock;     /* for rx descriptor data       */
+	struct mwl_desc_data desc_data[SYSADPT_NUM_OF_DESC_DATA];
+	struct sk_buff_head txq[SYSADPT_NUM_OF_DESC_DATA];
+	struct sk_buff_head delay_q;
+	/* number of descriptors owned by fw at any one time */
+	int fw_desc_cnt[SYSADPT_NUM_OF_DESC_DATA];
+
+	struct tasklet_struct tx_task;
+	struct tasklet_struct rx_task;
+	struct tasklet_struct qe_task;
+	int txq_limit;
+	bool is_tx_schedule;
+	int recv_limit;
+	bool is_rx_schedule;
+	bool is_qe_schedule;
+	s8 noise;                    /* Most recently reported noise in dBm */
+	struct ieee80211_supported_band band_24;
+	struct ieee80211_channel channels_24[BAND_24_CHANNEL_NUM];
+	struct ieee80211_rate rates_24[BAND_24_RATE_NUM];
+	struct ieee80211_supported_band band_50;
+	struct ieee80211_channel channels_50[BAND_50_CHANNEL_NUM];
+	struct ieee80211_rate rates_50[BAND_50_RATE_NUM];
+
+	u32 ap_macids_supported;
+	u32 sta_macids_supported;
+	u32 macids_used;
+	spinlock_t vif_lock;         /* for private interface info   */
+	struct list_head vif_list;   /* List of interfaces.          */
+	u32 running_bsses;           /* bitmap of running BSSes      */
+
+	spinlock_t sta_lock;         /* for private sta info         */
+	struct list_head sta_list;   /* List of stations             */
+
+	bool radio_on;
+	bool radio_short_preamble;
+	bool wmm_enabled;
+	struct ieee80211_tx_queue_params wmm_params[SYSADPT_TX_WMM_QUEUES];
+
+	/* Ampdu stream information */
+	u8 num_ampdu_queues;
+	spinlock_t stream_lock;      /* for ampdu stream             */
+	struct mwl_ampdu_stream ampdu[SYSADPT_TX_AMPDU_QUEUES];
+	struct work_struct watchdog_ba_handle;
+
+	bool mfg_mode;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_phy;
+	u32 reg_type;
+	u32 reg_offset;
+	u32 reg_value;
+#endif
+};
+
+struct beacon_info {
+	bool valid;
+	u16 cap_info;
+	u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G];
+	u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G];
+	u8 ie_list_ht[148];
+	u8 ie_list_vht[24];
+	u8 *ie_wmm_ptr;
+	u8 *ie_rsn_ptr;
+	u8 *ie_rsn48_ptr;
+	u8 *ie_ht_ptr;
+	u8 *ie_vht_ptr;
+#ifdef CONFIG_MAC80211_MESH
+	u8 *ie_meshid_ptr;
+	u8 *ie_meshcfg_ptr;
+	u8 *ie_meshchsw_ptr;
+#endif
+	u8 ie_wmm_len;
+	u8 ie_rsn_len;
+	u8 ie_rsn48_len;
+	u8 ie_ht_len;
+	u8 ie_vht_len;
+#ifdef CONFIG_MAC80211_MESH
+	u8 ie_meshid_len;
+	u8 ie_meshcfg_len;
+	u8 ie_meshchsw_len;
+#endif
+};
+
+struct mwl_vif {
+	struct list_head list;
+	int macid;       /* Firmware macid for this vif.  */
+	u16 seqno;       /* Non AMPDU sequence number assigned by driver.  */
+	struct {         /* Saved WEP keys */
+		u8 enabled;
+		u8 key[sizeof(struct ieee80211_key_conf) + WLAN_KEY_LEN_WEP104];
+	} wep_key_conf[NUM_WEP_KEYS];
+	u8 bssid[ETH_ALEN];          /* BSSID */
+	u8 sta_mac[ETH_ALEN];        /* Station mac address */
+	/* A flag to indicate is HW crypto is enabled for this bssid */
+	bool is_hw_crypto_enabled;
+	/* Indicate if this is station mode */
+	struct beacon_info beacon_info;
+	u16 iv16;
+	u32 iv32;
+	s8 keyidx;
+};
+
+struct mwl_tx_info {
+	unsigned long start_time;
+	u32 pkts;
+};
+
+struct mwl_amsdu_frag {
+	struct sk_buff *skb;
+	u8 *cur_pos;
+	unsigned long jiffies;
+	u8 pad;
+	u8 num;
+};
+
+struct mwl_amsdu_ctrl {
+	struct mwl_amsdu_frag frag[SYSADPT_TX_WMM_QUEUES];
+	u8 cap;
+};
+
+struct mwl_sta {
+	struct list_head list;
+	bool is_mesh_node;
+	bool is_ampdu_allowed;
+	struct mwl_tx_info tx_stats[MWL_MAX_TID];
+	bool is_amsdu_allowed;
+	spinlock_t amsdu_lock;      /* for amsdu aggregation       */
+	struct mwl_amsdu_ctrl amsdu_ctrl;
+	u16 iv16;
+	u32 iv32;
+};
+
+/* DMA header used by firmware and hardware. */
+struct mwl_dma_data {
+	__le16 fwlen;
+	struct ieee80211_hdr wh;
+	char data[0];
+} __packed;
+
+/* Transmission information to transmit a socket buffer. */
+struct mwl_tx_ctrl {
+	void *vif;
+	void *sta;
+	void *k_conf;
+	void *amsdu_pkts;
+	u8 tx_priority;
+	u8 type;
+	u16 qos_ctrl;
+	u8 xmit_control;
+};
+
+static inline struct mwl_vif *mwl_dev_get_vif(const struct ieee80211_vif *vif)
+{
+	return (struct mwl_vif *)&vif->drv_priv;
+}
+
+static inline struct mwl_sta *mwl_dev_get_sta(const struct ieee80211_sta *sta)
+{
+	return (struct mwl_sta *)&sta->drv_priv;
+}
+
+/* Defined in mac80211.c. */
+extern const struct ieee80211_ops mwl_mac80211_ops;
+
+#endif /* _dev_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwcmd.c b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
new file mode 100644
index 0000000..93c0a03
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwcmd.c
@@ -0,0 +1,2421 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements firmware host command related
+ * functions.
+ */
+
+#include <linux/etherdevice.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "hostcmd.h"
+
+#define MAX_WAIT_FW_COMPLETE_ITERATIONS         2000
+#define MAX_WAIT_GET_HW_SPECS_ITERATONS         3
+
+struct cmd_header {
+	__le16 command;
+	__le16 len;
+} __packed;
+
+static bool mwl_fwcmd_chk_adapter(struct mwl_priv *priv)
+{
+	u32 regval;
+
+	regval = readl(priv->iobase1 + MACREG_REG_INT_CODE);
+
+	if (regval == 0xffffffff) {
+		wiphy_err(priv->hw->wiphy, "adapter does not exist\n");
+		return false;
+	}
+
+	return true;
+}
+
+static void mwl_fwcmd_send_cmd(struct mwl_priv *priv)
+{
+	if (priv->mfg_mode) {
+		struct cmd_header *cmd_hdr =
+			(struct cmd_header *)&priv->pcmd_buf[2];
+		u16 len = le16_to_cpu(cmd_hdr->len);
+
+		writel(priv->pphys_cmd_buf, priv->iobase1 + 0xcd0);
+		writel(0x00, priv->iobase1 + 0xcd4);
+		writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
+		writel(len + 4, priv->iobase1 + 0xc40);
+	}
+	writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
+	writel(MACREG_H2ARIC_BIT_DOOR_BELL,
+	       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+static char *mwl_fwcmd_get_cmd_string(unsigned short cmd)
+{
+	int max_entries = 0;
+	int curr_cmd = 0;
+
+	static const struct {
+		u16 cmd;
+		char *cmd_string;
+	} cmds[] = {
+		{ HOSTCMD_CMD_GET_HW_SPEC, "GetHwSpecifications" },
+		{ HOSTCMD_CMD_SET_HW_SPEC, "SetHwSepcifications" },
+		{ HOSTCMD_CMD_802_11_GET_STAT, "80211GetStat" },
+		{ HOSTCMD_CMD_802_11_RADIO_CONTROL, "80211RadioControl" },
+		{ HOSTCMD_CMD_MEM_ADDR_ACCESS, "MEMAddrAccess" },
+		{ HOSTCMD_CMD_802_11_TX_POWER, "80211TxPower" },
+		{ HOSTCMD_CMD_802_11_RF_ANTENNA, "80211RfAntenna" },
+		{ HOSTCMD_CMD_BROADCAST_SSID_ENABLE, "broadcast_ssid_enable" },
+		{ HOSTCMD_CMD_SET_RF_CHANNEL, "SetRfChannel" },
+		{ HOSTCMD_CMD_SET_AID, "SetAid" },
+		{ HOSTCMD_CMD_SET_INFRA_MODE, "SetInfraMode" },
+		{ HOSTCMD_CMD_802_11_RTS_THSD, "80211RtsThreshold" },
+		{ HOSTCMD_CMD_SET_EDCA_PARAMS, "SetEDCAParams" },
+		{ HOSTCMD_CMD_SET_WMM_MODE, "SetWMMMode" },
+		{ HOSTCMD_CMD_HT_GUARD_INTERVAL, "HtGuardInterval" },
+		{ HOSTCMD_CMD_SET_FIXED_RATE, "SetFixedRate" },
+		{ HOSTCMD_CMD_SET_IES, "SetInformationElements" },
+		{ HOSTCMD_CMD_SET_LINKADAPT_CS_MODE, "LinkAdaptCsMode" },
+		{ HOSTCMD_CMD_SET_MAC_ADDR, "SetMacAddr" },
+		{ HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" },
+		{ HOSTCMD_CMD_GET_WATCHDOG_BITMAP, "GetWatchdogBitMap" },
+		{ HOSTCMD_CMD_DEL_MAC_ADDR, "DelMacAddr" },
+		{ HOSTCMD_CMD_BSS_START, "BssStart" },
+		{ HOSTCMD_CMD_AP_BEACON, "SetApBeacon" },
+		{ HOSTCMD_CMD_SET_NEW_STN, "SetNewStation" },
+		{ HOSTCMD_CMD_SET_APMODE, "SetApMode" },
+		{ HOSTCMD_CMD_UPDATE_ENCRYPTION, "UpdateEncryption" },
+		{ HOSTCMD_CMD_BASTREAM, "BAStream" },
+		{ HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL, "SetOptimizationLevel"},
+		{ HOSTCMD_CMD_DWDS_ENABLE, "DwdsEnable" },
+		{ HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" },
+		{ HOSTCMD_CMD_SET_CDD, "SetCDD" },
+	};
+
+	max_entries = ARRAY_SIZE(cmds);
+
+	for (curr_cmd = 0; curr_cmd < max_entries; curr_cmd++)
+		if ((cmd & 0x7fff) == cmds[curr_cmd].cmd)
+			return cmds[curr_cmd].cmd_string;
+
+	return "unknown";
+}
+
+static int mwl_fwcmd_wait_complete(struct mwl_priv *priv, unsigned short cmd)
+{
+	unsigned int curr_iteration = MAX_WAIT_FW_COMPLETE_ITERATIONS;
+	unsigned short int_code = 0;
+
+	do {
+		if (priv->mfg_mode)
+			int_code = le16_to_cpu(*((__le16 *)&priv->pcmd_buf[2]));
+		else
+			int_code = le16_to_cpu(*((__le16 *)&priv->pcmd_buf[0]));
+		mdelay(1);
+	} while ((int_code != cmd) && (--curr_iteration));
+
+	if (curr_iteration == 0) {
+		wiphy_err(priv->hw->wiphy, "cmd 0x%04x=%s timed out\n",
+			  cmd, mwl_fwcmd_get_cmd_string(cmd));
+		wiphy_err(priv->hw->wiphy, "return code: 0x%04x\n", int_code);
+		return -EIO;
+	}
+
+	mdelay(3);
+
+	return 0;
+}
+
+static int mwl_fwcmd_exec_cmd(struct mwl_priv *priv, unsigned short cmd)
+{
+	bool busy = false;
+
+	if (!mwl_fwcmd_chk_adapter(priv)) {
+		wiphy_err(priv->hw->wiphy, "adapter does not exist\n");
+		priv->in_send_cmd = false;
+		return -EIO;
+	}
+
+	if (!priv->in_send_cmd) {
+		priv->in_send_cmd = true;
+		mwl_fwcmd_send_cmd(priv);
+		if (mwl_fwcmd_wait_complete(priv, 0x8000 | cmd)) {
+			wiphy_err(priv->hw->wiphy, "timeout: 0x%04x\n", cmd);
+			priv->in_send_cmd = false;
+			return -EIO;
+		}
+	} else {
+		wiphy_warn(priv->hw->wiphy,
+			   "previous command is still running\n");
+		busy = true;
+	}
+
+	if (!busy)
+		priv->in_send_cmd = false;
+
+	return 0;
+}
+
+static int mwl_fwcmd_802_11_radio_control(struct mwl_priv *priv,
+					  bool enable, bool force)
+{
+	struct hostcmd_cmd_802_11_radio_control *pcmd;
+
+	if (enable == priv->radio_on && !force)
+		return 0;
+
+	pcmd = (struct hostcmd_cmd_802_11_radio_control *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RADIO_CONTROL);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action = cpu_to_le16(WL_SET);
+	pcmd->control = cpu_to_le16(priv->radio_short_preamble ?
+		WL_AUTO_PREAMBLE : WL_LONG_PREAMBLE);
+	pcmd->radio_on = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RADIO_CONTROL)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	priv->radio_on = enable;
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+static int mwl_fwcmd_get_tx_powers(struct mwl_priv *priv, u16 *powlist, u16 ch,
+				   u16 band, u16 width, u16 sub_ch)
+{
+	struct hostcmd_cmd_802_11_tx_power *pcmd;
+	int i;
+
+	pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_GET_LIST);
+	pcmd->ch = cpu_to_le16(ch);
+	pcmd->bw = cpu_to_le16(width);
+	pcmd->band = cpu_to_le16(band);
+	pcmd->sub_ch = cpu_to_le16(sub_ch);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_TX_POWER)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++)
+		powlist[i] = le16_to_cpu(pcmd->power_level_list[i]);
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+static int mwl_fwcmd_set_tx_powers(struct mwl_priv *priv, u16 txpow[],
+				   u8 action, u16 ch, u16 band,
+				   u16 width, u16 sub_ch)
+{
+	struct hostcmd_cmd_802_11_tx_power *pcmd;
+	int i;
+
+	pcmd = (struct hostcmd_cmd_802_11_tx_power *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_TX_POWER);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action = cpu_to_le16(action);
+	pcmd->ch = cpu_to_le16(ch);
+	pcmd->bw = cpu_to_le16(width);
+	pcmd->band = cpu_to_le16(band);
+	pcmd->sub_ch = cpu_to_le16(sub_ch);
+
+	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++)
+		pcmd->power_level_list[i] = cpu_to_le16(txpow[i]);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_TX_POWER)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+static u8 mwl_fwcmd_get_80m_pri_chnl_offset(u8 channel)
+{
+	u8 act_primary = ACT_PRIMARY_CHAN_0;
+
+	switch (channel) {
+	case 36:
+		act_primary = ACT_PRIMARY_CHAN_0;
+		break;
+	case 40:
+		act_primary = ACT_PRIMARY_CHAN_1;
+		break;
+	case 44:
+		act_primary = ACT_PRIMARY_CHAN_2;
+		break;
+	case 48:
+		act_primary = ACT_PRIMARY_CHAN_3;
+		break;
+	case 52:
+		act_primary = ACT_PRIMARY_CHAN_0;
+		break;
+	case 56:
+		act_primary = ACT_PRIMARY_CHAN_1;
+		break;
+	case 60:
+		act_primary = ACT_PRIMARY_CHAN_2;
+		break;
+	case 64:
+		act_primary = ACT_PRIMARY_CHAN_3;
+		break;
+	case 100:
+		act_primary = ACT_PRIMARY_CHAN_0;
+		break;
+	case 104:
+		act_primary = ACT_PRIMARY_CHAN_1;
+		break;
+	case 108:
+		act_primary = ACT_PRIMARY_CHAN_2;
+		break;
+	case 112:
+		act_primary = ACT_PRIMARY_CHAN_3;
+		break;
+	case 116:
+		act_primary = ACT_PRIMARY_CHAN_0;
+		break;
+	case 120:
+		act_primary = ACT_PRIMARY_CHAN_1;
+		break;
+	case 124:
+		act_primary = ACT_PRIMARY_CHAN_2;
+		break;
+	case 128:
+		act_primary = ACT_PRIMARY_CHAN_3;
+		break;
+	case 132:
+		act_primary = ACT_PRIMARY_CHAN_0;
+		break;
+	case 136:
+		act_primary = ACT_PRIMARY_CHAN_1;
+		break;
+	case 140:
+		act_primary = ACT_PRIMARY_CHAN_2;
+		break;
+	case 144:
+		act_primary = ACT_PRIMARY_CHAN_3;
+		break;
+	case 149:
+		act_primary = ACT_PRIMARY_CHAN_0;
+		break;
+	case 153:
+		act_primary = ACT_PRIMARY_CHAN_1;
+		break;
+	case 157:
+		act_primary = ACT_PRIMARY_CHAN_2;
+		break;
+	case 161:
+		act_primary = ACT_PRIMARY_CHAN_3;
+		break;
+	}
+
+	return act_primary;
+}
+
+static void mwl_fwcmd_parse_beacon(struct mwl_priv *priv,
+				   struct mwl_vif *vif, u8 *beacon, int len)
+{
+	struct ieee80211_mgmt *mgmt;
+	struct beacon_info *beacon_info;
+	int baselen;
+	u8 *pos;
+	size_t left;
+	bool elem_parse_failed;
+
+	mgmt = (struct ieee80211_mgmt *)beacon;
+
+	baselen = (u8 *)mgmt->u.beacon.variable - (u8 *)mgmt;
+	if (baselen > len)
+		return;
+
+	beacon_info = &vif->beacon_info;
+	memset(beacon_info, 0, sizeof(struct beacon_info));
+	beacon_info->valid = false;
+	beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
+	beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
+
+	beacon_info->cap_info = le16_to_cpu(mgmt->u.beacon.capab_info);
+
+	pos = (u8 *)mgmt->u.beacon.variable;
+	left = len - baselen;
+
+	elem_parse_failed = false;
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		left -= 2;
+
+		if (elen > left) {
+			elem_parse_failed = true;
+			break;
+		}
+
+		switch (id) {
+		case WLAN_EID_SUPP_RATES:
+		case WLAN_EID_EXT_SUPP_RATES:
+			{
+			int idx, bi, oi;
+			u8 rate;
+
+			for (bi = 0; bi < SYSADPT_MAX_DATA_RATES_G;
+			     bi++) {
+				if (beacon_info->b_rate_set[bi] == 0)
+					break;
+			}
+
+			for (oi = 0; oi < SYSADPT_MAX_DATA_RATES_G;
+			     oi++) {
+				if (beacon_info->op_rate_set[oi] == 0)
+					break;
+			}
+
+			for (idx = 0; idx < elen; idx++) {
+				rate = pos[idx];
+				if ((rate & 0x80) != 0) {
+					if (bi < SYSADPT_MAX_DATA_RATES_G)
+						beacon_info->b_rate_set[bi++]
+							= rate & 0x7f;
+					else {
+						elem_parse_failed = true;
+						break;
+					}
+				}
+				if (oi < SYSADPT_MAX_DATA_RATES_G)
+					beacon_info->op_rate_set[oi++] =
+						rate & 0x7f;
+				else {
+					elem_parse_failed = true;
+					break;
+				}
+			}
+			}
+			break;
+		case WLAN_EID_RSN:
+			beacon_info->ie_rsn48_len = (elen + 2);
+			beacon_info->ie_rsn48_ptr = (pos - 2);
+			break;
+		case WLAN_EID_HT_CAPABILITY:
+		case WLAN_EID_HT_OPERATION:
+		case WLAN_EID_OVERLAP_BSS_SCAN_PARAM:
+		case WLAN_EID_EXT_CAPABILITY:
+			beacon_info->ie_ht_len += (elen + 2);
+			if (beacon_info->ie_ht_len >
+			    sizeof(beacon_info->ie_list_ht)) {
+				elem_parse_failed = true;
+			} else {
+				*beacon_info->ie_ht_ptr++ = id;
+				*beacon_info->ie_ht_ptr++ = elen;
+				memcpy(beacon_info->ie_ht_ptr, pos, elen);
+				beacon_info->ie_ht_ptr += elen;
+			}
+			break;
+#ifdef CONFIG_MAC80211_MESH
+		case WLAN_EID_MESH_CONFIG:
+			beacon_info->ie_meshcfg_len = (elen + 2);
+			beacon_info->ie_meshcfg_ptr = (pos - 2);
+			break;
+		case WLAN_EID_MESH_ID:
+			beacon_info->ie_meshid_len = (elen + 2);
+			beacon_info->ie_meshid_ptr = (pos - 2);
+			break;
+		case WLAN_EID_CHAN_SWITCH_PARAM:
+			beacon_info->ie_meshchsw_len = (elen + 2);
+			beacon_info->ie_meshchsw_ptr = (pos - 2);
+			break;
+#endif
+		case WLAN_EID_VHT_CAPABILITY:
+		case WLAN_EID_VHT_OPERATION:
+		case WLAN_EID_OPMODE_NOTIF:
+			beacon_info->ie_vht_len += (elen + 2);
+			if (beacon_info->ie_vht_len >
+			    sizeof(beacon_info->ie_list_vht)) {
+				elem_parse_failed = true;
+			} else {
+				*beacon_info->ie_vht_ptr++ = id;
+				*beacon_info->ie_vht_ptr++ = elen;
+				memcpy(beacon_info->ie_vht_ptr, pos, elen);
+				beacon_info->ie_vht_ptr += elen;
+			}
+			break;
+		case WLAN_EID_VENDOR_SPECIFIC:
+			if ((pos[0] == 0x00) && (pos[1] == 0x50) &&
+			    (pos[2] == 0xf2)) {
+				if (pos[3] == 0x01) {
+					beacon_info->ie_rsn_len = (elen + 2);
+					beacon_info->ie_rsn_ptr = (pos - 2);
+				}
+
+				if (pos[3] == 0x02) {
+					beacon_info->ie_wmm_len = (elen + 2);
+					beacon_info->ie_wmm_ptr = (pos - 2);
+				}
+			}
+			break;
+		default:
+			break;
+		}
+
+		left -= elen;
+		pos += elen;
+	}
+
+	if (!elem_parse_failed) {
+		beacon_info->ie_ht_ptr = &beacon_info->ie_list_ht[0];
+		beacon_info->ie_vht_ptr = &beacon_info->ie_list_vht[0];
+		beacon_info->valid = true;
+	}
+}
+
+static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif)
+{
+	struct hostcmd_cmd_set_ies *pcmd;
+	struct beacon_info *beacon = &mwl_vif->beacon_info;
+	u16 ie_list_len_proprietary = 0;
+
+	if (!beacon->valid)
+		return -EINVAL;
+
+	if (beacon->ie_ht_len > sizeof(pcmd->ie_list_ht))
+		goto einval;
+
+	if (beacon->ie_vht_len > sizeof(pcmd->ie_list_vht))
+		goto einval;
+
+	pcmd = (struct hostcmd_cmd_set_ies *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_IES);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	pcmd->action = cpu_to_le16(HOSTCMD_ACT_GEN_SET);
+
+	memcpy(pcmd->ie_list_ht, beacon->ie_ht_ptr, beacon->ie_ht_len);
+	pcmd->ie_list_len_ht = cpu_to_le16(beacon->ie_ht_len);
+
+	memcpy(pcmd->ie_list_vht, beacon->ie_vht_ptr, beacon->ie_vht_len);
+	pcmd->ie_list_len_vht = cpu_to_le16(beacon->ie_vht_len);
+
+#ifdef CONFIG_MAC80211_MESH
+	memcpy(pcmd->ie_list_proprietary, beacon->ie_meshid_ptr,
+	       beacon->ie_meshid_len);
+	ie_list_len_proprietary = beacon->ie_meshid_len;
+	memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary,
+	       beacon->ie_meshcfg_ptr, beacon->ie_meshcfg_len);
+	ie_list_len_proprietary += beacon->ie_meshcfg_len;
+	memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary,
+	       beacon->ie_meshchsw_ptr, beacon->ie_meshchsw_len);
+	ie_list_len_proprietary += beacon->ie_meshchsw_len;
+#endif
+
+	if (priv->chip_type == MWL8897) {
+		memcpy(pcmd->ie_list_proprietary + ie_list_len_proprietary,
+		       beacon->ie_wmm_ptr, beacon->ie_wmm_len);
+		ie_list_len_proprietary += mwl_vif->beacon_info.ie_wmm_len;
+	}
+
+	pcmd->ie_list_len_proprietary = cpu_to_le16(ie_list_len_proprietary);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_IES)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+
+einval:
+
+	wiphy_err(priv->hw->wiphy, "length of IE is too long\n");
+
+	return -EINVAL;
+}
+
+static int mwl_fwcmd_set_ap_beacon(struct mwl_priv *priv,
+				   struct mwl_vif *mwl_vif,
+				   struct ieee80211_bss_conf *bss_conf)
+{
+	struct hostcmd_cmd_ap_beacon *pcmd;
+	struct ds_params *phy_ds_param_set;
+
+	if (!mwl_vif->beacon_info.valid)
+		return -EINVAL;
+
+	/* wmm structure of start command is defined less one byte,
+	 * due to following field country is not used, add byte one
+	 * to bypass the check.
+	 */
+	if (mwl_vif->beacon_info.ie_wmm_len >
+	    (sizeof(pcmd->start_cmd.wmm_param) + 1))
+		goto ielenerr;
+
+	if (mwl_vif->beacon_info.ie_rsn_len > sizeof(pcmd->start_cmd.rsn_ie))
+		goto ielenerr;
+
+	if (mwl_vif->beacon_info.ie_rsn48_len >
+	    sizeof(pcmd->start_cmd.rsn48_ie))
+		goto ielenerr;
+
+	pcmd = (struct hostcmd_cmd_ap_beacon *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_AP_BEACON);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	ether_addr_copy(pcmd->start_cmd.sta_mac_addr, mwl_vif->bssid);
+	memcpy(pcmd->start_cmd.ssid, bss_conf->ssid, bss_conf->ssid_len);
+	pcmd->start_cmd.bss_type = 1;
+	pcmd->start_cmd.bcn_period  = cpu_to_le16(bss_conf->beacon_int);
+	pcmd->start_cmd.dtim_period = bss_conf->dtim_period; /* 8bit */
+
+	phy_ds_param_set = &pcmd->start_cmd.phy_param_set.ds_param_set;
+	phy_ds_param_set->elem_id = WLAN_EID_DS_PARAMS;
+	phy_ds_param_set->len = sizeof(phy_ds_param_set->current_chnl);
+	phy_ds_param_set->current_chnl = bss_conf->chandef.chan->hw_value;
+
+	pcmd->start_cmd.probe_delay = cpu_to_le16(10);
+	pcmd->start_cmd.cap_info = cpu_to_le16(mwl_vif->beacon_info.cap_info);
+
+	memcpy(&pcmd->start_cmd.wmm_param, mwl_vif->beacon_info.ie_wmm_ptr,
+	       mwl_vif->beacon_info.ie_wmm_len);
+
+	memcpy(&pcmd->start_cmd.rsn_ie, mwl_vif->beacon_info.ie_rsn_ptr,
+	       mwl_vif->beacon_info.ie_rsn_len);
+
+	memcpy(&pcmd->start_cmd.rsn48_ie, mwl_vif->beacon_info.ie_rsn48_ptr,
+	       mwl_vif->beacon_info.ie_rsn48_len);
+
+	memcpy(pcmd->start_cmd.b_rate_set, mwl_vif->beacon_info.b_rate_set,
+	       SYSADPT_MAX_DATA_RATES_G);
+
+	memcpy(pcmd->start_cmd.op_rate_set, mwl_vif->beacon_info.op_rate_set,
+	       SYSADPT_MAX_DATA_RATES_G);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_AP_BEACON)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+
+ielenerr:
+
+	wiphy_err(priv->hw->wiphy, "length of IE is too long\n");
+
+	return -EINVAL;
+}
+
+static int mwl_fwcmd_encryption_set_cmd_info(struct hostcmd_cmd_set_key *cmd,
+					     u8 *addr,
+					     struct ieee80211_key_conf *key)
+{
+	cmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+	cmd->cmd_hdr.len = cpu_to_le16(sizeof(*cmd));
+	cmd->key_param.length = cpu_to_le16(sizeof(*cmd) -
+		offsetof(struct hostcmd_cmd_set_key, key_param));
+	cmd->key_param.key_index = cpu_to_le32(key->keyidx);
+	cmd->key_param.key_len = cpu_to_le16(key->keylen);
+	ether_addr_copy(cmd->key_param.mac_addr, addr);
+
+	switch (key->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_WEP);
+		if (key->keyidx == 0)
+			cmd->key_param.key_info =
+				cpu_to_le32(ENCR_KEY_FLAG_WEP_TXKEY);
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_TKIP);
+		cmd->key_param.key_info =
+			(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+			cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) :
+			cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY);
+		cmd->key_param.key_info |=
+			cpu_to_le32(ENCR_KEY_FLAG_MICKEY_VALID |
+				      ENCR_KEY_FLAG_TSC_VALID);
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		cmd->key_param.key_type_id = cpu_to_le16(KEY_TYPE_ID_AES);
+		cmd->key_param.key_info =
+			(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
+			cpu_to_le32(ENCR_KEY_FLAG_PAIRWISE) :
+			cpu_to_le32(ENCR_KEY_FLAG_TXGROUPKEY);
+		break;
+	default:
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+void mwl_fwcmd_reset(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	if (mwl_fwcmd_chk_adapter(priv))
+		writel(ISR_RESET,
+		       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+void mwl_fwcmd_int_enable(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	if (mwl_fwcmd_chk_adapter(priv)) {
+		writel(0x00,
+		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+		writel((MACREG_A2HRIC_BIT_MASK),
+		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+	}
+}
+
+void mwl_fwcmd_int_disable(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	if (mwl_fwcmd_chk_adapter(priv))
+		writel(0x00,
+		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+}
+
+int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_get_hw_spec *pcmd;
+	int retry;
+	int i;
+
+	if (priv->mfg_mode)
+		return 0;
+
+	pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd);
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	eth_broadcast_addr(pcmd->permanent_addr);
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_HW_SPEC);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->fw_awake_cookie = cpu_to_le32(priv->pphys_cmd_buf + 2048);
+
+	retry = 0;
+	while (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_HW_SPEC)) {
+		if (retry++ > MAX_WAIT_GET_HW_SPECS_ITERATONS) {
+			wiphy_err(hw->wiphy, "can't get hw specs\n");
+			spin_unlock_bh(&priv->fwcmd_lock);
+			return -EIO;
+		}
+
+		mdelay(1000);
+		wiphy_debug(hw->wiphy,
+			    "repeat command = %p\n", pcmd);
+	}
+
+	ether_addr_copy(&priv->hw_data.mac_addr[0], pcmd->permanent_addr);
+	priv->desc_data[0].wcb_base =
+		le32_to_cpu(pcmd->wcb_base0) & 0x0000ffff;
+	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+		priv->desc_data[i].wcb_base =
+			le32_to_cpu(pcmd->wcb_base[i - 1]) & 0x0000ffff;
+	priv->desc_data[0].rx_desc_read =
+		le32_to_cpu(pcmd->rxpd_rd_ptr) & 0x0000ffff;
+	priv->desc_data[0].rx_desc_write =
+		le32_to_cpu(pcmd->rxpd_wr_ptr) & 0x0000ffff;
+	priv->hw_data.region_code = le16_to_cpu(pcmd->region_code) & 0x00ff;
+	priv->hw_data.fw_release_num = le32_to_cpu(pcmd->fw_release_num);
+	priv->hw_data.max_num_tx_desc = le16_to_cpu(pcmd->num_wcb);
+	priv->hw_data.max_num_mc_addr = le16_to_cpu(pcmd->num_mcast_addr);
+	priv->hw_data.num_antennas = le16_to_cpu(pcmd->num_antenna);
+	priv->hw_data.hw_version = pcmd->version;
+	priv->hw_data.host_interface = pcmd->host_if;
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_hw_spec *pcmd;
+	int i;
+
+	if (priv->mfg_mode)
+		return 0;
+
+	pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_HW_SPEC);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->wcb_base[0] = cpu_to_le32(priv->desc_data[0].pphys_tx_ring);
+	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+		pcmd->wcb_base[i] =
+			cpu_to_le32(priv->desc_data[i].pphys_tx_ring);
+	pcmd->tx_wcb_num_per_queue = cpu_to_le32(SYSADPT_MAX_NUM_TX_DESC);
+	pcmd->num_tx_queues = cpu_to_le32(SYSADPT_NUM_OF_DESC_DATA);
+	pcmd->total_rx_wcb = cpu_to_le32(SYSADPT_MAX_NUM_RX_DESC);
+	pcmd->rxpd_wr_ptr = cpu_to_le32(priv->desc_data[0].pphys_rx_ring);
+	pcmd->features = 0;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_HW_SPEC)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,
+		       struct ieee80211_low_level_stats *stats)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_802_11_get_stat *pcmd;
+
+	pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_GET_STAT);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_GET_STAT)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	stats->dot11ACKFailureCount =
+		le32_to_cpu(pcmd->ack_failures);
+	stats->dot11RTSFailureCount =
+		le32_to_cpu(pcmd->rts_failures);
+	stats->dot11FCSErrorCount =
+		le32_to_cpu(pcmd->rx_fcs_errors);
+	stats->dot11RTSSuccessCount =
+		le32_to_cpu(pcmd->rts_successes);
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw)
+{
+	return mwl_fwcmd_802_11_radio_control(hw->priv, true, false);
+}
+
+int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw)
+{
+	return mwl_fwcmd_802_11_radio_control(hw->priv, false, false);
+}
+
+int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble)
+{
+	struct mwl_priv *priv = hw->priv;
+	int rc;
+
+	if (priv->mfg_mode)
+		return 0;
+
+	priv->radio_short_preamble = short_preamble;
+	rc = mwl_fwcmd_802_11_radio_control(priv, true, true);
+
+	return rc;
+}
+
+int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len,
+			     u32 *val, u16 set)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_mem_addr_access *pcmd;
+	int i;
+
+	pcmd = (struct hostcmd_cmd_mem_addr_access *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_MEM_ADDR_ACCESS);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->address = cpu_to_le32(addr);
+	pcmd->length = cpu_to_le16(len);
+	pcmd->value[0] = cpu_to_le32(*val);
+	pcmd->reserved = cpu_to_le16(set);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_MEM_ADDR_ACCESS)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	for (i = 0; i < len; i++)
+		val[i] = le32_to_cpu(pcmd->value[i]);
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw,
+			   struct ieee80211_conf *conf, u8 fraction)
+{
+	struct ieee80211_channel *channel = conf->chandef.chan;
+	struct mwl_priv *priv = hw->priv;
+	int reduce_val = 0;
+	u16 band = 0, width = 0, sub_ch = 0;
+	u16 maxtxpow[SYSADPT_TX_POWER_LEVEL_TOTAL];
+	int i, tmp;
+	int rc;
+
+	switch (fraction) {
+	case 0:
+		reduce_val = 0;    /* Max */
+		break;
+	case 1:
+		reduce_val = 2;    /* 75% -1.25db */
+		break;
+	case 2:
+		reduce_val = 3;    /* 50% -3db */
+		break;
+	case 3:
+		reduce_val = 6;    /* 25% -6db */
+		break;
+	default:
+		/* larger than case 3,  pCmd->MaxPowerLevel is min */
+		reduce_val = 0xff;
+		break;
+	}
+
+	if (channel->band == IEEE80211_BAND_2GHZ)
+		band = FREQ_BAND_2DOT4GHZ;
+	else if (channel->band == IEEE80211_BAND_5GHZ)
+		band = FREQ_BAND_5GHZ;
+
+	switch (conf->chandef.width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+		width = CH_20_MHZ_WIDTH;
+		sub_ch = NO_EXT_CHANNEL;
+		break;
+	case NL80211_CHAN_WIDTH_40:
+		width = CH_40_MHZ_WIDTH;
+		if (conf->chandef.center_freq1 > channel->center_freq)
+			sub_ch = EXT_CH_ABOVE_CTRL_CH;
+		else
+			sub_ch = EXT_CH_BELOW_CTRL_CH;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+		width = CH_80_MHZ_WIDTH;
+		if (conf->chandef.center_freq1 > channel->center_freq)
+			sub_ch = EXT_CH_ABOVE_CTRL_CH;
+		else
+			sub_ch = EXT_CH_BELOW_CTRL_CH;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if ((priv->powinited & MWL_POWER_INIT_2) == 0) {
+		mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow,
+					channel->hw_value, band, width, sub_ch);
+		priv->powinited |= MWL_POWER_INIT_2;
+	}
+
+	if ((priv->powinited & MWL_POWER_INIT_1) == 0) {
+		mwl_fwcmd_get_tx_powers(priv, priv->target_powers,
+					channel->hw_value, band, width, sub_ch);
+		priv->powinited |= MWL_POWER_INIT_1;
+	}
+
+	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
+		if (priv->target_powers[i] > priv->max_tx_pow[i])
+			tmp = priv->max_tx_pow[i];
+		else
+			tmp = priv->target_powers[i];
+		maxtxpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0;
+	}
+
+	rc = mwl_fwcmd_set_tx_powers(priv, maxtxpow, HOSTCMD_ACT_GEN_SET,
+				     channel->hw_value, band, width, sub_ch);
+
+	return rc;
+}
+
+int mwl_fwcmd_tx_power(struct ieee80211_hw *hw,
+		       struct ieee80211_conf *conf, u8 fraction)
+{
+	struct ieee80211_channel *channel = conf->chandef.chan;
+	struct mwl_priv *priv = hw->priv;
+	int reduce_val = 0;
+	u16 band = 0, width = 0, sub_ch = 0;
+	u16 txpow[SYSADPT_TX_POWER_LEVEL_TOTAL];
+	int index, found = 0;
+	int i, tmp;
+	int rc;
+
+	switch (fraction) {
+	case 0:
+		reduce_val = 0;    /* Max */
+		break;
+	case 1:
+		reduce_val = 2;    /* 75% -1.25db */
+		break;
+	case 2:
+		reduce_val = 3;    /* 50% -3db */
+		break;
+	case 3:
+		reduce_val = 6;    /* 25% -6db */
+		break;
+	default:
+		/* larger than case 3,  pCmd->MaxPowerLevel is min */
+		reduce_val = 0xff;
+		break;
+	}
+
+	if (channel->band == IEEE80211_BAND_2GHZ)
+		band = FREQ_BAND_2DOT4GHZ;
+	else if (channel->band == IEEE80211_BAND_5GHZ)
+		band = FREQ_BAND_5GHZ;
+
+	switch (conf->chandef.width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+		width = CH_20_MHZ_WIDTH;
+		sub_ch = NO_EXT_CHANNEL;
+		break;
+	case NL80211_CHAN_WIDTH_40:
+		width = CH_40_MHZ_WIDTH;
+		if (conf->chandef.center_freq1 > channel->center_freq)
+			sub_ch = EXT_CH_ABOVE_CTRL_CH;
+		else
+			sub_ch = EXT_CH_BELOW_CTRL_CH;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+		width = CH_80_MHZ_WIDTH;
+		if (conf->chandef.center_freq1 > channel->center_freq)
+			sub_ch = EXT_CH_ABOVE_CTRL_CH;
+		else
+			sub_ch = EXT_CH_BELOW_CTRL_CH;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* search tx power table if exist */
+	for (index = 0; index < SYSADPT_MAX_NUM_CHANNELS; index++) {
+		struct mwl_tx_pwr_tbl *tx_pwr;
+
+		tx_pwr = &priv->tx_pwr_tbl[index];
+
+		/* do nothing if table is not loaded */
+		if (tx_pwr->channel == 0)
+			break;
+
+		if (tx_pwr->channel == channel->hw_value) {
+			priv->cdd = tx_pwr->cdd;
+			priv->txantenna2 = tx_pwr->txantenna2;
+
+			if (tx_pwr->setcap)
+				priv->powinited = MWL_POWER_INIT_1;
+			else
+				priv->powinited = MWL_POWER_INIT_2;
+
+			for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
+				if (tx_pwr->setcap)
+					priv->max_tx_pow[i] =
+						tx_pwr->tx_power[i];
+				else
+					priv->target_powers[i] =
+						tx_pwr->tx_power[i];
+			}
+
+			found = 1;
+			break;
+		}
+	}
+
+	if ((priv->powinited & MWL_POWER_INIT_2) == 0) {
+		mwl_fwcmd_get_tx_powers(priv, priv->max_tx_pow,
+					channel->hw_value, band, width, sub_ch);
+
+		priv->powinited |= MWL_POWER_INIT_2;
+	}
+
+	if ((priv->powinited & MWL_POWER_INIT_1) == 0) {
+		mwl_fwcmd_get_tx_powers(priv, priv->target_powers,
+					channel->hw_value, band, width, sub_ch);
+
+		priv->powinited |= MWL_POWER_INIT_1;
+	}
+
+	for (i = 0; i < SYSADPT_TX_POWER_LEVEL_TOTAL; i++) {
+		if (found) {
+			if ((priv->tx_pwr_tbl[index].setcap) &&
+			    (priv->tx_pwr_tbl[index].tx_power[i] >
+			    priv->max_tx_pow[i]))
+				tmp = priv->max_tx_pow[i];
+			else
+				tmp = priv->tx_pwr_tbl[index].tx_power[i];
+		} else {
+			if (priv->target_powers[i] > priv->max_tx_pow[i])
+				tmp = priv->max_tx_pow[i];
+			else
+				tmp = priv->target_powers[i];
+		}
+
+		txpow[i] = ((tmp - reduce_val) > 0) ? (tmp - reduce_val) : 0;
+	}
+
+	rc = mwl_fwcmd_set_tx_powers(priv, txpow, HOSTCMD_ACT_GEN_SET_LIST,
+				     channel->hw_value, band, width, sub_ch);
+
+	return rc;
+}
+
+int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_802_11_rf_antenna *pcmd;
+
+	if (priv->mfg_mode)
+		return 0;
+
+	pcmd = (struct hostcmd_cmd_802_11_rf_antenna *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RF_ANTENNA);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+	pcmd->action = cpu_to_le16(dir);
+
+	if (dir == WL_ANTENNATYPE_RX) {
+		u8 rx_antenna = 4; /* if auto, set 4 rx antennas in SC2 */
+
+		if (antenna != 0)
+			pcmd->antenna_mode = cpu_to_le16(antenna);
+		else
+			pcmd->antenna_mode = cpu_to_le16(rx_antenna);
+	} else {
+		u8 tx_antenna = 0xf; /* if auto, set 4 tx antennas in SC2 */
+
+		if (antenna != 0)
+			pcmd->antenna_mode = cpu_to_le16(antenna);
+		else
+			pcmd->antenna_mode = cpu_to_le16(tx_antenna);
+	}
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RF_ANTENNA)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif, bool enable)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_broadcast_ssid_enable *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_broadcast_ssid_enable *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BROADCAST_SSID_ENABLE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+	pcmd->enable = cpu_to_le32(enable);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BROADCAST_SSID_ENABLE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw,
+			     struct ieee80211_conf *conf)
+{
+	struct ieee80211_channel *channel = conf->chandef.chan;
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_rf_channel *pcmd;
+	u32 chnl_flags, freq_band, chnl_width, act_primary;
+
+	pcmd = (struct hostcmd_cmd_set_rf_channel *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RF_CHANNEL);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action = cpu_to_le16(WL_SET);
+	pcmd->curr_chnl = channel->hw_value;
+
+	if (channel->band == IEEE80211_BAND_2GHZ) {
+		freq_band = FREQ_BAND_2DOT4GHZ;
+	} else if (channel->band == IEEE80211_BAND_5GHZ) {
+		freq_band = FREQ_BAND_5GHZ;
+	} else {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		return -EINVAL;
+	}
+
+	switch (conf->chandef.width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+		chnl_width = CH_20_MHZ_WIDTH;
+		act_primary = ACT_PRIMARY_CHAN_0;
+		break;
+	case NL80211_CHAN_WIDTH_40:
+		chnl_width = CH_40_MHZ_WIDTH;
+		if (conf->chandef.center_freq1 > channel->center_freq)
+			act_primary = ACT_PRIMARY_CHAN_0;
+		else
+			act_primary = ACT_PRIMARY_CHAN_1;
+		break;
+	case NL80211_CHAN_WIDTH_80:
+		chnl_width = CH_80_MHZ_WIDTH;
+		act_primary =
+			mwl_fwcmd_get_80m_pri_chnl_offset(pcmd->curr_chnl);
+		break;
+	default:
+		spin_unlock_bh(&priv->fwcmd_lock);
+		return -EINVAL;
+	}
+
+	chnl_flags = (freq_band & FREQ_BAND_MASK) |
+		((chnl_width << CHNL_WIDTH_SHIFT) & CHNL_WIDTH_MASK) |
+		((act_primary << ACT_PRIMARY_SHIFT) & ACT_PRIMARY_MASK);
+
+	pcmd->chnl_flags = cpu_to_le32(chnl_flags);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_RF_CHANNEL)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_aid(struct ieee80211_hw *hw,
+		      struct ieee80211_vif *vif, u8 *bssid, u16 aid)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_aid *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_aid *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_AID);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+	pcmd->aid = cpu_to_le16(aid);
+	ether_addr_copy(pcmd->mac_addr, bssid);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_AID)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_infra_mode *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_infra_mode *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_INFRA_MODE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_INFRA_MODE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, int threshold)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_802_11_rts_thsd *pcmd;
+
+	pcmd = (struct hostcmd_cmd_802_11_rts_thsd *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_802_11_RTS_THSD);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action  = cpu_to_le16(WL_SET);
+	pcmd->threshold = cpu_to_le16(threshold);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_802_11_RTS_THSD)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index,
+			      u16 cw_min, u16 cw_max, u8 aifs, u16 txop)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_edca_params *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_edca_params *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_EDCA_PARAMS);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+	pcmd->action = cpu_to_le16(0xffff);
+	pcmd->txop = cpu_to_le16(txop);
+	pcmd->cw_max = cpu_to_le32(ilog2(cw_max + 1));
+	pcmd->cw_min = cpu_to_le32(ilog2(cw_min + 1));
+	pcmd->aifsn = aifs;
+	pcmd->txq_num = index;
+
+	/* The array index defined in qos.h has a reversed bk and be.
+	 * The HW queue was not used this way; the qos code needs to
+	 * be changed or checked
+	 */
+	if (index == 0)
+		pcmd->txq_num = 1;
+	else if (index == 1)
+		pcmd->txq_num = 0;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_EDCA_PARAMS)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_wmm_mode *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_wmm_mode *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_WMM_MODE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action = cpu_to_le16(enable ? WL_ENABLE : WL_DISABLE);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_WMM_MODE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_ht_guard_interval *pcmd;
+
+	pcmd = (struct hostcmd_cmd_ht_guard_interval *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_HT_GUARD_INTERVAL);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action = cpu_to_le32(WL_SET);
+	pcmd->gi_type = cpu_to_le32(gi_type);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_HT_GUARD_INTERVAL)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_fixed_rate *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_fixed_rate *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_FIXED_RATE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+	pcmd->action = cpu_to_le32(HOSTCMD_ACT_NOT_USE_FIXED_RATE);
+	pcmd->multicast_rate = mcast;
+	pcmd->management_rate = mgmt;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_FIXED_RATE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw, u16 cs_mode)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_linkadapt_cs_mode *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_linkadapt_cs_mode *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_LINKADAPT_CS_MODE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action  = cpu_to_le16(HOSTCMD_ACT_GEN_SET);
+	pcmd->cs_mode = cpu_to_le16(cs_mode);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_LINKADAPT_CS_MODE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_rate_adapt_mode *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_rate_adapt_mode *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_RATE_ADAPT_MODE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->action = cpu_to_le16(WL_SET);
+	pcmd->rate_adapt_mode = cpu_to_le16(mode);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_RATE_ADAPT_MODE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif, u8 *mac_addr)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_mac_addr *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_MAC_ADDR);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+	pcmd->mac_type = cpu_to_le16(WL_MAC_TYPE_SECONDARY_CLIENT);
+	ether_addr_copy(pcmd->mac_addr, mac_addr);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_MAC_ADDR)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_get_watchdog_bitmap *pcmd;
+
+	pcmd = (struct hostcmd_cmd_get_watchdog_bitmap *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_GET_WATCHDOG_BITMAP);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_GET_WATCHDOG_BITMAP)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	*bitmap = pcmd->watchdog_bitmap;
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif, u8 *mac_addr)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_mac_addr *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DEL_MAC_ADDR);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+	ether_addr_copy(pcmd->mac_addr, mac_addr);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_DEL_MAC_ADDR)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_bss_start(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif, bool enable)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_bss_start *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	if (enable && (priv->running_bsses & (1 << mwl_vif->macid)))
+		return 0;
+
+	if (!enable && !(priv->running_bsses & (1 << mwl_vif->macid)))
+		return 0;
+
+	pcmd = (struct hostcmd_cmd_bss_start *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BSS_START);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	if (enable) {
+		pcmd->enable = cpu_to_le32(WL_ENABLE);
+	} else {
+		if (mwl_vif->macid == 0)
+			pcmd->enable = cpu_to_le32(WL_DISABLE);
+		else
+			pcmd->enable = cpu_to_le32(WL_DISABLE_VMAC);
+	}
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BSS_START)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (enable)
+		priv->running_bsses |= (1 << mwl_vif->macid);
+	else
+		priv->running_bsses &= ~(1 << mwl_vif->macid);
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw,
+			 struct ieee80211_vif *vif, u8 *beacon, int len)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	mwl_fwcmd_parse_beacon(priv, mwl_vif, beacon, len);
+
+	if (mwl_fwcmd_set_ies(priv, mwl_vif))
+		goto err;
+
+	if (mwl_fwcmd_set_ap_beacon(priv, mwl_vif, &vif->bss_conf))
+		goto err;
+
+	mwl_vif->beacon_info.valid = false;
+
+	return 0;
+
+err:
+
+	mwl_vif->beacon_info.valid = false;
+
+	return -EIO;
+}
+
+int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif,
+			      struct ieee80211_sta *sta)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_new_stn *pcmd;
+	u32 rates;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD);
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		pcmd->aid = 0;
+		pcmd->stn_id = 0;
+	} else {
+		pcmd->aid = cpu_to_le16(sta->aid);
+		pcmd->stn_id = cpu_to_le16(sta->aid);
+	}
+	ether_addr_copy(pcmd->mac_addr, sta->addr);
+
+	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;
+	pcmd->peer_info.legacy_rate_bitmap = cpu_to_le32(rates);
+
+	if (sta->ht_cap.ht_supported) {
+		pcmd->peer_info.ht_rates[0] = sta->ht_cap.mcs.rx_mask[0];
+		pcmd->peer_info.ht_rates[1] = sta->ht_cap.mcs.rx_mask[1];
+		pcmd->peer_info.ht_rates[2] = sta->ht_cap.mcs.rx_mask[2];
+		pcmd->peer_info.ht_rates[3] = sta->ht_cap.mcs.rx_mask[3];
+		pcmd->peer_info.ht_cap_info = cpu_to_le16(sta->ht_cap.cap);
+		pcmd->peer_info.mac_ht_param_info =
+			(sta->ht_cap.ampdu_factor & 3) |
+			((sta->ht_cap.ampdu_density & 7) << 2);
+	}
+
+	if (sta->vht_cap.vht_supported) {
+		pcmd->peer_info.vht_max_rx_mcs =
+			cpu_to_le32(*((u32 *)
+			&sta->vht_cap.vht_mcs.rx_mcs_map));
+		pcmd->peer_info.vht_cap = cpu_to_le32(sta->vht_cap.cap);
+		pcmd->peer_info.vht_rx_channel_width = sta->bandwidth;
+	}
+
+	pcmd->is_qos_sta = sta->wme;
+	pcmd->qos_info = ((sta->uapsd_queues << 4) | (sta->max_sp << 1));
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+
+		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+			spin_unlock_bh(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_new_stn *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_ADD);
+	ether_addr_copy(pcmd->mac_addr, vif->addr);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif, u8 *addr)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_new_stn *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_NEW_STN);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	pcmd->action = cpu_to_le16(HOSTCMD_ACT_STA_ACTION_REMOVE);
+	ether_addr_copy(pcmd->mac_addr, addr);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+
+		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+			spin_unlock_bh(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_apmode *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_apmode *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_APMODE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->apmode = apmode;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_APMODE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       u8 *addr, u8 encr_type)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_update_encryption *pcmd;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_update_encryption *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	pcmd->action_type = cpu_to_le32(ENCR_ACTION_ENABLE_HW_ENCR);
+	ether_addr_copy(pcmd->mac_addr, addr);
+	pcmd->action_data[0] = encr_type;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		if (ether_addr_equal(mwl_vif->bssid, addr))
+			ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+		else
+			ether_addr_copy(pcmd->mac_addr, mwl_vif->bssid);
+
+		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+			spin_unlock_bh(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif, u8 *addr,
+				 struct ieee80211_key_conf *key)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_key *pcmd;
+	int rc;
+	int keymlen;
+	u32 action;
+	u8 idx;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key);
+	if (rc) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "encryption not support\n");
+		return rc;
+	}
+
+	idx = key->keyidx;
+
+	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
+		action = ENCR_ACTION_TYPE_SET_KEY;
+	else
+		action = ENCR_ACTION_TYPE_SET_GROUP_KEY;
+
+	switch (key->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+	case WLAN_CIPHER_SUITE_WEP104:
+		if (!mwl_vif->wep_key_conf[idx].enabled) {
+			memcpy(mwl_vif->wep_key_conf[idx].key, key,
+			       sizeof(*key) + key->keylen);
+			mwl_vif->wep_key_conf[idx].enabled = 1;
+		}
+
+		keymlen = key->keylen;
+		action = ENCR_ACTION_TYPE_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:
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "encryption not support\n");
+		return -ENOTSUPP;
+	}
+
+	memcpy((void *)&pcmd->key_param.key, key->key, keymlen);
+	pcmd->action_type = cpu_to_le32(action);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		if (ether_addr_equal(mwl_vif->bssid, addr))
+			ether_addr_copy(pcmd->key_param.mac_addr,
+					mwl_vif->sta_mac);
+		else
+			ether_addr_copy(pcmd->key_param.mac_addr,
+					mwl_vif->bssid);
+
+		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+			spin_unlock_bh(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif, u8 *addr,
+				    struct ieee80211_key_conf *key)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_key *pcmd;
+	int rc;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_UPDATE_ENCRYPTION);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+
+	rc = mwl_fwcmd_encryption_set_cmd_info(pcmd, addr, key);
+	if (rc) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "encryption not support\n");
+		return rc;
+	}
+
+	pcmd->action_type = cpu_to_le32(ENCR_ACTION_TYPE_REMOVE_KEY);
+
+	if (key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+	    key->cipher == WLAN_CIPHER_SUITE_WEP104)
+		mwl_vif->wep_key_conf[key->keyidx].enabled = 0;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_UPDATE_ENCRYPTION)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_check_ba(struct ieee80211_hw *hw,
+		       struct mwl_ampdu_stream *stream,
+		       struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_bastream *pcmd;
+	u32 ba_flags, ba_type, ba_direction;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+	pcmd->cmd_hdr.result = cpu_to_le16(0xffff);
+
+	pcmd->action_type = cpu_to_le32(BA_CHECK_STREAM);
+	ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0],
+			stream->sta->addr);
+	pcmd->ba_info.create_params.tid = stream->tid;
+	ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE;
+	ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM;
+	ba_flags = (ba_type & BA_TYPE_MASK) |
+		((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK);
+	pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags);
+	pcmd->ba_info.create_params.queue_id = stream->idx;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "check ba failed execution\n");
+		return -EIO;
+	}
+
+	if (pcmd->cmd_hdr.result != 0) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "check ba result error %d\n",
+			  le16_to_cpu(pcmd->cmd_hdr.result));
+		return -EINVAL;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_create_ba(struct ieee80211_hw *hw,
+			struct mwl_ampdu_stream *stream,
+			u8 buf_size, struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_bastream *pcmd;
+	u32 ba_flags, ba_type, ba_direction;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->cmd_hdr.macid = mwl_vif->macid;
+	pcmd->cmd_hdr.result = cpu_to_le16(0xffff);
+
+	pcmd->action_type = cpu_to_le32(BA_CREATE_STREAM);
+	pcmd->ba_info.create_params.bar_thrs = cpu_to_le32(buf_size);
+	pcmd->ba_info.create_params.window_size = cpu_to_le32(buf_size);
+	ether_addr_copy(&pcmd->ba_info.create_params.peer_mac_addr[0],
+			stream->sta->addr);
+	pcmd->ba_info.create_params.tid = stream->tid;
+	ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE;
+	ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM;
+	ba_flags = (ba_type & BA_TYPE_MASK) |
+		((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK);
+	pcmd->ba_info.create_params.flags = cpu_to_le32(ba_flags);
+	pcmd->ba_info.create_params.queue_id = stream->idx;
+	pcmd->ba_info.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);
+	pcmd->ba_info.create_params.reset_seq_no = 1;
+	pcmd->ba_info.create_params.current_seq = cpu_to_le16(0);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "create ba failed execution\n");
+		return -EIO;
+	}
+
+	if (pcmd->cmd_hdr.result != 0) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "create ba result error %d\n",
+			  le16_to_cpu(pcmd->cmd_hdr.result));
+		return -EINVAL;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw,
+			 u8 idx)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_bastream *pcmd;
+	u32 ba_flags, ba_type, ba_direction;
+
+	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_BASTREAM);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+
+	pcmd->action_type = cpu_to_le32(BA_DESTROY_STREAM);
+	ba_type = BASTREAM_FLAG_IMMEDIATE_TYPE;
+	ba_direction = BASTREAM_FLAG_DIRECTION_UPSTREAM;
+	ba_flags = (ba_type & BA_TYPE_MASK) |
+		((ba_direction << BA_DIRECTION_SHIFT) & BA_DIRECTION_MASK);
+	pcmd->ba_info.destroy_params.flags = cpu_to_le32(ba_flags);
+	pcmd->ba_info.destroy_params.fw_ba_context.context = cpu_to_le32(idx);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_BASTREAM)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "destroy ba failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+/* caller must hold priv->stream_lock when calling the stream functions */
+struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw,
+					      struct ieee80211_sta *sta,
+					      u8 tid)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_ampdu_stream *stream;
+	int i;
+
+	for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; 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;
+			return stream;
+		}
+	}
+
+	return NULL;
+}
+
+void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw,
+			       struct ieee80211_sta *sta)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_ampdu_stream *stream;
+	int i;
+
+	for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+		stream = &priv->ampdu[i];
+
+		if (stream->sta == sta) {
+			mwl_fwcmd_destroy_ba(hw, stream->idx);
+			mwl_fwcmd_remove_stream(hw, stream);
+		}
+	}
+}
+
+int mwl_fwcmd_start_stream(struct ieee80211_hw *hw,
+			   struct mwl_ampdu_stream *stream)
+{
+	/* if the stream has already been started, don't start it again */
+	if (stream->state != AMPDU_STREAM_NEW)
+		return 0;
+
+	return ieee80211_start_tx_ba_session(stream->sta, stream->tid, 0);
+}
+
+void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw,
+			     struct mwl_ampdu_stream *stream)
+{
+	memset(stream, 0, sizeof(*stream));
+}
+
+struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw,
+						 u8 *addr, u8 tid)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_ampdu_stream *stream;
+	int i;
+
+	for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+		stream = &priv->ampdu[i];
+
+		if (stream->state == AMPDU_NO_STREAM)
+			continue;
+
+		if (ether_addr_equal(stream->sta->addr, addr) &&
+		    stream->tid == tid)
+			return stream;
+	}
+
+	return NULL;
+}
+
+bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid)
+{
+	struct mwl_sta *sta_info;
+	struct mwl_tx_info *tx_stats;
+
+	if (WARN_ON(tid >= SYSADPT_MAX_TID))
+		return false;
+
+	sta_info = mwl_dev_get_sta(sta);
+
+	tx_stats = &sta_info->tx_stats[tid];
+
+	return (sta_info->is_ampdu_allowed &&
+		tx_stats->pkts > SYSADPT_AMPDU_PACKET_THRESHOLD);
+}
+
+int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_optimization_level *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_optimization_level *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->opt_level = opt_level;
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_dwds_enable *pcmd;
+
+	pcmd = (struct hostcmd_cmd_dwds_enable *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_DWDS_ENABLE);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->enable = cpu_to_le32(enable);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_DWDS_ENABLE)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_fw_flush_timer *pcmd;
+
+	pcmd = (struct hostcmd_cmd_fw_flush_timer *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_FW_FLUSH_TIMER);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->value = cpu_to_le32(value);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_FW_FLUSH_TIMER)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct hostcmd_cmd_set_cdd *pcmd;
+
+	pcmd = (struct hostcmd_cmd_set_cdd *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	pcmd->cmd_hdr.cmd = cpu_to_le16(HOSTCMD_CMD_SET_CDD);
+	pcmd->cmd_hdr.len = cpu_to_le16(sizeof(*pcmd));
+	pcmd->enable = cpu_to_le32(priv->cdd);
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_CDD)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_send_mfg_cmd(struct mwl_priv *priv, char *mfgcmd)
+{
+	struct hostcmd_header *pcmd;
+	struct cmd_header *cmd_hd = (struct cmd_header *)(mfgcmd + 4);
+	u16 len;
+	u16 cmd;
+
+	pcmd = (struct hostcmd_header *)&priv->pcmd_buf[0];
+
+	spin_lock_bh(&priv->fwcmd_lock);
+
+	len = le16_to_cpu(cmd_hd->len);
+	memset(pcmd, 0x00, len + 4);
+	memcpy((char *)pcmd, mfgcmd, len + 4);
+	cmd = le16_to_cpu(cmd_hd->command);
+	if (mwl_fwcmd_exec_cmd(priv, cmd)) {
+		spin_unlock_bh(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution");
+		return -EIO;
+	}
+	cmd_hd = (struct cmd_header *)&priv->pcmd_buf[2];
+	len = le16_to_cpu(cmd_hd->len);
+	memcpy(mfgcmd, (char *)&priv->pcmd_buf[2], len);
+	spin_unlock_bh(&priv->fwcmd_lock);
+
+	return 0;
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwcmd.h b/drivers/net/wireless/marvell/mwlwifi/fwcmd.h
new file mode 100644
index 0000000..726b169
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwcmd.h
@@ -0,0 +1,198 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines firmware host command related
+ * functions.
+ */
+
+#ifndef _fwcmd_h_
+#define _fwcmd_h_
+
+/*  Define OpMode for SoftAP/Station mode
+ *
+ *  The following mode signature has to be written to PCI scratch register#0
+ *  right after successfully downloading the last block of firmware and
+ *  before waiting for firmware ready signature
+ */
+
+#define HOSTCMD_STA_MODE                0x5A
+#define HOSTCMD_SOFTAP_MODE             0xA5
+
+#define HOSTCMD_STA_FWRDY_SIGNATURE     0xF0F1F2F4
+#define HOSTCMD_SOFTAP_FWRDY_SIGNATURE  0xF1F2F4A5
+
+#define GUARD_INTERVAL_STANDARD         1
+#define GUARD_INTERVAL_SHORT            2
+#define GUARD_INTERVAL_AUTO             3
+
+#define	LINK_CS_STATE_CONSERV           0
+#define	LINK_CS_STATE_AGGR              1
+#define	LINK_CS_STATE_AUTO              2
+#define	LINK_CS_STATE_AUTO_DISABLED     3
+
+enum {
+	WL_ANTENNATYPE_RX = 1,
+	WL_ANTENNATYPE_TX = 2,
+};
+
+enum encr_type {
+	ENCR_TYPE_WEP = 0,
+	ENCR_TYPE_DISABLE = 1,
+	ENCR_TYPE_TKIP = 4,
+	ENCR_TYPE_AES = 6,
+	ENCR_TYPE_MIX = 7,
+};
+
+void mwl_fwcmd_reset(struct ieee80211_hw *hw);
+
+void mwl_fwcmd_int_enable(struct ieee80211_hw *hw);
+
+void mwl_fwcmd_int_disable(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_get_hw_specs(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,
+		       struct ieee80211_low_level_stats *stats);
+
+int mwl_fwcmd_radio_enable(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_radio_disable(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_set_radio_preamble(struct ieee80211_hw *hw,
+				 bool short_preamble);
+
+int mwl_fwcmd_get_addr_value(struct ieee80211_hw *hw, u32 addr, u32 len,
+			     u32 *val, u16 set);
+
+int mwl_fwcmd_max_tx_power(struct ieee80211_hw *hw,
+			   struct ieee80211_conf *conf, u8 fraction);
+
+int mwl_fwcmd_tx_power(struct ieee80211_hw *hw,
+		       struct ieee80211_conf *conf, u8 fraction);
+
+int mwl_fwcmd_rf_antenna(struct ieee80211_hw *hw, int dir, int antenna);
+
+int mwl_fwcmd_broadcast_ssid_enable(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif, bool enable);
+
+int mwl_fwcmd_set_rf_channel(struct ieee80211_hw *hw,
+			     struct ieee80211_conf *conf);
+
+int mwl_fwcmd_set_aid(struct ieee80211_hw *hw,
+		      struct ieee80211_vif *vif, u8 *bssid, u16 aid);
+
+int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif);
+
+int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw,
+				int threshold);
+
+int mwl_fwcmd_set_edca_params(struct ieee80211_hw *hw, u8 index,
+			      u16 cw_min, u16 cw_max, u8 aifs, u16 txop);
+
+int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable);
+
+int mwl_fwcmd_ht_guard_interval(struct ieee80211_hw *hw, u32 gi_type);
+
+int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw,
+			     int mcast, int mgmt);
+
+int mwl_fwcmd_set_linkadapt_cs_mode(struct ieee80211_hw *hw,
+				    u16 cs_mode);
+
+int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw,
+				  u16 mode);
+
+int mwl_fwcmd_set_mac_addr_client(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif, u8 *mac_addr);
+
+int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw,
+				  u8 *bitmap);
+
+int mwl_fwcmd_remove_mac_addr(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif, u8 *mac_addr);
+
+int mwl_fwcmd_bss_start(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif, bool enable);
+
+int mwl_fwcmd_set_beacon(struct ieee80211_hw *hw,
+			 struct ieee80211_vif *vif, u8 *beacon, int len);
+
+int mwl_fwcmd_set_new_stn_add(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif,
+			      struct ieee80211_sta *sta);
+
+int mwl_fwcmd_set_new_stn_add_self(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif);
+
+int mwl_fwcmd_set_new_stn_del(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif, u8 *addr);
+
+int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode);
+
+int mwl_fwcmd_update_encryption_enable(struct ieee80211_hw *hw,
+				       struct ieee80211_vif *vif,
+				       u8 *addr, u8 encr_type);
+
+int mwl_fwcmd_encryption_set_key(struct ieee80211_hw *hw,
+				 struct ieee80211_vif *vif, u8 *addr,
+				 struct ieee80211_key_conf *key);
+
+int mwl_fwcmd_encryption_remove_key(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif, u8 *addr,
+				    struct ieee80211_key_conf *key);
+
+int mwl_fwcmd_check_ba(struct ieee80211_hw *hw,
+		       struct mwl_ampdu_stream *stream,
+		       struct ieee80211_vif *vif);
+
+int mwl_fwcmd_create_ba(struct ieee80211_hw *hw,
+			struct mwl_ampdu_stream *stream,
+			u8 buf_size, struct ieee80211_vif *vif);
+
+int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw,
+			 u8 idx);
+
+struct mwl_ampdu_stream *mwl_fwcmd_add_stream(struct ieee80211_hw *hw,
+					      struct ieee80211_sta *sta,
+					      u8 tid);
+
+void mwl_fwcmd_del_sta_streams(struct ieee80211_hw *hw,
+			       struct ieee80211_sta *sta);
+
+int mwl_fwcmd_start_stream(struct ieee80211_hw *hw,
+			   struct mwl_ampdu_stream *stream);
+
+void mwl_fwcmd_remove_stream(struct ieee80211_hw *hw,
+			     struct mwl_ampdu_stream *stream);
+
+struct mwl_ampdu_stream *mwl_fwcmd_lookup_stream(struct ieee80211_hw *hw,
+						 u8 *addr, u8 tid);
+
+bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid);
+
+int mwl_fwcmd_set_optimization_level(struct ieee80211_hw *hw, u8 opt_level);
+
+int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable);
+
+int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value);
+
+int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw);
+
+int mwl_fwcmd_send_mfg_cmd(struct mwl_priv *priv, char *mfgcmd);
+
+#endif /* _fwcmd_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwdl.c b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
new file mode 100644
index 0000000..ec44aa4
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwdl.c
@@ -0,0 +1,199 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements firmware download related
+ * functions.
+ */
+
+#include <linux/io.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#ifdef CONFIG_SUPPORT_MFG
+#include "mfg.h"
+#endif
+#include "fwdl.h"
+
+#define FW_DOWNLOAD_BLOCK_SIZE          256
+#define FW_CHECK_MSECS                  3
+
+#define FW_MAX_NUM_CHECKS               0xffff
+
+static void mwl_fwdl_trig_pcicmd(struct mwl_priv *priv)
+{
+	writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
+
+	writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
+
+	writel(MACREG_H2ARIC_BIT_DOOR_BELL,
+	       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+static void mwl_fwdl_trig_pcicmd_bootcode(struct mwl_priv *priv)
+{
+	writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR);
+
+	writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
+
+	writel(MACREG_H2ARIC_BIT_DOOR_BELL,
+	       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+}
+
+int mwl_fwdl_download_firmware(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	const struct firmware *fw;
+	u32 curr_iteration = 0;
+	u32 size_fw_downloaded = 0;
+	u32 int_code = 0;
+	u32 len = 0;
+#ifdef CONFIG_SUPPORT_MFG
+	u32 fwreadysignature = priv->mfg_mode ?
+		MFG_FW_READY_SIGNATURE : HOSTCMD_SOFTAP_FWRDY_SIGNATURE;
+#else
+	u32 fwreadysignature = HOSTCMD_SOFTAP_FWRDY_SIGNATURE;
+#endif
+
+	fw = priv->fw_ucode;
+
+	mwl_fwcmd_reset(hw);
+
+	/* FW before jumping to boot rom, it will enable PCIe transaction retry,
+	 * wait for boot code to stop it.
+	 */
+	mdelay(FW_CHECK_MSECS);
+
+	writel(MACREG_A2HRIC_BIT_MASK,
+	       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CLEAR_SEL);
+	writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
+	writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK);
+	writel(MACREG_A2HRIC_BIT_MASK,
+	       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+	/* this routine interacts with SC2 bootrom to download firmware binary
+	 * to the device. After DMA'd to SC2, the firmware could be deflated to
+	 * reside on its respective blocks such as ITCM, DTCM, SQRAM,
+	 * (or even DDR, AFTER DDR is init'd before fw download
+	 */
+	wiphy_info(hw->wiphy, "fw download start 88\n");
+
+	/* Disable PFU before FWDL */
+	writel(0x100, priv->iobase1 + 0xE0E4);
+
+	/* make sure SCRATCH2 C40 is clear, in case we are too quick */
+	while (readl(priv->iobase1 + 0xc40) == 0)
+		;
+
+	while (size_fw_downloaded < fw->size) {
+		len = readl(priv->iobase1 + 0xc40);
+
+		if (!len)
+			break;
+
+		/* this copies the next chunk of fw binary to be delivered */
+		memcpy((char *)&priv->pcmd_buf[0],
+		       (fw->data + size_fw_downloaded), len);
+
+		/* this function writes pdata to c10, then write 2 to c18 */
+		mwl_fwdl_trig_pcicmd_bootcode(priv);
+
+		/* this is arbitrary per your platform; we use 0xffff */
+		curr_iteration = FW_MAX_NUM_CHECKS;
+
+		/* NOTE: the following back to back checks on C1C is time
+		 * sensitive, hence may need to be tweaked dependent on host
+		 * processor. Time for SC2 to go from the write of event 2 to
+		 * C1C == 2 is ~1300 nSec. Hence the checkings on host has to
+		 * consider how efficient your code can be to meet this timing,
+		 * or you can alternatively tweak this routines to fit your
+		 * platform
+		 */
+		do {
+			int_code = readl(priv->iobase1 + 0xc1c);
+			if (int_code != 0)
+				break;
+			curr_iteration--;
+		} while (curr_iteration);
+
+		do {
+			int_code = readl(priv->iobase1 + 0xc1c);
+			if ((int_code & MACREG_H2ARIC_BIT_DOOR_BELL) !=
+			    MACREG_H2ARIC_BIT_DOOR_BELL)
+				break;
+			curr_iteration--;
+		} while (curr_iteration);
+
+		if (curr_iteration == 0) {
+			/* This limited loop check allows you to exit gracefully
+			 * without locking up your entire system just because fw
+			 * download failed
+			 */
+			wiphy_err(hw->wiphy,
+				  "Exhausted curr_iteration for fw download\n");
+			goto err_download;
+		}
+
+		size_fw_downloaded += len;
+	}
+
+	wiphy_info(hw->wiphy,
+		   "FwSize = %d downloaded Size = %d curr_iteration %d\n",
+		   (int)fw->size, size_fw_downloaded, curr_iteration);
+
+	/* Now firware is downloaded successfully, so this part is to check
+	 * whether fw can properly execute to an extent that write back
+	 * signature to indicate its readiness to the host. NOTE: if your
+	 * downloaded fw crashes, this signature checking will fail. This
+	 * part is similar as SC1
+	 */
+	*((u32 *)&priv->pcmd_buf[1]) = 0;
+	mwl_fwdl_trig_pcicmd(priv);
+	curr_iteration = FW_MAX_NUM_CHECKS;
+	if (priv->mfg_mode)
+		writel(fwreadysignature, priv->iobase1 + 0xcf0);
+	do {
+		curr_iteration--;
+		if (!priv->mfg_mode) {
+			writel(HOSTCMD_SOFTAP_MODE,
+			       priv->iobase1 + MACREG_REG_GEN_PTR);
+			mdelay(FW_CHECK_MSECS);
+			int_code = readl(priv->iobase1 + MACREG_REG_INT_CODE);
+		} else {
+			mdelay(FW_CHECK_MSECS);
+			int_code = readl(priv->iobase1 + 0xc44);
+		}
+		if (!(curr_iteration % 0xff))
+			wiphy_err(hw->wiphy, "%x;", int_code);
+	} while ((curr_iteration) &&
+		 (int_code != fwreadysignature));
+
+	if (curr_iteration == 0) {
+		wiphy_err(hw->wiphy,
+			  "Exhausted curr_iteration for fw signature\n");
+		goto err_download;
+	}
+
+	wiphy_info(hw->wiphy, "complete\n");
+	writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE);
+
+	return 0;
+
+err_download:
+
+	mwl_fwcmd_reset(hw);
+
+	return -EIO;
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/fwdl.h b/drivers/net/wireless/marvell/mwlwifi/fwdl.h
new file mode 100644
index 0000000..b117142
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/fwdl.h
@@ -0,0 +1,25 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines firmware download related
+ * functions.
+ */
+
+#ifndef _fwdl_h_
+#define _fwdl_h_
+
+int mwl_fwdl_download_firmware(struct ieee80211_hw *hw);
+
+#endif /* _fwdl_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/hostcmd.h b/drivers/net/wireless/marvell/mwlwifi/hostcmd.h
new file mode 100644
index 0000000..1fd4bdf
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/hostcmd.h
@@ -0,0 +1,786 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines firmware host command related
+ * structure.
+ */
+
+#ifndef _hostcmd_h_
+#define _hostcmd_h_
+
+/* 16 bit host command code */
+#define HOSTCMD_CMD_GET_HW_SPEC                 0x0003
+#define HOSTCMD_CMD_SET_HW_SPEC                 0x0004
+#define HOSTCMD_CMD_802_11_GET_STAT             0x0014
+#define HOSTCMD_CMD_802_11_RADIO_CONTROL        0x001c
+#define HOSTCMD_CMD_MEM_ADDR_ACCESS             0x001d
+#define HOSTCMD_CMD_802_11_TX_POWER             0x001f
+#define HOSTCMD_CMD_802_11_RF_ANTENNA           0x0020
+#define HOSTCMD_CMD_BROADCAST_SSID_ENABLE       0x0050 /* per-vif */
+#define HOSTCMD_CMD_SET_RF_CHANNEL              0x010a
+#define HOSTCMD_CMD_SET_AID                     0x010d /* per-vif */
+#define HOSTCMD_CMD_SET_INFRA_MODE              0x010e /* per-vif */
+#define HOSTCMD_CMD_802_11_RTS_THSD             0x0113
+#define HOSTCMD_CMD_SET_EDCA_PARAMS             0x0115
+#define HOSTCMD_CMD_SET_WMM_MODE                0x0123
+#define HOSTCMD_CMD_HT_GUARD_INTERVAL           0x0124
+#define HOSTCMD_CMD_SET_FIXED_RATE              0x0126
+#define HOSTCMD_CMD_SET_IES                     0x0127
+#define HOSTCMD_CMD_SET_LINKADAPT_CS_MODE       0x0129
+#define HOSTCMD_CMD_SET_MAC_ADDR                0x0202 /* per-vif */
+#define HOSTCMD_CMD_SET_RATE_ADAPT_MODE         0x0203
+#define HOSTCMD_CMD_GET_WATCHDOG_BITMAP         0x0205
+#define HOSTCMD_CMD_DEL_MAC_ADDR                0x0206 /* per-vif */
+#define HOSTCMD_CMD_BSS_START                   0x1100 /* per-vif */
+#define HOSTCMD_CMD_AP_BEACON                   0x1101 /* per-vif */
+#define HOSTCMD_CMD_SET_NEW_STN                 0x1111 /* per-vif */
+#define HOSTCMD_CMD_SET_APMODE                  0x1114
+#define HOSTCMD_CMD_UPDATE_ENCRYPTION           0x1122 /* per-vif */
+#define HOSTCMD_CMD_BASTREAM                    0x1125
+#define HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL      0x1133
+#define HOSTCMD_CMD_DWDS_ENABLE                 0x1144
+#define HOSTCMD_CMD_FW_FLUSH_TIMER              0x1148
+#define HOSTCMD_CMD_SET_CDD                     0x1150
+
+/* Define general result code for each command */
+#define HOSTCMD_RESULT_OK                       0x0000
+/* General error */
+#define HOSTCMD_RESULT_ERROR                    0x0001
+/* Command is not valid */
+#define HOSTCMD_RESULT_NOT_SUPPORT              0x0002
+/* Command is pending (will be processed) */
+#define HOSTCMD_RESULT_PENDING                  0x0003
+/* System is busy (command ignored) */
+#define HOSTCMD_RESULT_BUSY                     0x0004
+/* Data buffer is not big enough */
+#define HOSTCMD_RESULT_PARTIAL_DATA             0x0005
+
+/* Define channel related constants */
+#define FREQ_BAND_2DOT4GHZ                      0x1
+#define FREQ_BAND_4DOT9GHZ                      0x2
+#define FREQ_BAND_5GHZ                          0x4
+#define FREQ_BAND_5DOT2GHZ                      0x8
+#define CH_AUTO_WIDTH	                        0
+#define CH_10_MHZ_WIDTH                         0x1
+#define CH_20_MHZ_WIDTH                         0x2
+#define CH_40_MHZ_WIDTH                         0x4
+#define CH_80_MHZ_WIDTH                         0x5
+#define EXT_CH_ABOVE_CTRL_CH                    0x1
+#define EXT_CH_AUTO                             0x2
+#define EXT_CH_BELOW_CTRL_CH                    0x3
+#define NO_EXT_CHANNEL                          0x0
+
+#define ACT_PRIMARY_CHAN_0                      0
+#define ACT_PRIMARY_CHAN_1                      1
+#define ACT_PRIMARY_CHAN_2                      2
+#define ACT_PRIMARY_CHAN_3                      3
+
+/* Define rate related constants */
+#define HOSTCMD_ACT_NOT_USE_FIXED_RATE          0x0002
+
+/* Define station related constants */
+#define HOSTCMD_ACT_STA_ACTION_ADD              0
+#define HOSTCMD_ACT_STA_ACTION_REMOVE           2
+
+/* Define key related constants */
+#define MAX_ENCR_KEY_LENGTH                     16
+#define MIC_KEY_LENGTH                          8
+
+#define KEY_TYPE_ID_WEP                         0x00
+#define KEY_TYPE_ID_TKIP                        0x01
+#define KEY_TYPE_ID_AES	                        0x02
+
+#define ENCR_KEY_FLAG_TXGROUPKEY                0x00000004
+#define ENCR_KEY_FLAG_PAIRWISE                  0x00000008
+#define ENCR_KEY_FLAG_TSC_VALID                 0x00000040
+#define ENCR_KEY_FLAG_WEP_TXKEY                 0x01000000
+#define ENCR_KEY_FLAG_MICKEY_VALID              0x02000000
+
+/* Define block ack related constants */
+#define BASTREAM_FLAG_IMMEDIATE_TYPE            1
+#define BASTREAM_FLAG_DIRECTION_UPSTREAM        0
+
+/* Define general purpose action */
+#define HOSTCMD_ACT_GEN_SET                     0x0001
+#define HOSTCMD_ACT_GEN_SET_LIST                0x0002
+#define HOSTCMD_ACT_GEN_GET_LIST                0x0003
+
+/* Misc */
+#define MAX_ENCR_KEY_LENGTH                     16
+#define MIC_KEY_LENGTH                          8
+
+enum {
+	WL_DISABLE = 0,
+	WL_ENABLE = 1,
+	WL_DISABLE_VMAC = 0x80,
+};
+
+enum {
+	WL_GET = 0,
+	WL_SET = 1,
+	WL_RESET = 2,
+};
+
+enum {
+	WL_LONG_PREAMBLE = 1,
+	WL_SHORT_PREAMBLE = 3,
+	WL_AUTO_PREAMBLE = 5,
+};
+
+enum encr_action_type {
+	/* request to enable/disable HW encryption */
+	ENCR_ACTION_ENABLE_HW_ENCR,
+	/* request to set encryption key */
+	ENCR_ACTION_TYPE_SET_KEY,
+	/* request to remove one or more keys */
+	ENCR_ACTION_TYPE_REMOVE_KEY,
+	ENCR_ACTION_TYPE_SET_GROUP_KEY,
+};
+
+enum ba_action_type {
+	BA_CREATE_STREAM,
+	BA_UPDATE_STREAM,
+	BA_DESTROY_STREAM,
+	BA_FLUSH_STREAM,
+	BA_CHECK_STREAM,
+};
+
+enum mac_type {
+	WL_MAC_TYPE_PRIMARY_CLIENT,
+	WL_MAC_TYPE_SECONDARY_CLIENT,
+	WL_MAC_TYPE_PRIMARY_AP,
+	WL_MAC_TYPE_SECONDARY_AP,
+};
+
+/* General host command header */
+struct hostcmd_header {
+	__le16 cmd;
+	__le16 len;
+	u8 seq_num;
+	u8 macid;
+	__le16 result;
+} __packed;
+
+/* HOSTCMD_CMD_GET_HW_SPEC */
+struct hostcmd_cmd_get_hw_spec {
+	struct hostcmd_header cmd_hdr;
+	u8 version;                  /* version of the HW                    */
+	u8 host_if;                  /* host interface                       */
+	__le16 num_wcb;              /* Max. number of WCB FW can handle     */
+	__le16 num_mcast_addr;       /* MaxNbr of MC addresses FW can handle */
+	u8 permanent_addr[ETH_ALEN]; /* MAC address programmed in HW         */
+	__le16 region_code;
+	__le16 num_antenna;          /* Number of antenna used      */
+	__le32 fw_release_num;       /* 4 byte of FW release number */
+	__le32 wcb_base0;
+	__le32 rxpd_wr_ptr;
+	__le32 rxpd_rd_ptr;
+	__le32 fw_awake_cookie;
+	__le32 wcb_base[SYSADPT_TOTAL_TX_QUEUES - 1];
+} __packed;
+
+/* HOSTCMD_CMD_SET_HW_SPEC */
+struct hostcmd_cmd_set_hw_spec {
+	struct hostcmd_header cmd_hdr;
+	/* HW revision */
+	u8 version;
+	/* Host interface */
+	u8 host_if;
+	/* Max. number of Multicast address FW can handle */
+	__le16 num_mcast_addr;
+	/* MAC address */
+	u8 permanent_addr[ETH_ALEN];
+	/* Region Code */
+	__le16 region_code;
+	/* 4 byte of FW release number, example 0x1234=1.2.3.4 */
+	__le32 fw_release_num;
+	/* Firmware awake cookie - used to ensure that the device
+	 * is not in sleep mode
+	 */
+	__le32 fw_awake_cookie;
+	/* Device capabilities (see above) */
+	__le32 device_caps;
+	/* Rx shared memory queue */
+	__le32 rxpd_wr_ptr;
+	/* Actual number of TX queues in WcbBase array */
+	__le32 num_tx_queues;
+	/* TX WCB Rings */
+	__le32 wcb_base[SYSADPT_NUM_OF_DESC_DATA];
+	/* Max AMSDU size (00 - AMSDU Disabled,
+	 * 01 - 4K, 10 - 8K, 11 - not defined)
+	 */
+	__le32 features;
+	__le32 tx_wcb_num_per_queue;
+	__le32 total_rx_wcb;
+} __packed;
+
+/* HOSTCMD_CMD_802_11_GET_STAT */
+struct hostcmd_cmd_802_11_get_stat {
+	struct hostcmd_header cmd_hdr;
+	__le32 tx_retry_successes;
+	__le32 tx_multiple_retry_successes;
+	__le32 tx_failures;
+	__le32 rts_successes;
+	__le32 rts_failures;
+	__le32 ack_failures;
+	__le32 rx_duplicate_frames;
+	__le32 rx_fcs_errors;
+	__le32 tx_watchdog_timeouts;
+	__le32 rx_overflows;
+	__le32 rx_frag_errors;
+	__le32 rx_mem_errors;
+	__le32 pointer_errors;
+	__le32 tx_underflows;
+	__le32 tx_done;
+	__le32 tx_done_buf_try_put;
+	__le32 tx_done_buf_put;
+	/* Put size of requested buffer in here */
+	__le32 wait_for_tx_buf;
+	__le32 tx_attempts;
+	__le32 tx_successes;
+	__le32 tx_fragments;
+	__le32 tx_multicasts;
+	__le32 rx_non_ctl_pkts;
+	__le32 rx_multicasts;
+	__le32 rx_undecryptable_frames;
+	__le32 rx_icv_errors;
+	__le32 rx_excluded_frames;
+	__le32 rx_weak_iv_count;
+	__le32 rx_unicasts;
+	__le32 rx_bytes;
+	__le32 rx_errors;
+	__le32 rx_rts_count;
+	__le32 tx_cts_count;
+} __packed;
+
+/* HOSTCMD_CMD_802_11_RADIO_CONTROL */
+struct hostcmd_cmd_802_11_radio_control {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;
+	/* @bit0: 1/0,on/off, @bit1: 1/0, long/short @bit2: 1/0,auto/fix */
+	__le16 control;
+	__le16 radio_on;
+} __packed;
+
+/* HOSTCMD_CMD_MEM_ADDR_ACCESS */
+struct hostcmd_cmd_mem_addr_access {
+	struct hostcmd_header cmd_hdr;
+	__le32 address;
+	__le16 length;
+	__le16 reserved;
+	__le32 value[64];
+} __packed;
+
+/* HOSTCMD_CMD_802_11_TX_POWER */
+struct hostcmd_cmd_802_11_tx_power {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;
+	__le16 band;
+	__le16 ch;
+	__le16 bw;
+	__le16 sub_ch;
+	__le16 power_level_list[SYSADPT_TX_POWER_LEVEL_TOTAL];
+} __packed;
+
+/* HOSTCMD_CMD_802_11_RF_ANTENNA */
+struct hostcmd_cmd_802_11_rf_antenna {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;
+	__le16 antenna_mode;     /* Number of antennas or 0xffff(diversity) */
+} __packed;
+
+/* HOSTCMD_CMD_BROADCAST_SSID_ENABLE */
+struct hostcmd_cmd_broadcast_ssid_enable {
+	struct hostcmd_header cmd_hdr;
+	__le32 enable;
+} __packed;
+
+/* HOSTCMD_CMD_SET_RF_CHANNEL */
+#define FREQ_BAND_MASK     0x0000003f
+#define CHNL_WIDTH_MASK    0x000007c0
+#define CHNL_WIDTH_SHIFT   6
+#define ACT_PRIMARY_MASK   0x00003800
+#define ACT_PRIMARY_SHIFT  11
+
+struct hostcmd_cmd_set_rf_channel {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;
+	u8 curr_chnl;
+	__le32 chnl_flags;
+} __packed;
+
+/* HOSTCMD_CMD_SET_AID */
+struct hostcmd_cmd_set_aid {
+	struct hostcmd_header cmd_hdr;
+	__le16 aid;
+	u8 mac_addr[ETH_ALEN];       /* AP's Mac Address(BSSID) */
+	__le32 gprotect;
+	u8 ap_rates[SYSADPT_MAX_DATA_RATES_G];
+} __packed;
+
+/* HOSTCMD_CMD_SET_INFRA_MODE */
+struct hostcmd_cmd_set_infra_mode {
+	struct hostcmd_header cmd_hdr;
+} __packed;
+
+/* HOSTCMD_CMD_802_11_RTS_THSD */
+struct hostcmd_cmd_802_11_rts_thsd {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;
+	__le16	threshold;
+} __packed;
+
+/* HOSTCMD_CMD_SET_EDCA_PARAMS */
+struct hostcmd_cmd_set_edca_params {
+	struct hostcmd_header cmd_hdr;
+	/* 0 = get all, 0x1 =set CWMin/Max,  0x2 = set TXOP , 0x4 =set AIFSN */
+	__le16 action;
+	__le16 txop;                 /* in unit of 32 us */
+	__le32 cw_max;               /* 0~15 */
+	__le32 cw_min;               /* 0~15 */
+	u8 aifsn;
+	u8 txq_num;                  /* Tx Queue number. */
+} __packed;
+
+/* HOSTCMD_CMD_SET_WMM_MODE */
+struct hostcmd_cmd_set_wmm_mode {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;               /* 0->unset, 1->set */
+} __packed;
+
+/* HOSTCMD_CMD_HT_GUARD_INTERVAL */
+struct hostcmd_cmd_ht_guard_interval {
+	struct hostcmd_header cmd_hdr;
+	__le32 action;
+	__le32 gi_type;
+} __packed;
+
+/* HOSTCMD_CMD_SET_FIXED_RATE */
+struct fix_rate_flag {           /* lower rate after the retry count */
+	/* 0: legacy, 1: HT */
+	__le32 fix_rate_type;
+	/* 0: retry count is not valid, 1: use retry count specified */
+	__le32 retry_count_valid;
+} __packed;
+
+struct fix_rate_entry {
+	struct fix_rate_flag fix_rate_type_flags;
+	/* depending on the flags above, this can be either a legacy
+	 * rate(not index) or an MCS code.
+	 */
+	__le32 fixed_rate;
+	__le32 retry_count;
+} __packed;
+
+struct hostcmd_cmd_set_fixed_rate {
+	struct hostcmd_header cmd_hdr;
+	/* HOSTCMD_ACT_NOT_USE_FIXED_RATE 0x0002 */
+	__le32 action;
+	/* use fixed rate specified but firmware can drop to */
+	__le32 allow_rate_drop;
+	__le32 entry_count;
+	struct fix_rate_entry fixed_rate_table[4];
+	u8 multicast_rate;
+	u8 multi_rate_tx_type;
+	u8 management_rate;
+} __packed;
+
+/* HOSTCMD_CMD_SET_IES */
+struct hostcmd_cmd_set_ies {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;               /* 0->unset, 1->set */
+	__le16 ie_list_len_ht;
+	__le16 ie_list_len_vht;
+	__le16 ie_list_len_proprietary;
+	/*Buffer size same as Generic_Beacon*/
+	u8 ie_list_ht[148];
+	u8 ie_list_vht[24];
+	u8 ie_list_proprietary[112];
+} __packed;
+
+/* HOSTCMD_CMD_SET_LINKADAPT_CS_MODE */
+struct hostcmd_cmd_set_linkadapt_cs_mode {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;
+	__le16 cs_mode;
+} __packed;
+
+/* HOSTCMD_CMD_SET_MAC_ADDR, HOSTCMD_CMD_DEL_MAC_ADDR */
+struct hostcmd_cmd_set_mac_addr {
+	struct hostcmd_header cmd_hdr;
+	__le16 mac_type;
+	u8 mac_addr[ETH_ALEN];
+} __packed;
+
+/* HOSTCMD_CMD_SET_RATE_ADAPT_MODE */
+struct hostcmd_cmd_set_rate_adapt_mode {
+	struct hostcmd_header cmd_hdr;
+	__le16 action;
+	__le16 rate_adapt_mode;      /* 0:Indoor, 1:Outdoor */
+} __packed;
+
+/* HOSTCMD_CMD_GET_WATCHDOG_BITMAP */
+struct hostcmd_cmd_get_watchdog_bitmap {
+	struct hostcmd_header cmd_hdr;
+	u8 watchdog_bitmap;          /* for SW/BA */
+} __packed;
+
+/* HOSTCMD_CMD_BSS_START */
+struct hostcmd_cmd_bss_start {
+	struct hostcmd_header cmd_hdr;
+	__le32 enable;                  /* FALSE: Disable or TRUE: Enable */
+} __packed;
+
+/* HOSTCMD_CMD_AP_BEACON */
+struct cf_params {
+	u8 elem_id;
+	u8 len;
+	u8 cfp_cnt;
+	u8 cfp_period;
+	__le16 cfp_max_duration;
+	__le16 cfp_duration_remaining;
+} __packed;
+
+struct ibss_params {
+	u8 elem_id;
+	u8 len;
+	__le16	atim_window;
+} __packed;
+
+union ss_params {
+	struct cf_params cf_param_set;
+	struct ibss_params ibss_param_set;
+} __packed;
+
+struct fh_params {
+	u8 elem_id;
+	u8 len;
+	__le16 dwell_time;
+	u8 hop_set;
+	u8 hop_pattern;
+	u8 hop_index;
+} __packed;
+
+struct ds_params {
+	u8 elem_id;
+	u8 len;
+	u8 current_chnl;
+} __packed;
+
+union phy_params {
+	struct fh_params fh_param_set;
+	struct ds_params ds_param_set;
+} __packed;
+
+struct rsn_ie {
+	u8 elem_id;
+	u8 len;
+	u8 oui_type[4];              /* 00:50:f2:01 */
+	u8 ver[2];
+	u8 grp_key_cipher[4];
+	u8 pws_key_cnt[2];
+	u8 pws_key_cipher_list[4];
+	u8 auth_key_cnt[2];
+	u8 auth_key_list[4];
+} __packed;
+
+struct rsn48_ie {
+	u8 elem_id;
+	u8 len;
+	u8 ver[2];
+	u8 grp_key_cipher[4];
+	u8 pws_key_cnt[2];
+	u8 pws_key_cipher_list[4];
+	u8 auth_key_cnt[2];
+	u8 auth_key_list[4];
+	u8 rsn_cap[2];
+	u8 pmk_id_cnt[2];
+	u8 pmk_id_list[16];          /* Should modify to 16 * S */
+	u8 reserved[8];
+} __packed;
+
+struct ac_param_rcd {
+	u8 aci_aifsn;
+	u8 ecw_min_max;
+	__le16 txop_lim;
+} __packed;
+
+struct wmm_param_elem {
+	u8 elem_id;
+	u8 len;
+	u8 oui[3];
+	u8 type;
+	u8 sub_type;
+	u8 version;
+	u8 rsvd;
+	struct ac_param_rcd ac_be;
+	struct ac_param_rcd ac_bk;
+	struct ac_param_rcd ac_vi;
+	struct ac_param_rcd ac_vo;
+} __packed;
+
+struct channel_info {
+	u8 first_channel_num;
+	u8 num_channels;
+	u8 max_tx_pwr_level;
+} __packed;
+
+struct country {
+	u8 elem_id;
+	u8 len;
+	u8 country_str[3];
+	struct channel_info channel_info[40];
+} __packed;
+
+struct start_cmd {
+	u8 sta_mac_addr[ETH_ALEN];
+	u8 ssid[IEEE80211_MAX_SSID_LEN];
+	u8 bss_type;
+	__le16 bcn_period;
+	u8 dtim_period;
+	union ss_params ss_param_set;
+	union phy_params phy_param_set;
+	__le16 probe_delay;
+	__le16 cap_info;
+	u8 b_rate_set[SYSADPT_MAX_DATA_RATES_G];
+	u8 op_rate_set[SYSADPT_MAX_DATA_RATES_G];
+	struct rsn_ie rsn_ie;
+	struct rsn48_ie rsn48_ie;
+	struct wmm_param_elem wmm_param;
+	struct country country;
+	__le32 ap_rf_type;           /* 0->B, 1->G, 2->Mixed, 3->A, 4->11J */
+} __packed;
+
+struct hostcmd_cmd_ap_beacon {
+	struct hostcmd_header cmd_hdr;
+	struct start_cmd start_cmd;
+} __packed;
+
+/* HOSTCMD_CMD_SET_NEW_STN */
+struct add_ht_info {
+	u8 control_chnl;
+	u8 add_chnl;
+	__le16 op_mode;
+	__le16 stbc;
+} __packed;
+
+struct peer_info {
+	__le32 legacy_rate_bitmap;
+	u8 ht_rates[4];
+	__le16 cap_info;
+	__le16 ht_cap_info;
+	u8 mac_ht_param_info;
+	u8 mrvl_sta;
+	struct add_ht_info add_ht_info;
+	__le32 tx_bf_capabilities;   /* EXBF_SUPPORT */
+	__le32 vht_max_rx_mcs;
+	__le32 vht_cap;
+	/* 0:20Mhz, 1:40Mhz, 2:80Mhz, 3:160 or 80+80Mhz */
+	u8 vht_rx_channel_width;
+} __packed;
+
+struct hostcmd_cmd_set_new_stn {
+	struct hostcmd_header cmd_hdr;
+	__le16 aid;
+	u8 mac_addr[ETH_ALEN];
+	__le16 stn_id;
+	__le16 action;
+	__le16 reserved;
+	struct peer_info peer_info;
+	/* UAPSD_SUPPORT */
+	u8 qos_info;
+	u8 is_qos_sta;
+	__le32 fw_sta_ptr;
+} __packed;
+
+/* HOSTCMD_CMD_SET_APMODE */
+struct hostcmd_cmd_set_apmode {
+	struct hostcmd_header cmd_hdr;
+	u8 apmode;
+} __packed;
+
+/* HOSTCMD_CMD_UPDATE_ENCRYPTION */
+struct hostcmd_cmd_update_encryption {
+	struct hostcmd_header cmd_hdr;
+	/* Action type - see encr_action_type */
+	__le32 action_type;          /* encr_action_type */
+	/* size of the data buffer attached. */
+	__le32 data_length;
+	u8 mac_addr[ETH_ALEN];
+	u8 action_data[1];
+} __packed;
+
+struct wep_type_key {
+	/* WEP key material (max 128bit) */
+	u8 key_material[MAX_ENCR_KEY_LENGTH];
+} __packed;
+
+struct encr_tkip_seqcnt {
+	__le16 low;
+	__le32 high;
+} __packed;
+
+struct tkip_type_key {
+	/* TKIP Key material. Key type (group or pairwise key) is
+	 * determined by flags
+	 */
+	/* in KEY_PARAM_SET structure. */
+	u8 key_material[MAX_ENCR_KEY_LENGTH];
+	/* MIC keys */
+	u8 tkip_tx_mic_key[MIC_KEY_LENGTH];
+	u8 tkip_rx_mic_key[MIC_KEY_LENGTH];
+	struct encr_tkip_seqcnt	tkip_rsc;
+	struct encr_tkip_seqcnt	tkip_tsc;
+} __packed;
+
+struct aes_type_key {
+	/* AES Key material */
+	u8 key_material[MAX_ENCR_KEY_LENGTH];
+} __packed;
+
+union mwl_key_type {
+	struct wep_type_key  wep_key;
+	struct tkip_type_key tkip_key;
+	struct aes_type_key  aes_key;
+} __packed;
+
+struct key_param_set {
+	/* Total length of this structure (Key is variable size array) */
+	__le16 length;
+	/* Key type - WEP, TKIP or AES-CCMP. */
+	/* See definitions above */
+	__le16 key_type_id;
+	/* key flags (ENCR_KEY_FLAG_XXX_ */
+	__le32 key_info;
+	/* For WEP only - actual key index */
+	__le32 key_index;
+	/* Size of the key */
+	__le16 key_len;
+	/* Key material (variable size array) */
+	union mwl_key_type key;
+	u8 mac_addr[ETH_ALEN];
+} __packed;
+
+struct hostcmd_cmd_set_key {
+	struct hostcmd_header cmd_hdr;
+	/* Action type - see encr_action_type */
+	__le32 action_type;          /* encr_action_type */
+	/* size of the data buffer attached. */
+	__le32 data_length;
+	/* data buffer - maps to one KEY_PARAM_SET structure */
+	struct key_param_set key_param;
+} __packed;
+
+/* HOSTCMD_CMD_BASTREAM */
+#define BA_TYPE_MASK       0x00000001
+#define BA_DIRECTION_MASK  0x00000006
+#define BA_DIRECTION_SHIFT 1
+
+struct ba_context {
+	__le32 context;
+} __packed;
+
+/* parameters for block ack creation */
+struct create_ba_params {
+	/* BA Creation flags - see above */
+	__le32 flags;
+	/* idle threshold */
+	__le32 idle_thrs;
+	/* block ack transmit threshold (after how many pkts should we
+	 * send BAR?)
+	 */
+	__le32 bar_thrs;
+	/* receiver window size */
+	__le32 window_size;
+	/* MAC Address of the BA partner */
+	u8 peer_mac_addr[ETH_ALEN];
+	/* Dialog Token */
+	u8 dialog_token;
+	/* TID for the traffic stream in this BA */
+	u8 tid;
+	/* shared memory queue ID (not sure if this is required) */
+	u8 queue_id;
+	u8 param_info;
+	/* returned by firmware - firmware context pointer. */
+	/* this context pointer will be passed to firmware for all
+	 * future commands.
+	 */
+	struct ba_context fw_ba_context;
+	u8 reset_seq_no;             /** 0 or 1**/
+	__le16 current_seq;
+	/* This is for virtual station in Sta proxy mode for V6FW */
+	u8 sta_src_mac_addr[ETH_ALEN];
+} __packed;
+
+/* new transmit sequence number information */
+struct ba_update_seq_num {
+	/* BA flags - see above */
+	__le32 flags;
+	/* returned by firmware in the create ba stream response */
+	struct ba_context fw_ba_context;
+	/* new sequence number for this block ack stream */
+	__le16 ba_seq_num;
+} __packed;
+
+struct ba_stream_context {
+	/* BA Stream flags */
+	__le32 flags;
+	/* returned by firmware in the create ba stream response */
+	struct ba_context fw_ba_context;
+} __packed;
+
+union ba_info {
+	/* information required to create BA Stream... */
+	struct create_ba_params	create_params;
+	/* update starting/new sequence number etc. */
+	struct ba_update_seq_num updt_seq_num;
+	/* destroy an existing stream... */
+	struct ba_stream_context destroy_params;
+	/* destroy an existing stream... */
+	struct ba_stream_context flush_params;
+} __packed;
+
+struct hostcmd_cmd_bastream {
+	struct hostcmd_header cmd_hdr;
+	__le32 action_type;
+	union ba_info ba_info;
+} __packed;
+
+/* HOSTCMD_CMD_SET_OPTIMIZATION_LEVEL */
+struct hostcmd_cmd_set_optimization_level {
+	struct hostcmd_header cmd_hdr;
+	u8 opt_level;
+} __packed;
+
+/* HOSTCMD_CMD_DWDS_ENABLE */
+struct hostcmd_cmd_dwds_enable {
+	struct hostcmd_header cmd_hdr;
+	__le32 enable;               /* 0 -- Disable. or 1 -- Enable. */
+} __packed;
+
+/* HOSTCMD_CMD_FW_FLUSH_TIMER */
+struct hostcmd_cmd_fw_flush_timer {
+	struct hostcmd_header cmd_hdr;
+	/* 0 -- Disable. > 0 -- holds time value in usecs. */
+	__le32 value;
+} __packed;
+
+/* HOSTCMD_CMD_SET_CDD */
+struct hostcmd_cmd_set_cdd {
+	struct hostcmd_header cmd_hdr;
+	__le32 enable;
+} __packed;
+
+#endif /* _hostcmd_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/isr.c b/drivers/net/wireless/marvell/mwlwifi/isr.c
new file mode 100644
index 0000000..20b6088
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/isr.c
@@ -0,0 +1,150 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements interrupt related functions. */
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "isr.h"
+
+#define INVALID_WATCHDOG 0xAA
+
+irqreturn_t mwl_isr(int irq, void *dev_id)
+{
+	struct ieee80211_hw *hw = dev_id;
+	struct mwl_priv *priv = hw->priv;
+	void __iomem *int_status_mask;
+	unsigned int int_status, clr_status;
+	u32 status;
+
+	int_status_mask = priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK;
+
+	int_status = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
+
+	if (int_status == 0x00000000)
+		return IRQ_NONE;
+
+	if (int_status == 0xffffffff) {
+		wiphy_warn(hw->wiphy, "card unplugged?\n");
+	} else {
+		clr_status = int_status;
+
+		if (int_status & MACREG_A2HRIC_BIT_TX_DONE) {
+			int_status &= ~MACREG_A2HRIC_BIT_TX_DONE;
+
+			if (!priv->is_tx_schedule) {
+				status = readl(int_status_mask);
+				writel((status & ~MACREG_A2HRIC_BIT_TX_DONE),
+				       int_status_mask);
+				tasklet_schedule(&priv->tx_task);
+				priv->is_tx_schedule = true;
+			}
+		}
+
+		if (int_status & MACREG_A2HRIC_BIT_RX_RDY) {
+			int_status &= ~MACREG_A2HRIC_BIT_RX_RDY;
+
+			if (!priv->is_rx_schedule) {
+				status = readl(int_status_mask);
+				writel((status & ~MACREG_A2HRIC_BIT_RX_RDY),
+				       int_status_mask);
+				tasklet_schedule(&priv->rx_task);
+				priv->is_rx_schedule = true;
+			}
+		}
+
+		if (int_status & MACREG_A2HRIC_BIT_QUE_EMPTY) {
+			int_status &= ~MACREG_A2HRIC_BIT_QUE_EMPTY;
+
+			if (!priv->is_qe_schedule) {
+				status = readl(int_status_mask);
+				writel((status & ~MACREG_A2HRIC_BIT_QUE_EMPTY),
+				       int_status_mask);
+				tasklet_schedule(&priv->qe_task);
+				priv->is_qe_schedule = true;
+			}
+		}
+
+		if (int_status & MACREG_A2HRIC_BA_WATCHDOG) {
+			status = readl(int_status_mask);
+			writel((status & ~MACREG_A2HRIC_BA_WATCHDOG),
+			       int_status_mask);
+			int_status &= ~MACREG_A2HRIC_BA_WATCHDOG;
+			ieee80211_queue_work(hw, &priv->watchdog_ba_handle);
+		}
+
+		writel(~clr_status,
+		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE);
+	}
+
+	return IRQ_HANDLED;
+}
+
+void mwl_watchdog_ba_events(struct work_struct *work)
+{
+	int rc;
+	u8 bitmap = 0, stream_index;
+	struct mwl_ampdu_stream *streams;
+	struct mwl_priv *priv =
+		container_of(work, struct mwl_priv, watchdog_ba_handle);
+	u32 status;
+
+	rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap);
+
+	if (rc)
+		goto done;
+
+	spin_lock_bh(&priv->stream_lock);
+
+	/* the bitmap is the hw queue number.  Map it to the ampdu queue. */
+	if (bitmap != INVALID_WATCHDOG) {
+		if (bitmap == SYSADPT_TX_AMPDU_QUEUES)
+			stream_index = 0;
+		else if (bitmap > SYSADPT_TX_AMPDU_QUEUES)
+			stream_index = bitmap - SYSADPT_TX_AMPDU_QUEUES;
+		else
+			stream_index = bitmap + 3; /** queue 0 is stream 3*/
+
+		if (bitmap != 0xFF) {
+			/* Check if the stream is in use before disabling it */
+			streams = &priv->ampdu[stream_index];
+
+			if (streams->state == AMPDU_STREAM_ACTIVE)
+				ieee80211_stop_tx_ba_session(streams->sta,
+							     streams->tid);
+		} else {
+			for (stream_index = 0;
+			     stream_index < SYSADPT_TX_AMPDU_QUEUES;
+			     stream_index++) {
+				streams = &priv->ampdu[stream_index];
+
+				if (streams->state != AMPDU_STREAM_ACTIVE)
+					continue;
+
+				ieee80211_stop_tx_ba_session(streams->sta,
+							     streams->tid);
+			}
+		}
+	}
+
+	spin_unlock_bh(&priv->stream_lock);
+
+done:
+
+	status = readl(priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+	writel(status | MACREG_A2HRIC_BA_WATCHDOG,
+	       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/isr.h b/drivers/net/wireless/marvell/mwlwifi/isr.h
new file mode 100644
index 0000000..2598115
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/isr.h
@@ -0,0 +1,26 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines interrupt related functions. */
+
+#ifndef _isr_h_
+#define _isr_h_
+
+#include <linux/interrupt.h>
+
+irqreturn_t mwl_isr(int irq, void *dev_id);
+void mwl_watchdog_ba_events(struct work_struct *work);
+
+#endif /* _isr_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/mac80211.c b/drivers/net/wireless/marvell/mwlwifi/mac80211.c
new file mode 100644
index 0000000..ff93357
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/mac80211.c
@@ -0,0 +1,699 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements mac80211 related functions. */
+
+#include <linux/etherdevice.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "tx.h"
+
+#define MWL_DRV_NAME        KBUILD_MODNAME
+
+#define MAX_AMPDU_ATTEMPTS  5
+
+static const struct ieee80211_rate mwl_rates_24[] = {
+	{ .bitrate = 10, .hw_value = 2, },
+	{ .bitrate = 20, .hw_value = 4, },
+	{ .bitrate = 55, .hw_value = 11, },
+	{ .bitrate = 110, .hw_value = 22, },
+	{ .bitrate = 220, .hw_value = 44, },
+	{ .bitrate = 60, .hw_value = 12, },
+	{ .bitrate = 90, .hw_value = 18, },
+	{ .bitrate = 120, .hw_value = 24, },
+	{ .bitrate = 180, .hw_value = 36, },
+	{ .bitrate = 240, .hw_value = 48, },
+	{ .bitrate = 360, .hw_value = 72, },
+	{ .bitrate = 480, .hw_value = 96, },
+	{ .bitrate = 540, .hw_value = 108, },
+};
+
+static const struct ieee80211_rate mwl_rates_50[] = {
+	{ .bitrate = 60, .hw_value = 12, },
+	{ .bitrate = 90, .hw_value = 18, },
+	{ .bitrate = 120, .hw_value = 24, },
+	{ .bitrate = 180, .hw_value = 36, },
+	{ .bitrate = 240, .hw_value = 48, },
+	{ .bitrate = 360, .hw_value = 72, },
+	{ .bitrate = 480, .hw_value = 96, },
+	{ .bitrate = 540, .hw_value = 108, },
+};
+
+static void mwl_mac80211_tx(struct ieee80211_hw *hw,
+			    struct ieee80211_tx_control *control,
+			    struct sk_buff *skb)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	if (!priv->radio_on) {
+		wiphy_warn(hw->wiphy,
+			   "dropped TX frame since radio is disabled\n");
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	mwl_tx_xmit(hw, control, skb);
+}
+
+static int mwl_mac80211_start(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	int rc;
+
+	/* Enable TX reclaim and RX tasklets. */
+	tasklet_enable(&priv->tx_task);
+	tasklet_enable(&priv->rx_task);
+	tasklet_enable(&priv->qe_task);
+
+	/* Enable interrupts */
+	mwl_fwcmd_int_enable(hw);
+
+	rc = mwl_fwcmd_radio_enable(hw);
+	if (rc)
+		goto fwcmd_fail;
+	rc = mwl_fwcmd_set_rate_adapt_mode(hw, 0);
+	if (rc)
+		goto fwcmd_fail;
+	rc = mwl_fwcmd_set_wmm_mode(hw, true);
+	if (rc)
+		goto fwcmd_fail;
+	rc = mwl_fwcmd_ht_guard_interval(hw, GUARD_INTERVAL_AUTO);
+	if (rc)
+		goto fwcmd_fail;
+	rc = mwl_fwcmd_set_dwds_stamode(hw, true);
+	if (rc)
+		goto fwcmd_fail;
+	rc = mwl_fwcmd_set_fw_flush_timer(hw, SYSADPT_AMSDU_FLUSH_TIME);
+	if (rc)
+		goto fwcmd_fail;
+	rc = mwl_fwcmd_set_optimization_level(hw, 1);
+	if (rc)
+		goto fwcmd_fail;
+
+	ieee80211_wake_queues(hw);
+	return 0;
+
+fwcmd_fail:
+	mwl_fwcmd_int_disable(hw);
+	tasklet_disable(&priv->tx_task);
+	tasklet_disable(&priv->rx_task);
+	tasklet_disable(&priv->qe_task);
+
+	return rc;
+}
+
+static void mwl_mac80211_stop(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	mwl_fwcmd_radio_disable(hw);
+
+	ieee80211_stop_queues(hw);
+
+	/* Disable interrupts */
+	mwl_fwcmd_int_disable(hw);
+
+	/* Disable TX reclaim and RX tasklets. */
+	tasklet_disable(&priv->tx_task);
+	tasklet_disable(&priv->rx_task);
+	tasklet_disable(&priv->qe_task);
+
+	/* Return all skbs to mac80211 */
+	mwl_tx_done((unsigned long)hw);
+}
+
+static int mwl_mac80211_add_interface(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	u32 macids_supported;
+	int macid;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_MESH_POINT:
+		macids_supported = priv->ap_macids_supported;
+		break;
+	case NL80211_IFTYPE_STATION:
+		macids_supported = priv->sta_macids_supported;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	macid = ffs(macids_supported & ~priv->macids_used);
+
+	if (!macid) {
+		wiphy_warn(hw->wiphy, "no macid can be allocated\n");
+		return -EBUSY;
+	}
+	macid--;
+
+	/* Setup driver private area. */
+	mwl_vif = mwl_dev_get_vif(vif);
+	memset(mwl_vif, 0, sizeof(*mwl_vif));
+	mwl_vif->macid = macid;
+	mwl_vif->seqno = 0;
+	mwl_vif->is_hw_crypto_enabled = false;
+	mwl_vif->beacon_info.valid = false;
+	mwl_vif->iv16 = 1;
+	mwl_vif->iv32 = 0;
+	mwl_vif->keyidx = 0;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_AP:
+		ether_addr_copy(mwl_vif->bssid, vif->addr);
+		mwl_fwcmd_set_new_stn_add_self(hw, vif);
+		break;
+	case NL80211_IFTYPE_MESH_POINT:
+		ether_addr_copy(mwl_vif->bssid, vif->addr);
+		mwl_fwcmd_set_new_stn_add_self(hw, vif);
+		break;
+	case NL80211_IFTYPE_STATION:
+		ether_addr_copy(mwl_vif->sta_mac, vif->addr);
+		mwl_fwcmd_bss_start(hw, vif, true);
+		mwl_fwcmd_set_infra_mode(hw, vif);
+		mwl_fwcmd_set_mac_addr_client(hw, vif, vif->addr);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	priv->macids_used |= 1 << mwl_vif->macid;
+	spin_lock_bh(&priv->vif_lock);
+	list_add_tail(&mwl_vif->list, &priv->vif_list);
+	spin_unlock_bh(&priv->vif_lock);
+
+	return 0;
+}
+
+static void mwl_mac80211_remove_vif(struct mwl_priv *priv,
+				    struct ieee80211_vif *vif)
+{
+	struct mwl_vif *mwl_vif = mwl_dev_get_vif(vif);
+
+	if (!priv->macids_used)
+		return;
+
+	mwl_tx_del_pkts_via_vif(priv->hw, vif);
+
+	priv->macids_used &= ~(1 << mwl_vif->macid);
+	spin_lock_bh(&priv->vif_lock);
+	list_del(&mwl_vif->list);
+	spin_unlock_bh(&priv->vif_lock);
+}
+
+static void mwl_mac80211_remove_interface(struct ieee80211_hw *hw,
+					  struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	switch (vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_MESH_POINT:
+		mwl_fwcmd_set_new_stn_del(hw, vif, vif->addr);
+		break;
+	case NL80211_IFTYPE_STATION:
+		mwl_fwcmd_remove_mac_addr(hw, vif, vif->addr);
+		break;
+	default:
+		break;
+	}
+
+	mwl_mac80211_remove_vif(priv, vif);
+}
+
+static int mwl_mac80211_config(struct ieee80211_hw *hw,
+			       u32 changed)
+{
+	struct ieee80211_conf *conf = &hw->conf;
+	int rc;
+
+	wiphy_debug(hw->wiphy, "change: 0x%x\n", changed);
+
+	if (conf->flags & IEEE80211_CONF_IDLE)
+		rc = mwl_fwcmd_radio_disable(hw);
+	else
+		rc = mwl_fwcmd_radio_enable(hw);
+
+	if (rc)
+		goto out;
+
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		int rate = 0;
+
+		if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
+			mwl_fwcmd_set_apmode(hw, AP_MODE_2_4GHZ_11AC_MIXED);
+			mwl_fwcmd_set_linkadapt_cs_mode(hw,
+							LINK_CS_STATE_CONSERV);
+			rate = mwl_rates_24[0].hw_value;
+		} else if (conf->chandef.chan->band == IEEE80211_BAND_5GHZ) {
+			mwl_fwcmd_set_apmode(hw, AP_MODE_11AC);
+			mwl_fwcmd_set_linkadapt_cs_mode(hw,
+							LINK_CS_STATE_AUTO);
+			rate = mwl_rates_50[0].hw_value;
+		}
+
+		rc = mwl_fwcmd_set_rf_channel(hw, conf);
+		if (rc)
+			goto out;
+		rc = mwl_fwcmd_use_fixed_rate(hw, rate, rate);
+		if (rc)
+			goto out;
+		rc = mwl_fwcmd_max_tx_power(hw, conf, 0);
+		if (rc)
+			goto out;
+		rc = mwl_fwcmd_tx_power(hw, conf, 0);
+		if (rc)
+			goto out;
+		rc = mwl_fwcmd_set_cdd(hw);
+	}
+
+out:
+
+	return rc;
+}
+
+static void mwl_mac80211_bss_info_changed_sta(struct ieee80211_hw *hw,
+					      struct ieee80211_vif *vif,
+					      struct ieee80211_bss_conf *info,
+					      u32 changed)
+{
+	if (changed & BSS_CHANGED_ERP_PREAMBLE)
+		mwl_fwcmd_set_radio_preamble(hw,
+					     vif->bss_conf.use_short_preamble);
+
+	if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc)
+		mwl_fwcmd_set_aid(hw, vif, (u8 *)vif->bss_conf.bssid,
+				  vif->bss_conf.aid);
+}
+
+static void mwl_mac80211_bss_info_changed_ap(struct ieee80211_hw *hw,
+					     struct ieee80211_vif *vif,
+					     struct ieee80211_bss_conf *info,
+					     u32 changed)
+{
+	if (changed & BSS_CHANGED_ERP_PREAMBLE)
+		mwl_fwcmd_set_radio_preamble(hw,
+					     vif->bss_conf.use_short_preamble);
+
+	if (changed & BSS_CHANGED_BASIC_RATES) {
+		int idx;
+		int rate;
+
+		/* Use lowest supported basic rate for multicasts
+		 * and management frames (such as probe responses --
+		 * beacons will always go out at 1 Mb/s).
+		 */
+		idx = ffs(vif->bss_conf.basic_rates);
+		if (idx)
+			idx--;
+
+		if (hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ)
+			rate = mwl_rates_24[idx].hw_value;
+		else
+			rate = mwl_rates_50[idx].hw_value;
+
+		mwl_fwcmd_use_fixed_rate(hw, rate, rate);
+	}
+
+	if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) {
+		struct sk_buff *skb;
+
+		skb = ieee80211_beacon_get(hw, vif);
+
+		if (skb) {
+			mwl_fwcmd_set_beacon(hw, vif, skb->data, skb->len);
+			dev_kfree_skb_any(skb);
+		}
+
+		if ((info->ssid[0] != '\0') &&
+		    (info->ssid_len != 0) &&
+		    (!info->hidden_ssid))
+			mwl_fwcmd_broadcast_ssid_enable(hw, vif, true);
+		else
+			mwl_fwcmd_broadcast_ssid_enable(hw, vif, false);
+	}
+
+	if (changed & BSS_CHANGED_BEACON_ENABLED)
+		mwl_fwcmd_bss_start(hw, vif, info->enable_beacon);
+}
+
+static void mwl_mac80211_bss_info_changed(struct ieee80211_hw *hw,
+					  struct ieee80211_vif *vif,
+					  struct ieee80211_bss_conf *info,
+					  u32 changed)
+{
+	switch (vif->type) {
+	case NL80211_IFTYPE_AP:
+	case NL80211_IFTYPE_MESH_POINT:
+		mwl_mac80211_bss_info_changed_ap(hw, vif, info, changed);
+		break;
+	case NL80211_IFTYPE_STATION:
+		mwl_mac80211_bss_info_changed_sta(hw, vif, info, changed);
+		break;
+	default:
+		break;
+	}
+}
+
+static void mwl_mac80211_configure_filter(struct ieee80211_hw *hw,
+					  unsigned int changed_flags,
+					  unsigned int *total_flags,
+					  u64 multicast)
+{
+	/* AP firmware doesn't allow fine-grained control over
+	 * the receive filter.
+	 */
+	*total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC;
+}
+
+static int mwl_mac80211_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)
+{
+	struct mwl_vif *mwl_vif;
+	int rc = 0;
+	u8 encr_type;
+	u8 *addr;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	if (!sta) {
+		addr = vif->addr;
+	} else {
+		addr = sta->addr;
+		if (vif->type == NL80211_IFTYPE_STATION)
+			ether_addr_copy(mwl_vif->bssid, addr);
+	}
+
+	if (cmd_param == SET_KEY) {
+		rc = mwl_fwcmd_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 = ENCR_TYPE_WEP;
+		} else if (key->cipher == WLAN_CIPHER_SUITE_CCMP) {
+			encr_type = ENCR_TYPE_AES;
+			if ((key->flags & IEEE80211_KEY_FLAG_PAIRWISE) == 0) {
+				if (vif->type != NL80211_IFTYPE_STATION)
+					mwl_vif->keyidx = key->keyidx;
+			}
+		} else if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+			encr_type = ENCR_TYPE_TKIP;
+		} else {
+			encr_type = ENCR_TYPE_DISABLE;
+		}
+
+		rc = mwl_fwcmd_update_encryption_enable(hw, vif, addr,
+							encr_type);
+		if (rc)
+			goto out;
+
+		mwl_vif->is_hw_crypto_enabled = true;
+	} else {
+		rc = mwl_fwcmd_encryption_remove_key(hw, vif, addr, key);
+		if (rc)
+			goto out;
+	}
+
+out:
+
+	return rc;
+}
+
+static int mwl_mac80211_set_rts_threshold(struct ieee80211_hw *hw,
+					  u32 value)
+{
+	return mwl_fwcmd_set_rts_threshold(hw, value);
+}
+
+static int mwl_mac80211_sta_add(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				struct ieee80211_sta *sta)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_vif *mwl_vif;
+	struct mwl_sta *sta_info;
+	struct ieee80211_key_conf *key;
+	int rc;
+	int i;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+	sta_info = mwl_dev_get_sta(sta);
+
+	memset(sta_info, 0, sizeof(*sta_info));
+	if (vif->type == NL80211_IFTYPE_MESH_POINT)
+		sta_info->is_mesh_node = true;
+	if (sta->ht_cap.ht_supported) {
+		sta_info->is_ampdu_allowed = true;
+		sta_info->is_amsdu_allowed = false;
+		if (sta->ht_cap.cap & IEEE80211_HT_CAP_MAX_AMSDU)
+			sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_8K;
+		else
+			sta_info->amsdu_ctrl.cap = MWL_AMSDU_SIZE_4K;
+	}
+	sta_info->iv16 = 1;
+	sta_info->iv32 = 0;
+	spin_lock_init(&sta_info->amsdu_lock);
+	spin_lock_bh(&priv->sta_lock);
+	list_add_tail(&sta_info->list, &priv->sta_list);
+	spin_unlock_bh(&priv->sta_lock);
+
+	if (vif->type == NL80211_IFTYPE_STATION)
+		mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr);
+
+	rc = mwl_fwcmd_set_new_stn_add(hw, vif, sta);
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		key = (struct ieee80211_key_conf *)mwl_vif->wep_key_conf[i].key;
+
+		if (mwl_vif->wep_key_conf[i].enabled)
+			mwl_mac80211_set_key(hw, SET_KEY, vif, sta, key);
+	}
+
+	return rc;
+}
+
+static int mwl_mac80211_sta_remove(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif,
+				   struct ieee80211_sta *sta)
+{
+	struct mwl_priv *priv = hw->priv;
+	int rc;
+	struct mwl_sta *sta_info = mwl_dev_get_sta(sta);
+
+	mwl_tx_del_sta_amsdu_pkts(sta);
+
+	spin_lock_bh(&priv->stream_lock);
+	mwl_fwcmd_del_sta_streams(hw, sta);
+	spin_unlock_bh(&priv->stream_lock);
+
+	mwl_tx_del_pkts_via_sta(hw, sta);
+
+	rc = mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr);
+
+	spin_lock_bh(&priv->sta_lock);
+	list_del(&sta_info->list);
+	spin_unlock_bh(&priv->sta_lock);
+
+	return rc;
+}
+
+static int mwl_mac80211_conf_tx(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				u16 queue,
+				const struct ieee80211_tx_queue_params *params)
+{
+	struct mwl_priv *priv = hw->priv;
+	int rc = 0;
+
+	if (WARN_ON(queue > SYSADPT_TX_WMM_QUEUES - 1))
+		return -EINVAL;
+
+	memcpy(&priv->wmm_params[queue], params, sizeof(*params));
+
+	if (!priv->wmm_enabled) {
+		rc = mwl_fwcmd_set_wmm_mode(hw, true);
+		priv->wmm_enabled = true;
+	}
+
+	if (!rc) {
+		int q = SYSADPT_TX_WMM_QUEUES - 1 - queue;
+
+		rc = mwl_fwcmd_set_edca_params(hw, q,
+					       params->cw_min, params->cw_max,
+					       params->aifs, params->txop);
+	}
+
+	return rc;
+}
+
+static int mwl_mac80211_get_stats(struct ieee80211_hw *hw,
+				  struct ieee80211_low_level_stats *stats)
+{
+	return mwl_fwcmd_get_stat(hw, stats);
+}
+
+static int mwl_mac80211_get_survey(struct ieee80211_hw *hw,
+				   int idx,
+				   struct survey_info *survey)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct ieee80211_conf *conf = &hw->conf;
+
+	if (idx != 0)
+		return -ENOENT;
+
+	survey->channel = conf->chandef.chan;
+	survey->filled = SURVEY_INFO_NOISE_DBM;
+	survey->noise = priv->noise;
+
+	return 0;
+}
+
+static int mwl_mac80211_ampdu_action(struct ieee80211_hw *hw,
+				     struct ieee80211_vif *vif,
+				     enum ieee80211_ampdu_mlme_action action,
+				     struct ieee80211_sta *sta,
+				     u16 tid, u16 *ssn, u8 buf_size, bool amsdu)
+{
+	int rc = 0;
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_ampdu_stream *stream;
+	u8 *addr = sta->addr, idx;
+	struct mwl_sta *sta_info;
+
+	sta_info = mwl_dev_get_sta(sta);
+
+	spin_lock_bh(&priv->stream_lock);
+
+	stream = mwl_fwcmd_lookup_stream(hw, addr, tid);
+
+	switch (action) {
+	case IEEE80211_AMPDU_RX_START:
+	case IEEE80211_AMPDU_RX_STOP:
+		break;
+	case IEEE80211_AMPDU_TX_START:
+		if (!sta_info->is_ampdu_allowed) {
+			wiphy_warn(hw->wiphy, "ampdu not allowed\n");
+			rc = -EPERM;
+			break;
+		}
+
+		if (!stream) {
+			stream = mwl_fwcmd_add_stream(hw, sta, tid);
+			if (!stream) {
+				wiphy_warn(hw->wiphy, "no stream found\n");
+				rc = -EPERM;
+				break;
+			}
+		}
+
+		spin_unlock_bh(&priv->stream_lock);
+		rc = mwl_fwcmd_check_ba(hw, stream, vif);
+		spin_lock_bh(&priv->stream_lock);
+		if (rc) {
+			mwl_fwcmd_remove_stream(hw, stream);
+			wiphy_err(hw->wiphy,
+				  "ampdu start error code: %d\n", rc);
+			rc = -EPERM;
+			break;
+		}
+		stream->state = AMPDU_STREAM_IN_PROGRESS;
+		*ssn = 0;
+		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) {
+				mwl_tx_del_ampdu_pkts(hw, sta, tid);
+				idx = stream->idx;
+				spin_unlock_bh(&priv->stream_lock);
+				mwl_fwcmd_destroy_ba(hw, idx);
+				spin_lock_bh(&priv->stream_lock);
+			}
+
+			mwl_fwcmd_remove_stream(hw, stream);
+			ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
+		} else {
+			rc = -EPERM;
+		}
+		break;
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		if (stream) {
+			if (WARN_ON(stream->state !=
+				    AMPDU_STREAM_IN_PROGRESS)) {
+				rc = -EPERM;
+				break;
+			}
+			spin_unlock_bh(&priv->stream_lock);
+			rc = mwl_fwcmd_create_ba(hw, stream, buf_size, vif);
+			spin_lock_bh(&priv->stream_lock);
+
+			if (!rc) {
+				stream->state = AMPDU_STREAM_ACTIVE;
+			} else {
+				idx = stream->idx;
+				spin_unlock_bh(&priv->stream_lock);
+				mwl_fwcmd_destroy_ba(hw, idx);
+				spin_lock_bh(&priv->stream_lock);
+				mwl_fwcmd_remove_stream(hw, stream);
+				wiphy_err(hw->wiphy,
+					  "ampdu operation error code: %d\n",
+					  rc);
+			}
+		} else {
+			rc = -EPERM;
+		}
+		break;
+	default:
+		rc = -ENOTSUPP;
+		break;
+	}
+
+	spin_unlock_bh(&priv->stream_lock);
+
+	return rc;
+}
+
+const struct ieee80211_ops mwl_mac80211_ops = {
+	.tx                 = mwl_mac80211_tx,
+	.start              = mwl_mac80211_start,
+	.stop               = mwl_mac80211_stop,
+	.add_interface      = mwl_mac80211_add_interface,
+	.remove_interface   = mwl_mac80211_remove_interface,
+	.config             = mwl_mac80211_config,
+	.bss_info_changed   = mwl_mac80211_bss_info_changed,
+	.configure_filter   = mwl_mac80211_configure_filter,
+	.set_key            = mwl_mac80211_set_key,
+	.set_rts_threshold  = mwl_mac80211_set_rts_threshold,
+	.sta_add            = mwl_mac80211_sta_add,
+	.sta_remove         = mwl_mac80211_sta_remove,
+	.conf_tx            = mwl_mac80211_conf_tx,
+	.get_stats          = mwl_mac80211_get_stats,
+	.get_survey         = mwl_mac80211_get_survey,
+	.ampdu_action       = mwl_mac80211_ampdu_action,
+};
diff --git a/drivers/net/wireless/marvell/mwlwifi/main.c b/drivers/net/wireless/marvell/mwlwifi/main.c
new file mode 100644
index 0000000..dc759d3
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/main.c
@@ -0,0 +1,739 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements main functions of this module. */
+
+#include <linux/module.h>
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#endif
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwdl.h"
+#include "fwcmd.h"
+#include "tx.h"
+#include "rx.h"
+#include "isr.h"
+#ifdef CONFIG_SUPPORT_MFG
+#include "mfg.h"
+#endif
+#ifdef CONFIG_DEBUG_FS
+#include "debugfs.h"
+#endif
+
+#define MWL_DESC         "Marvell 802.11ac Wireless Network Driver"
+#define MWL_DEV_NAME     "Marvell 802.11ac Adapter"
+
+#define FILE_PATH_LEN    64
+#define CMD_BUF_SIZE     0x4000
+
+static struct pci_device_id mwl_pci_id_tbl[] = {
+	{ PCI_VDEVICE(MARVELL, 0x2a55), .driver_data = MWL8864, },
+	{ PCI_VDEVICE(MARVELL, 0x2b38), .driver_data = MWL8897, },
+	{ },
+};
+
+static struct mwl_chip_info mwl_chip_tbl[] = {
+	[MWL8864] = {
+		.part_name	= "88W8864",
+		.fw_image	= "mwlwifi/88W8864.bin",
+		.antenna_tx	= ANTENNA_TX_4_AUTO,
+		.antenna_rx	= ANTENNA_RX_4_AUTO,
+	},
+	[MWL8897] = {
+		.part_name	= "88W8897",
+		.fw_image	= "mwlwifi/88W8897.bin",
+		.antenna_tx	= ANTENNA_TX_2,
+		.antenna_rx	= ANTENNA_RX_2,
+	},
+};
+
+static const struct ieee80211_channel mwl_channels_24[] = {
+	{ .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 mwl_rates_24[] = {
+	{ .bitrate = 10, .hw_value = 2, },
+	{ .bitrate = 20, .hw_value = 4, },
+	{ .bitrate = 55, .hw_value = 11, },
+	{ .bitrate = 110, .hw_value = 22, },
+	{ .bitrate = 220, .hw_value = 44, },
+	{ .bitrate = 60, .hw_value = 12, },
+	{ .bitrate = 90, .hw_value = 18, },
+	{ .bitrate = 120, .hw_value = 24, },
+	{ .bitrate = 180, .hw_value = 36, },
+	{ .bitrate = 240, .hw_value = 48, },
+	{ .bitrate = 360, .hw_value = 72, },
+	{ .bitrate = 480, .hw_value = 96, },
+	{ .bitrate = 540, .hw_value = 108, },
+};
+
+static const struct ieee80211_channel mwl_channels_50[] = {
+	{ .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, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5260, .hw_value = 52, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5280, .hw_value = 56, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5300, .hw_value = 60, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5320, .hw_value = 64, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5500, .hw_value = 100, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5520, .hw_value = 104, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5540, .hw_value = 108, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5560, .hw_value = 112, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5580, .hw_value = 116, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5600, .hw_value = 120, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5620, .hw_value = 124, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5640, .hw_value = 128, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5660, .hw_value = 132, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5680, .hw_value = 136, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5700, .hw_value = 140, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5720, .hw_value = 144, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5745, .hw_value = 149, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5765, .hw_value = 153, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5785, .hw_value = 157, },
+	{ .band = IEEE80211_BAND_5GHZ, .center_freq = 5805, .hw_value = 161, },
+};
+
+static const struct ieee80211_rate mwl_rates_50[] = {
+	{ .bitrate = 60, .hw_value = 12, },
+	{ .bitrate = 90, .hw_value = 18, },
+	{ .bitrate = 120, .hw_value = 24, },
+	{ .bitrate = 180, .hw_value = 36, },
+	{ .bitrate = 240, .hw_value = 48, },
+	{ .bitrate = 360, .hw_value = 72, },
+	{ .bitrate = 480, .hw_value = 96, },
+	{ .bitrate = 540, .hw_value = 108, },
+};
+
+static const struct ieee80211_iface_limit ap_if_limits[] = {
+	{ .max = SYSADPT_NUM_OF_AP,	.types = BIT(NL80211_IFTYPE_AP) },
+#ifdef CONFIG_MAC80211_MESH
+	{ .max = 1,	.types = BIT(NL80211_IFTYPE_MESH_POINT) },
+#endif
+	{ .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 = SYSADPT_NUM_OF_AP,
+	.num_different_channels = 1,
+};
+
+static int mwl_alloc_pci_resource(struct mwl_priv *priv)
+{
+	struct pci_dev *pdev = priv->pdev;
+	void __iomem *addr;
+
+	priv->next_bar_num = 1;	/* 32-bit */
+	if (pci_resource_flags(pdev, 0) & 0x04)
+		priv->next_bar_num = 2;	/* 64-bit */
+
+	addr = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
+	if (IS_ERR(addr)) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot reserve PCI memory region 0\n",
+			  MWL_DRV_NAME);
+		goto err;
+	}
+	priv->iobase0 = addr;
+	wiphy_debug(priv->hw->wiphy, "priv->iobase0 = %p\n", priv->iobase0);
+
+	addr = devm_ioremap_resource(&pdev->dev,
+				     &pdev->resource[priv->next_bar_num]);
+	if (IS_ERR(addr)) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot reserve PCI memory region 1\n",
+			  MWL_DRV_NAME);
+		goto err;
+	}
+	priv->iobase1 = addr;
+	wiphy_debug(priv->hw->wiphy, "priv->iobase1 = %p\n", priv->iobase1);
+
+	priv->pcmd_buf =
+		(unsigned short *)dmam_alloc_coherent(&priv->pdev->dev,
+						      CMD_BUF_SIZE,
+						      &priv->pphys_cmd_buf,
+						      GFP_KERNEL);
+	if (!priv->pcmd_buf) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot alloc memory for command buffer\n",
+			  MWL_DRV_NAME);
+		goto err;
+	}
+	wiphy_debug(priv->hw->wiphy,
+		    "priv->pcmd_buf = %p  priv->pphys_cmd_buf = %p\n",
+		    priv->pcmd_buf,
+		    (void *)priv->pphys_cmd_buf);
+	memset(priv->pcmd_buf, 0x00, CMD_BUF_SIZE);
+
+	return 0;
+
+err:
+	wiphy_err(priv->hw->wiphy, "pci alloc fail\n");
+
+	return -EIO;
+}
+
+static int mwl_init_firmware(struct mwl_priv *priv, const char *fw_name)
+{
+	struct pci_dev *pdev;
+	int rc = 0;
+
+	pdev = priv->pdev;
+
+#ifdef CONFIG_SUPPORT_MFG
+	if (priv->mfg_mode)
+		rc = mwl_mfg_request_firmware(priv);
+	else
+#endif
+		rc = request_firmware((const struct firmware **)&priv->fw_ucode,
+				      fw_name, &priv->pdev->dev);
+
+	if (rc) {
+		if (priv->mfg_mode)
+			wiphy_err(priv->hw->wiphy, "cannot find firmware\n");
+		else
+			wiphy_err(priv->hw->wiphy,
+				  "%s: cannot find firmware image <%s>\n",
+				  MWL_DRV_NAME, fw_name);
+		goto err_load_fw;
+	}
+
+	rc = mwl_fwdl_download_firmware(priv->hw);
+	if (rc) {
+		if (priv->mfg_mode)
+			wiphy_err(priv->hw->wiphy, "download firmware fail\n");
+		else
+			wiphy_err(priv->hw->wiphy,
+				  "%s: cannot download firmware image <%s>\n",
+				  MWL_DRV_NAME, fw_name);
+		goto err_download_fw;
+	}
+
+	return rc;
+
+err_download_fw:
+
+#ifdef CONFIG_SUPPORT_MFG
+	if (priv->mfg_mode)
+		mwl_mfg_release_firmware(priv);
+	else
+#endif
+		release_firmware(priv->fw_ucode);
+
+err_load_fw:
+
+	wiphy_err(priv->hw->wiphy, "firmware init fail\n");
+
+	return rc;
+}
+
+static void mwl_process_of_dts(struct mwl_priv *priv)
+{
+#ifdef CONFIG_OF
+	struct property *prop;
+	u32 prop_value;
+
+	priv->dt_node =
+		of_find_node_by_name(pci_bus_to_OF_node(priv->pdev->bus),
+				     "mwlwifi");
+	if (!priv->dt_node)
+		return;
+
+	/* look for all matching property names */
+	for_each_property_of_node(priv->dt_node, prop) {
+		if (strcmp(prop->name, "marvell,2ghz") == 0)
+			priv->disable_2g = true;
+		if (strcmp(prop->name, "marvell,5ghz") == 0)
+			priv->disable_5g = true;
+		if (strcmp(prop->name, "marvell,chainmask") == 0) {
+			prop_value = be32_to_cpu(*((__be32 *)prop->value));
+			if (prop_value == 2)
+				priv->antenna_tx = ANTENNA_TX_2;
+
+			prop_value = be32_to_cpu(*((__be32 *)
+						 (prop->value + 4)));
+			if (prop_value == 2)
+				priv->antenna_rx = ANTENNA_RX_2;
+		}
+	}
+
+	priv->pwr_node = of_find_node_by_name(priv->dt_node,
+					      "marvell,powertable");
+#endif
+}
+
+static void mwl_set_ht_caps(struct mwl_priv *priv,
+			    struct ieee80211_supported_band *band)
+{
+	struct ieee80211_hw *hw;
+
+	hw = priv->hw;
+
+	band->ht_cap.ht_supported = 1;
+
+	band->ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING;
+	band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+	band->ht_cap.cap |= IEEE80211_HT_CAP_SM_PS;
+	band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+	band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+
+	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
+	band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+	band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_4;
+
+	band->ht_cap.mcs.rx_mask[0] = 0xff;
+	band->ht_cap.mcs.rx_mask[1] = 0xff;
+	if (priv->antenna_rx == ANTENNA_RX_4_AUTO)
+		band->ht_cap.mcs.rx_mask[2] = 0xff;
+	band->ht_cap.mcs.rx_mask[4] = 0x01;
+
+	band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+}
+
+static void mwl_set_vht_caps(struct mwl_priv *priv,
+			     struct ieee80211_supported_band *band)
+{
+	band->vht_cap.vht_supported = 1;
+
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_RXLDPC;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_RXSTBC_1;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN;
+	band->vht_cap.cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
+
+	if (priv->antenna_rx == ANTENNA_RX_2)
+		band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xfffa);
+	else
+		band->vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(0xffea);
+
+	if (priv->antenna_tx == ANTENNA_TX_2)
+		band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xfffa);
+	else
+		band->vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(0xffea);
+}
+
+static void mwl_set_caps(struct mwl_priv *priv)
+{
+	struct ieee80211_hw *hw;
+
+	hw = priv->hw;
+
+	/* set up band information for 2.4G */
+	if (!priv->disable_2g) {
+		BUILD_BUG_ON(sizeof(priv->channels_24) !=
+			     sizeof(mwl_channels_24));
+		memcpy(priv->channels_24, mwl_channels_24,
+		       sizeof(mwl_channels_24));
+
+		BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl_rates_24));
+		memcpy(priv->rates_24, mwl_rates_24, sizeof(mwl_rates_24));
+
+		priv->band_24.band = IEEE80211_BAND_2GHZ;
+		priv->band_24.channels = priv->channels_24;
+		priv->band_24.n_channels = ARRAY_SIZE(mwl_channels_24);
+		priv->band_24.bitrates = priv->rates_24;
+		priv->band_24.n_bitrates = ARRAY_SIZE(mwl_rates_24);
+
+		mwl_set_ht_caps(priv, &priv->band_24);
+		mwl_set_vht_caps(priv, &priv->band_24);
+
+		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24;
+	}
+
+	/* set up band information for 5G */
+	if (!priv->disable_5g) {
+		BUILD_BUG_ON(sizeof(priv->channels_50) !=
+			     sizeof(mwl_channels_50));
+		memcpy(priv->channels_50, mwl_channels_50,
+		       sizeof(mwl_channels_50));
+
+		BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl_rates_50));
+		memcpy(priv->rates_50, mwl_rates_50, sizeof(mwl_rates_50));
+
+		priv->band_50.band = IEEE80211_BAND_5GHZ;
+		priv->band_50.channels = priv->channels_50;
+		priv->band_50.n_channels = ARRAY_SIZE(mwl_channels_50);
+		priv->band_50.bitrates = priv->rates_50;
+		priv->band_50.n_bitrates = ARRAY_SIZE(mwl_rates_50);
+
+		mwl_set_ht_caps(priv, &priv->band_50);
+		mwl_set_vht_caps(priv, &priv->band_50);
+
+		hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50;
+	}
+}
+
+static int mwl_wl_init(struct mwl_priv *priv)
+{
+	struct ieee80211_hw *hw;
+	int rc;
+	int i;
+
+	hw = priv->hw;
+
+	hw->extra_tx_headroom = SYSADPT_MIN_BYTES_HEADROOM;
+	hw->queues = SYSADPT_TX_WMM_QUEUES;
+
+	/* Set rssi values to dBm */
+	ieee80211_hw_set(hw, SIGNAL_DBM);
+	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
+
+	/* Ask mac80211 not to trigger PS mode
+	 * based on PM bit of incoming frames.
+	 */
+	ieee80211_hw_set(hw, AP_LINK_PS);
+
+	hw->vif_data_size = sizeof(struct mwl_vif);
+	hw->sta_data_size = sizeof(struct mwl_sta);
+
+	priv->ap_macids_supported = 0x0000ffff;
+	priv->sta_macids_supported = 0x00010000;
+	priv->macids_used = 0;
+	INIT_LIST_HEAD(&priv->vif_list);
+	INIT_LIST_HEAD(&priv->sta_list);
+
+	/* Set default radio state, preamble and wmm */
+	priv->radio_on = false;
+	priv->radio_short_preamble = false;
+	priv->wmm_enabled = false;
+
+	priv->powinited = 0;
+
+	/* Handle watchdog ba events */
+	INIT_WORK(&priv->watchdog_ba_handle, mwl_watchdog_ba_events);
+
+	tasklet_init(&priv->tx_task, (void *)mwl_tx_done, (unsigned long)hw);
+	tasklet_disable(&priv->tx_task);
+	tasklet_init(&priv->rx_task, (void *)mwl_rx_recv, (unsigned long)hw);
+	tasklet_disable(&priv->rx_task);
+	tasklet_init(&priv->qe_task,
+		     (void *)mwl_tx_flush_amsdu, (unsigned long)hw);
+	tasklet_disable(&priv->qe_task);
+	priv->txq_limit = SYSADPT_TX_QUEUE_LIMIT;
+	priv->is_tx_schedule = false;
+	priv->recv_limit = SYSADPT_RECEIVE_LIMIT;
+	priv->is_rx_schedule = false;
+	priv->is_qe_schedule = false;
+
+	spin_lock_init(&priv->tx_desc_lock);
+	spin_lock_init(&priv->rx_desc_lock);
+	spin_lock_init(&priv->fwcmd_lock);
+	spin_lock_init(&priv->vif_lock);
+	spin_lock_init(&priv->sta_lock);
+	spin_lock_init(&priv->stream_lock);
+
+	rc = mwl_tx_init(hw);
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to initialize TX\n",
+			  MWL_DRV_NAME);
+		goto err_mwl_tx_init;
+	}
+
+	rc = mwl_rx_init(hw);
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to initialize RX\n",
+			  MWL_DRV_NAME);
+		goto err_mwl_rx_init;
+	}
+
+	rc = mwl_fwcmd_get_hw_specs(hw);
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to get HW specifications\n",
+			  MWL_DRV_NAME);
+		goto err_get_hw_specs;
+	}
+
+	SET_IEEE80211_PERM_ADDR(hw, priv->hw_data.mac_addr);
+
+	writel(priv->desc_data[0].pphys_tx_ring,
+	       priv->iobase0 + priv->desc_data[0].wcb_base);
+	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+		writel(priv->desc_data[i].pphys_tx_ring,
+		       priv->iobase0 + priv->desc_data[i].wcb_base);
+	writel(priv->desc_data[0].pphys_rx_ring,
+	       priv->iobase0 + priv->desc_data[0].rx_desc_read);
+	writel(priv->desc_data[0].pphys_rx_ring,
+	       priv->iobase0 + priv->desc_data[0].rx_desc_write);
+
+	rc = mwl_fwcmd_set_hw_specs(hw);
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to set HW specifications\n",
+			  MWL_DRV_NAME);
+		goto err_set_hw_specs;
+	}
+
+	wiphy_info(hw->wiphy,
+		   "firmware version: 0x%x\n", priv->hw_data.fw_release_num);
+
+	mwl_fwcmd_radio_disable(hw);
+
+	mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_TX, priv->antenna_tx);
+
+	mwl_fwcmd_rf_antenna(hw, WL_ANTENNATYPE_RX, priv->antenna_rx);
+
+	hw->wiphy->interface_modes = 0;
+	hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
+#ifdef CONFIG_MAC80211_MESH
+	hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MESH_POINT);
+#endif
+	hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
+	hw->wiphy->iface_combinations = &ap_if_comb;
+	hw->wiphy->n_iface_combinations = 1;
+
+	mwl_set_caps(priv);
+
+	rc = ieee80211_register_hw(hw);
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to register device\n",
+			  MWL_DRV_NAME);
+		goto err_register_hw;
+	}
+
+	rc = request_irq(priv->pdev->irq, mwl_isr,
+			 IRQF_SHARED, MWL_DRV_NAME, hw);
+	if (rc) {
+		priv->irq = -1;
+		wiphy_err(hw->wiphy, "fail to register IRQ handler\n");
+		goto err_register_irq;
+	}
+	priv->irq = priv->pdev->irq;
+
+	return rc;
+
+err_register_irq:
+err_register_hw:
+err_set_hw_specs:
+err_get_hw_specs:
+
+	mwl_rx_deinit(hw);
+
+err_mwl_rx_init:
+
+	mwl_tx_deinit(hw);
+
+err_mwl_tx_init:
+
+	wiphy_err(hw->wiphy, "init fail\n");
+
+	return rc;
+}
+
+static void mwl_wl_deinit(struct mwl_priv *priv)
+{
+	struct ieee80211_hw *hw;
+
+	hw = priv->hw;
+
+	if (priv->irq != -1) {
+		free_irq(priv->pdev->irq, hw);
+		priv->irq = -1;
+	}
+
+	ieee80211_unregister_hw(hw);
+	mwl_rx_deinit(hw);
+	mwl_tx_deinit(hw);
+	tasklet_kill(&priv->qe_task);
+	tasklet_kill(&priv->rx_task);
+	tasklet_kill(&priv->tx_task);
+	cancel_work_sync(&priv->watchdog_ba_handle);
+	mwl_fwcmd_reset(hw);
+}
+
+static int mwl_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	static bool printed_version;
+	struct ieee80211_hw *hw;
+	struct mwl_priv *priv;
+	const char *fw_name;
+	int rc = 0;
+
+	if (id->driver_data >= MWLUNKNOWN)
+		return -ENODEV;
+
+	if (!printed_version) {
+		pr_info("<<%s version %s>>",
+			MWL_DESC, MWL_DRV_VERSION);
+		printed_version = true;
+	}
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		pr_err("%s: cannot enable new PCI device",
+		       MWL_DRV_NAME);
+		return rc;
+	}
+
+	rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+	if (rc) {
+		pr_err("%s: 32-bit PCI DMA not supported",
+		       MWL_DRV_NAME);
+		goto err_pci_disable_device;
+	}
+
+	pci_set_master(pdev);
+
+	hw = ieee80211_alloc_hw(sizeof(*priv), &mwl_mac80211_ops);
+	if (!hw) {
+		pr_err("%s: ieee80211 alloc failed",
+		       MWL_DRV_NAME);
+		rc = -ENOMEM;
+		goto err_pci_disable_device;
+	}
+
+	SET_IEEE80211_DEV(hw, &pdev->dev);
+	pci_set_drvdata(pdev, hw);
+
+	priv = hw->priv;
+	priv->hw = hw;
+	priv->pdev = pdev;
+	priv->chip_type = id->driver_data;
+	priv->disable_2g = false;
+	priv->disable_5g = false;
+	priv->antenna_tx = mwl_chip_tbl[priv->chip_type].antenna_tx;
+	priv->antenna_rx = mwl_chip_tbl[priv->chip_type].antenna_rx;
+
+	rc = mwl_alloc_pci_resource(priv);
+	if (rc)
+		goto err_alloc_pci_resource;
+
+	fw_name = mwl_chip_tbl[priv->chip_type].fw_image;
+
+#ifdef CONFIG_SUPPORT_MFG
+	if (mfg_mode)
+		mwl_mfg_handler_init(priv);
+#endif
+
+	rc = mwl_init_firmware(priv, fw_name);
+
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to initialize firmware\n",
+			  MWL_DRV_NAME);
+		goto err_init_firmware;
+	}
+
+	/* firmware is loaded to H/W, it can be released now */
+#ifdef CONFIG_SUPPORT_MFG
+	if (priv->mfg_mode)
+		mwl_mfg_release_firmware(priv);
+	else
+#endif
+		release_firmware(priv->fw_ucode);
+
+	mwl_process_of_dts(priv);
+
+	rc = mwl_wl_init(priv);
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to initialize wireless lan\n",
+			  MWL_DRV_NAME);
+		goto err_wl_init;
+	}
+
+	wiphy_info(priv->hw->wiphy,
+		   "2G: %s\n", priv->disable_2g ? "disable" : "enable");
+	wiphy_info(priv->hw->wiphy,
+		   "5G: %s\n", priv->disable_5g ? "disable" : "enable");
+
+	if (priv->antenna_tx == ANTENNA_TX_4_AUTO)
+		wiphy_info(priv->hw->wiphy, "TX: 4 antennas\n");
+	else if (priv->antenna_tx == ANTENNA_TX_2)
+		wiphy_info(priv->hw->wiphy, "TX: 2 antennas\n");
+	else
+		wiphy_info(priv->hw->wiphy, "TX: unknown\n");
+	if (priv->antenna_rx == ANTENNA_RX_4_AUTO)
+		wiphy_info(priv->hw->wiphy, "RX: 4 antennas\n");
+	else if (priv->antenna_rx == ANTENNA_RX_2)
+		wiphy_info(priv->hw->wiphy, "RX: 2 antennas\n");
+	else
+		wiphy_info(priv->hw->wiphy, "RX: unknown\n");
+
+#ifdef CONFIG_DEBUG_FS
+	mwl_debugfs_init(hw);
+#endif
+
+	return rc;
+
+err_wl_init:
+err_init_firmware:
+
+	mwl_fwcmd_reset(hw);
+
+err_alloc_pci_resource:
+
+	pci_set_drvdata(pdev, NULL);
+	ieee80211_free_hw(hw);
+
+err_pci_disable_device:
+
+	pci_disable_device(pdev);
+
+	return rc;
+}
+
+static void mwl_remove(struct pci_dev *pdev)
+{
+	struct ieee80211_hw *hw = pci_get_drvdata(pdev);
+	struct mwl_priv *priv;
+
+	if (!hw)
+		return;
+
+	priv = hw->priv;
+
+	mwl_wl_deinit(priv);
+	pci_set_drvdata(pdev, NULL);
+	ieee80211_free_hw(hw);
+	pci_disable_device(pdev);
+
+#ifdef CONFIG_DEBUG_FS
+	mwl_debugfs_remove(hw);
+#endif
+}
+
+static struct pci_driver mwl_pci_driver = {
+	.name     = MWL_DRV_NAME,
+	.id_table = mwl_pci_id_tbl,
+	.probe    = mwl_probe,
+	.remove   = mwl_remove
+};
+
+module_pci_driver(mwl_pci_driver);
+
+MODULE_DESCRIPTION(MWL_DESC);
+MODULE_VERSION(MWL_DRV_VERSION);
+MODULE_AUTHOR("Marvell Semiconductor, Inc.");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE(MWL_DEV_NAME);
+MODULE_DEVICE_TABLE(pci, mwl_pci_id_tbl);
diff --git a/drivers/net/wireless/marvell/mwlwifi/rx.c b/drivers/net/wireless/marvell/mwlwifi/rx.c
new file mode 100644
index 0000000..4308107
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/rx.c
@@ -0,0 +1,624 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements receive related functions. */
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "rx.h"
+
+#define MAX_NUM_RX_RING_BYTES  (SYSADPT_MAX_NUM_RX_DESC * \
+				sizeof(struct mwl_rx_desc))
+
+#define MAX_NUM_RX_HNDL_BYTES  (SYSADPT_MAX_NUM_RX_DESC * \
+				sizeof(struct mwl_rx_hndl))
+
+#define DECRYPT_ERR_MASK        0x80
+#define GENERAL_DECRYPT_ERR     0xFF
+#define TKIP_DECRYPT_MIC_ERR    0x02
+#define WEP_DECRYPT_ICV_ERR     0x04
+#define TKIP_DECRYPT_ICV_ERR    0x08
+
+#define W836X_RSSI_OFFSET       8
+
+/* Receive rate information constants */
+#define RX_RATE_INFO_FORMAT_11A       0
+#define RX_RATE_INFO_FORMAT_11B       1
+#define RX_RATE_INFO_FORMAT_11N       2
+#define RX_RATE_INFO_FORMAT_11AC      4
+
+#define RX_RATE_INFO_HT20             0
+#define RX_RATE_INFO_HT40             1
+#define RX_RATE_INFO_HT80             2
+
+#define RX_RATE_INFO_LONG_INTERVAL    0
+#define RX_RATE_INFO_SHORT_INTERVAL   1
+
+static int mwl_rx_ring_alloc(struct mwl_priv *priv)
+{
+	struct mwl_desc_data *desc;
+
+	desc = &priv->desc_data[0];
+
+	desc->prx_ring = (struct mwl_rx_desc *)
+		dma_alloc_coherent(&priv->pdev->dev,
+				   MAX_NUM_RX_RING_BYTES,
+				   &desc->pphys_rx_ring,
+				   GFP_KERNEL);
+
+	if (!desc->prx_ring) {
+		wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
+		return -ENOMEM;
+	}
+
+	memset(desc->prx_ring, 0x00, MAX_NUM_RX_RING_BYTES);
+
+	desc->rx_hndl = kmalloc(MAX_NUM_RX_HNDL_BYTES, GFP_KERNEL);
+
+	if (!desc->rx_hndl) {
+		dma_free_coherent(&priv->pdev->dev,
+				  MAX_NUM_RX_RING_BYTES,
+				  desc->prx_ring,
+				  desc->pphys_rx_ring);
+		return -ENOMEM;
+	}
+
+	memset(desc->rx_hndl, 0x00, MAX_NUM_RX_HNDL_BYTES);
+
+	return 0;
+}
+
+static int mwl_rx_ring_init(struct mwl_priv *priv)
+{
+	struct mwl_desc_data *desc;
+	int i;
+	struct mwl_rx_hndl *rx_hndl;
+	dma_addr_t dma;
+	u32 val;
+
+	desc = &priv->desc_data[0];
+
+	if (desc->prx_ring) {
+		desc->rx_buf_size = SYSADPT_MAX_AGGR_SIZE;
+
+		for (i = 0; i < SYSADPT_MAX_NUM_RX_DESC; i++) {
+			rx_hndl = &desc->rx_hndl[i];
+			rx_hndl->psk_buff =
+				dev_alloc_skb(desc->rx_buf_size);
+
+			if (!rx_hndl->psk_buff) {
+				wiphy_err(priv->hw->wiphy,
+					  "rxdesc %i: no skbuff available\n",
+					  i);
+				return -ENOMEM;
+			}
+
+			skb_reserve(rx_hndl->psk_buff,
+				    SYSADPT_MIN_BYTES_HEADROOM);
+			desc->prx_ring[i].rx_control =
+				EAGLE_RXD_CTRL_DRIVER_OWN;
+			desc->prx_ring[i].status = EAGLE_RXD_STATUS_OK;
+			desc->prx_ring[i].qos_ctrl = 0x0000;
+			desc->prx_ring[i].channel = 0x00;
+			desc->prx_ring[i].rssi = 0x00;
+			desc->prx_ring[i].pkt_len =
+				cpu_to_le16(SYSADPT_MAX_AGGR_SIZE);
+			dma = pci_map_single(priv->pdev,
+					     rx_hndl->psk_buff->data,
+					     desc->rx_buf_size,
+					     PCI_DMA_FROMDEVICE);
+			if (pci_dma_mapping_error(priv->pdev, dma)) {
+				wiphy_err(priv->hw->wiphy,
+					  "failed to map pci memory!\n");
+				return -ENOMEM;
+			}
+			desc->prx_ring[i].pphys_buff_data = cpu_to_le32(dma);
+			val = (u32)desc->pphys_rx_ring +
+			      ((i + 1) * sizeof(struct mwl_rx_desc));
+			desc->prx_ring[i].pphys_next = cpu_to_le32(val);
+			rx_hndl->pdesc = &desc->prx_ring[i];
+			if (i < (SYSADPT_MAX_NUM_RX_DESC - 1))
+				rx_hndl->pnext = &desc->rx_hndl[i + 1];
+		}
+		desc->prx_ring[SYSADPT_MAX_NUM_RX_DESC - 1].pphys_next =
+			cpu_to_le32((u32)desc->pphys_rx_ring);
+		desc->rx_hndl[SYSADPT_MAX_NUM_RX_DESC - 1].pnext =
+			&desc->rx_hndl[0];
+		desc->pnext_rx_hndl = &desc->rx_hndl[0];
+
+		return 0;
+	}
+
+	wiphy_err(priv->hw->wiphy, "no valid RX mem\n");
+
+	return -ENOMEM;
+}
+
+static void mwl_rx_ring_cleanup(struct mwl_priv *priv)
+{
+	struct mwl_desc_data *desc;
+	int i;
+	struct mwl_rx_hndl *rx_hndl;
+
+	desc = &priv->desc_data[0];
+
+	if (desc->prx_ring) {
+		for (i = 0; i < SYSADPT_MAX_NUM_RX_DESC; i++) {
+			rx_hndl = &desc->rx_hndl[i];
+			if (!rx_hndl->psk_buff)
+				continue;
+
+			pci_unmap_single(priv->pdev,
+					 le32_to_cpu
+					 (rx_hndl->pdesc->pphys_buff_data),
+					 desc->rx_buf_size,
+					 PCI_DMA_FROMDEVICE);
+
+			dev_kfree_skb_any(rx_hndl->psk_buff);
+
+			wiphy_info(priv->hw->wiphy,
+				   "unmapped+free'd %i 0x%p 0x%x %i\n",
+				   i, rx_hndl->psk_buff->data,
+				   le32_to_cpu(rx_hndl->pdesc->pphys_buff_data),
+				   desc->rx_buf_size);
+
+			rx_hndl->psk_buff = NULL;
+		}
+	}
+}
+
+static void mwl_rx_ring_free(struct mwl_priv *priv)
+{
+	struct mwl_desc_data *desc;
+
+	desc = &priv->desc_data[0];
+
+	if (desc->prx_ring) {
+		mwl_rx_ring_cleanup(priv);
+
+		dma_free_coherent(&priv->pdev->dev,
+				  MAX_NUM_RX_RING_BYTES,
+				  desc->prx_ring,
+				  desc->pphys_rx_ring);
+
+		desc->prx_ring = NULL;
+	}
+
+	kfree(desc->rx_hndl);
+
+	desc->pnext_rx_hndl = NULL;
+}
+
+static inline void mwl_rx_prepare_status(struct mwl_rx_desc *pdesc,
+					 struct ieee80211_rx_status *status)
+{
+	u16 rate, format, nss, bw, gi, rt;
+
+	memset(status, 0, sizeof(*status));
+
+	status->signal = -(pdesc->rssi + W836X_RSSI_OFFSET);
+
+	rate = le16_to_cpu(pdesc->rate);
+	format = rate & MWL_RX_RATE_FORMAT_MASK;
+	nss = (rate & MWL_RX_RATE_NSS_MASK) >> MWL_RX_RATE_NSS_SHIFT;
+	bw = (rate & MWL_RX_RATE_BW_MASK) >> MWL_RX_RATE_BW_SHIFT;
+	gi = (rate & MWL_RX_RATE_GI_MASK) >> MWL_RX_RATE_GI_SHIFT;
+	rt = (rate & MWL_RX_RATE_RT_MASK) >> MWL_RX_RATE_RT_SHIFT;
+
+	switch (format) {
+	case RX_RATE_INFO_FORMAT_11N:
+		status->flag |= RX_FLAG_HT;
+		if (bw == RX_RATE_INFO_HT40)
+			status->flag |= RX_FLAG_40MHZ;
+		if (gi == RX_RATE_INFO_SHORT_INTERVAL)
+			status->flag |= RX_FLAG_SHORT_GI;
+		break;
+	case RX_RATE_INFO_FORMAT_11AC:
+		status->flag |= RX_FLAG_VHT;
+		if (bw == RX_RATE_INFO_HT40)
+			status->flag |= RX_FLAG_40MHZ;
+		if (bw == RX_RATE_INFO_HT80)
+			status->vht_flag |= RX_VHT_FLAG_80MHZ;
+		if (gi == RX_RATE_INFO_SHORT_INTERVAL)
+			status->flag |= RX_FLAG_SHORT_GI;
+		status->vht_nss = (nss + 1);
+		break;
+	}
+
+	status->rate_idx = rt;
+
+	if (pdesc->channel > BAND_24_CHANNEL_NUM) {
+		status->band = IEEE80211_BAND_5GHZ;
+		if ((!(status->flag & RX_FLAG_HT)) &&
+		    (!(status->flag & RX_FLAG_VHT))) {
+			status->rate_idx -= 5;
+			if (status->rate_idx >= BAND_50_RATE_NUM)
+				status->rate_idx = BAND_50_RATE_NUM - 1;
+		}
+	} else {
+		status->band = IEEE80211_BAND_2GHZ;
+		if ((!(status->flag & RX_FLAG_HT)) &&
+		    (!(status->flag & RX_FLAG_VHT))) {
+			if (status->rate_idx >= BAND_24_RATE_NUM)
+				status->rate_idx = BAND_24_RATE_NUM - 1;
+		}
+	}
+
+	status->freq = ieee80211_channel_to_frequency(pdesc->channel,
+						      status->band);
+
+	/* check if status has a specific error bit (bit 7) set or indicates
+	 * a general decrypt error
+	 */
+	if ((pdesc->status == GENERAL_DECRYPT_ERR) ||
+	    (pdesc->status & DECRYPT_ERR_MASK)) {
+		/* check if status is not equal to 0xFF
+		 * the 0xFF check is for backward compatibility
+		 */
+		if (pdesc->status != GENERAL_DECRYPT_ERR) {
+			if (((pdesc->status & (~DECRYPT_ERR_MASK)) &
+			    TKIP_DECRYPT_MIC_ERR) && !((pdesc->status &
+			    (WEP_DECRYPT_ICV_ERR | TKIP_DECRYPT_ICV_ERR)))) {
+				status->flag |= RX_FLAG_MMIC_ERROR;
+			}
+		}
+	}
+}
+
+static inline void mwl_rx_enable_sta_amsdu(struct mwl_priv *priv,
+					   u8 *sta_addr)
+{
+	struct mwl_sta *sta_info;
+	struct ieee80211_sta *sta;
+
+	spin_lock_bh(&priv->sta_lock);
+	list_for_each_entry(sta_info, &priv->sta_list, list) {
+		sta = container_of((char *)sta_info, struct ieee80211_sta,
+				   drv_priv[0]);
+		if (ether_addr_equal(sta->addr, sta_addr)) {
+			sta_info->is_amsdu_allowed = true;
+			break;
+		}
+	}
+	spin_unlock_bh(&priv->sta_lock);
+}
+
+static inline struct mwl_vif *mwl_rx_find_vif_bss(struct mwl_priv *priv,
+						  u8 *bssid)
+{
+	struct mwl_vif *mwl_vif;
+
+	spin_lock_bh(&priv->vif_lock);
+	list_for_each_entry(mwl_vif, &priv->vif_list, list) {
+		if (ether_addr_equal(bssid, mwl_vif->bssid)) {
+			spin_unlock_bh(&priv->vif_lock);
+			return mwl_vif;
+		}
+	}
+	spin_unlock_bh(&priv->vif_lock);
+
+	return NULL;
+}
+
+static inline void mwl_rx_remove_dma_header(struct sk_buff *skb, __le16 qos)
+{
+	struct mwl_dma_data *tr;
+	int hdrlen;
+
+	tr = (struct mwl_dma_data *)skb->data;
+	hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
+
+	if (hdrlen != sizeof(tr->wh)) {
+		if (ieee80211_is_data_qos(tr->wh.frame_control)) {
+			memmove(tr->data - hdrlen, &tr->wh, hdrlen - 2);
+			*((__le16 *)(tr->data - 2)) = qos;
+		} else {
+			memmove(tr->data - hdrlen, &tr->wh, hdrlen);
+		}
+	}
+
+	if (hdrlen != sizeof(*tr))
+		skb_pull(skb, sizeof(*tr) - hdrlen);
+}
+
+static inline bool mwl_rx_process_mesh_amsdu(struct mwl_priv *priv,
+					     struct sk_buff *skb,
+					     struct ieee80211_rx_status *status)
+{
+	struct ieee80211_hdr *wh;
+	struct mwl_sta *sta_info;
+	struct ieee80211_sta *sta;
+	u8 *qc;
+	int wh_len;
+	int len;
+	u8 pad;
+	u8 *data;
+	u16 frame_len;
+	struct sk_buff *newskb;
+
+	wh = (struct ieee80211_hdr *)skb->data;
+
+	spin_lock_bh(&priv->sta_lock);
+	list_for_each_entry(sta_info, &priv->sta_list, list) {
+		sta = container_of((char *)sta_info, struct ieee80211_sta,
+				   drv_priv[0]);
+		if (ether_addr_equal(sta->addr, wh->addr2)) {
+			if (!sta_info->is_mesh_node) {
+				spin_unlock_bh(&priv->sta_lock);
+				return false;
+			}
+		}
+	}
+	spin_unlock_bh(&priv->sta_lock);
+
+	qc = ieee80211_get_qos_ctl(wh);
+	*qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+	wh_len = ieee80211_hdrlen(wh->frame_control);
+	len = wh_len;
+	data = skb->data;
+
+	while (len < skb->len) {
+		frame_len = *(u8 *)(data + len + ETH_HLEN - 1) |
+			(*(u8 *)(data + len + ETH_HLEN - 2) << 8);
+
+		if ((len + ETH_HLEN + frame_len) > skb->len)
+			break;
+
+		newskb = dev_alloc_skb(wh_len + frame_len);
+		if (!newskb)
+			break;
+
+		ether_addr_copy(wh->addr3, data + len);
+		ether_addr_copy(wh->addr4, data + len + ETH_ALEN);
+		memcpy(newskb->data, wh, wh_len);
+		memcpy(newskb->data + wh_len, data + len + ETH_HLEN, frame_len);
+		skb_put(newskb, wh_len + frame_len);
+
+		pad = ((ETH_HLEN + frame_len) % 4) ?
+			(4 - (ETH_HLEN + frame_len) % 4) : 0;
+		len += (ETH_HLEN + frame_len + pad);
+		if (len < skb->len)
+			status->flag |= RX_FLAG_AMSDU_MORE;
+		else
+			status->flag &= ~RX_FLAG_AMSDU_MORE;
+		memcpy(IEEE80211_SKB_RXCB(newskb), status, sizeof(*status));
+		ieee80211_rx(priv->hw, newskb);
+	}
+
+	dev_kfree_skb_any(skb);
+
+	return true;
+}
+
+static int mwl_rx_refill(struct mwl_priv *priv, struct mwl_rx_hndl *rx_hndl)
+{
+	struct mwl_desc_data *desc;
+	dma_addr_t dma;
+
+	desc = &priv->desc_data[0];
+
+	rx_hndl->psk_buff = dev_alloc_skb(desc->rx_buf_size);
+
+	if (!rx_hndl->psk_buff)
+		return -ENOMEM;
+
+	skb_reserve(rx_hndl->psk_buff, SYSADPT_MIN_BYTES_HEADROOM);
+
+	rx_hndl->pdesc->status = EAGLE_RXD_STATUS_OK;
+	rx_hndl->pdesc->qos_ctrl = 0x0000;
+	rx_hndl->pdesc->channel = 0x00;
+	rx_hndl->pdesc->rssi = 0x00;
+	rx_hndl->pdesc->pkt_len = cpu_to_le16(desc->rx_buf_size);
+
+	dma = pci_map_single(priv->pdev,
+			     rx_hndl->psk_buff->data,
+			     desc->rx_buf_size,
+			     PCI_DMA_FROMDEVICE);
+	if (pci_dma_mapping_error(priv->pdev, dma)) {
+		dev_kfree_skb_any(rx_hndl->psk_buff);
+		wiphy_err(priv->hw->wiphy,
+			  "failed to map pci memory!\n");
+		return -ENOMEM;
+	}
+
+	rx_hndl->pdesc->pphys_buff_data = cpu_to_le32(dma);
+
+	return 0;
+}
+
+int mwl_rx_init(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	int rc;
+
+	rc = mwl_rx_ring_alloc(priv);
+	if (rc) {
+		wiphy_err(hw->wiphy, "allocating RX ring failed\n");
+		return rc;
+	}
+
+	rc = mwl_rx_ring_init(priv);
+	if (rc) {
+		mwl_rx_ring_free(priv);
+		wiphy_err(hw->wiphy,
+			  "initializing RX ring failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+void mwl_rx_deinit(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	mwl_rx_ring_cleanup(priv);
+	mwl_rx_ring_free(priv);
+}
+
+void mwl_rx_recv(unsigned long data)
+{
+	struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_desc_data *desc;
+	struct mwl_rx_hndl *curr_hndl;
+	int work_done = 0;
+	struct sk_buff *prx_skb = NULL;
+	int pkt_len;
+	struct ieee80211_rx_status status;
+	struct mwl_vif *mwl_vif = NULL;
+	struct ieee80211_hdr *wh;
+	u32 status_mask;
+
+	spin_lock(&priv->rx_desc_lock);
+	desc = &priv->desc_data[0];
+	curr_hndl = desc->pnext_rx_hndl;
+
+	if (!curr_hndl) {
+		status_mask = readl(priv->iobase1 +
+				    MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+		writel(status_mask | MACREG_A2HRIC_BIT_RX_RDY,
+		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+		priv->is_rx_schedule = false;
+		wiphy_warn(hw->wiphy, "busy or no receiving packets\n");
+		spin_unlock(&priv->rx_desc_lock);
+		return;
+	}
+
+	while ((curr_hndl->pdesc->rx_control == EAGLE_RXD_CTRL_DMA_OWN) &&
+	       (work_done < priv->recv_limit)) {
+		prx_skb = curr_hndl->psk_buff;
+		if (!prx_skb)
+			goto out;
+		pci_unmap_single(priv->pdev,
+				 le32_to_cpu(curr_hndl->pdesc->pphys_buff_data),
+				 desc->rx_buf_size,
+				 PCI_DMA_FROMDEVICE);
+		pkt_len = le16_to_cpu(curr_hndl->pdesc->pkt_len);
+
+		if (skb_tailroom(prx_skb) < pkt_len) {
+			dev_kfree_skb_any(prx_skb);
+			goto out;
+		}
+
+		if (curr_hndl->pdesc->channel !=
+		    hw->conf.chandef.chan->hw_value) {
+			dev_kfree_skb_any(prx_skb);
+			goto out;
+		}
+
+		mwl_rx_prepare_status(curr_hndl->pdesc, &status);
+
+		priv->noise = -curr_hndl->pdesc->noise_floor;
+
+		wh = &((struct mwl_dma_data *)prx_skb->data)->wh;
+
+		if (ieee80211_has_protected(wh->frame_control)) {
+			/* Check if hw crypto has been enabled for
+			 * this bss. If yes, set the status flags
+			 * accordingly
+			 */
+			if (ieee80211_has_tods(wh->frame_control))
+				mwl_vif = mwl_rx_find_vif_bss(priv, wh->addr1);
+			else
+				mwl_vif = mwl_rx_find_vif_bss(priv, wh->addr2);
+
+			if (mwl_vif && mwl_vif->is_hw_crypto_enabled) {
+				/* When MMIC ERROR is encountered
+				 * by the firmware, payload is
+				 * dropped and only 32 bytes of
+				 * mwlwifi 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 mwl_dma_data *tr;
+
+					tr = (struct mwl_dma_data *)
+					     prx_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(prx_skb, pkt_len);
+		mwl_rx_remove_dma_header(prx_skb, curr_hndl->pdesc->qos_ctrl);
+
+		wh = (struct ieee80211_hdr *)prx_skb->data;
+
+		if (ieee80211_is_mgmt(wh->frame_control)) {
+			struct ieee80211_mgmt *mgmt;
+			__le16 capab;
+
+			mgmt = (struct ieee80211_mgmt *)prx_skb->data;
+
+			if (unlikely(ieee80211_is_action(wh->frame_control) &&
+				     mgmt->u.action.category ==
+				     WLAN_CATEGORY_BACK &&
+				     mgmt->u.action.u.addba_resp.action_code ==
+				     WLAN_ACTION_ADDBA_RESP)) {
+				capab = mgmt->u.action.u.addba_resp.capab;
+				if (le16_to_cpu(capab) & 1)
+					mwl_rx_enable_sta_amsdu(priv, mgmt->sa);
+			}
+		}
+
+		if (ieee80211_is_data_qos(wh->frame_control) &&
+		    ieee80211_has_a4(wh->frame_control)) {
+			u8 *qc = ieee80211_get_qos_ctl(wh);
+
+			if (*qc & IEEE80211_QOS_CTL_A_MSDU_PRESENT)
+				if (mwl_rx_process_mesh_amsdu(priv, prx_skb,
+							      &status))
+					goto out;
+		}
+
+		memcpy(IEEE80211_SKB_RXCB(prx_skb), &status, sizeof(status));
+		ieee80211_rx(hw, prx_skb);
+out:
+		mwl_rx_refill(priv, curr_hndl);
+		curr_hndl->pdesc->rx_control = EAGLE_RXD_CTRL_DRIVER_OWN;
+		curr_hndl->pdesc->qos_ctrl = 0;
+		curr_hndl = curr_hndl->pnext;
+		work_done++;
+	}
+
+	desc->pnext_rx_hndl = curr_hndl;
+
+	status_mask = readl(priv->iobase1 +
+			    MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+	writel(status_mask | MACREG_A2HRIC_BIT_RX_RDY,
+	       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+	priv->is_rx_schedule = false;
+
+	spin_unlock(&priv->rx_desc_lock);
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/rx.h b/drivers/net/wireless/marvell/mwlwifi/rx.h
new file mode 100644
index 0000000..d32c558
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/rx.h
@@ -0,0 +1,25 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines receive related functions. */
+
+#ifndef _rx_h_
+#define _rx_h_
+
+int mwl_rx_init(struct ieee80211_hw *hw);
+void mwl_rx_deinit(struct ieee80211_hw *hw);
+void mwl_rx_recv(unsigned long data);
+
+#endif /* _rx_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/sysadpt.h b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h
new file mode 100644
index 0000000..8a1706a
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/sysadpt.h
@@ -0,0 +1,73 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines system adaptation related information. */
+
+#ifndef _mwl_sysadpt_h_
+#define _mwl_sysadpt_h_
+
+#define SYSADPT_MAX_NUM_CHANNELS       64
+
+#define SYSADPT_MAX_DATA_RATES_G       14
+
+#define SYSADPT_TX_POWER_LEVEL_TOTAL   16
+
+#define SYSADPT_TX_WMM_QUEUES          4
+
+#define SYSADPT_TX_AMPDU_QUEUES        4
+
+#define SYSADPT_NUM_OF_AP              16
+
+#define SYSADPT_TOTAL_TX_QUEUES        (SYSADPT_TX_WMM_QUEUES + \
+					SYSADPT_NUM_OF_AP)
+
+#define SYSADPT_TOTAL_HW_QUEUES        (SYSADPT_TX_WMM_QUEUES + \
+					SYSADPT_TX_AMPDU_QUEUES)
+
+#define SYSADPT_NUM_OF_DESC_DATA       (4 + SYSADPT_NUM_OF_AP)
+
+#define SYSADPT_MAX_NUM_TX_DESC        256
+
+#define SYSADPT_TX_QUEUE_LIMIT         (3 * SYSADPT_MAX_NUM_TX_DESC)
+
+#define SYSADPT_TX_WAKE_Q_THRESHOLD    (2 * SYSADPT_MAX_NUM_TX_DESC)
+
+#define SYSADPT_DELAY_FREE_Q_LIMIT     SYSADPT_MAX_NUM_TX_DESC
+
+#define SYSADPT_MAX_NUM_RX_DESC        256
+
+#define SYSADPT_RECEIVE_LIMIT          64
+
+#define SYSADPT_MAX_AGGR_SIZE          8192
+
+#define SYSADPT_MIN_BYTES_HEADROOM     64
+
+#define SYSADPT_AMPDU_PACKET_THRESHOLD 64
+
+#define SYSADPT_AMSDU_FW_MAX_SIZE      3300
+
+#define SYSADPT_AMSDU_4K_MAX_SIZE      SYSADPT_AMSDU_FW_MAX_SIZE
+
+#define SYSADPT_AMSDU_8K_MAX_SIZE      SYSADPT_AMSDU_FW_MAX_SIZE
+
+#define SYSADPT_AMSDU_ALLOW_SIZE       1600
+
+#define SYSADPT_AMSDU_FLUSH_TIME       500
+
+#define SYSADPT_AMSDU_PACKET_THRESHOLD 10
+
+#define SYSADPT_MAX_TID                8
+
+#endif /* _mwl_sysadpt_h_ */
diff --git a/drivers/net/wireless/marvell/mwlwifi/tx.c b/drivers/net/wireless/marvell/mwlwifi/tx.c
new file mode 100644
index 0000000..68a994d
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/tx.c
@@ -0,0 +1,1251 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file implements transmit related functions. */
+
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "tx.h"
+
+#define MAX_NUM_TX_RING_BYTES  (SYSADPT_MAX_NUM_TX_DESC * \
+				sizeof(struct mwl_tx_desc))
+
+#define MAX_NUM_TX_HNDL_BYTES   (SYSADPT_MAX_NUM_TX_DESC * \
+				sizeof(struct mwl_tx_hndl))
+
+#define EAGLE_TXD_XMITCTRL_USE_MC_RATE     0x8     /* Use multicast data rate */
+
+#define MWL_QOS_ACK_POLICY_MASK	           0x0060
+#define MWL_QOS_ACK_POLICY_NORMAL          0x0000
+#define MWL_QOS_ACK_POLICY_BLOCKACK        0x0060
+
+#define EXT_IV                             0x20
+#define INCREASE_IV(iv16, iv32) \
+{ \
+	(iv16)++; \
+	if ((iv16) == 0) \
+		(iv32)++; \
+}
+
+/* Transmit rate information constants */
+#define TX_RATE_FORMAT_LEGACY         0
+#define TX_RATE_FORMAT_11N            1
+#define TX_RATE_FORMAT_11AC           2
+
+#define TX_RATE_BANDWIDTH_20          0
+#define TX_RATE_BANDWIDTH_40          1
+#define TX_RATE_BANDWIDTH_80          2
+
+#define TX_RATE_INFO_STD_GI           0
+#define TX_RATE_INFO_SHORT_GI         1
+
+enum {
+	IEEE_TYPE_MANAGEMENT = 0,
+	IEEE_TYPE_CONTROL,
+	IEEE_TYPE_DATA
+};
+
+struct ccmp_hdr {
+	__le16 iv16;
+	u8 rsvd;
+	u8 key_id;
+	__le32 iv32;
+} __packed;
+
+static int mwl_tx_ring_alloc(struct mwl_priv *priv)
+{
+	struct mwl_desc_data *desc;
+	int num;
+	u8 *mem;
+
+	desc = &priv->desc_data[0];
+
+	mem = dma_alloc_coherent(&priv->pdev->dev,
+				 MAX_NUM_TX_RING_BYTES *
+				 SYSADPT_NUM_OF_DESC_DATA,
+				 &desc->pphys_tx_ring,
+				 GFP_KERNEL);
+
+	if (!mem) {
+		wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
+		return -ENOMEM;
+	}
+
+	for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+		desc = &priv->desc_data[num];
+
+		desc->ptx_ring = (struct mwl_tx_desc *)
+			(mem + num * MAX_NUM_TX_RING_BYTES);
+
+		desc->pphys_tx_ring = (dma_addr_t)
+			((u32)priv->desc_data[0].pphys_tx_ring +
+			num * MAX_NUM_TX_RING_BYTES);
+
+		memset(desc->ptx_ring, 0x00,
+		       MAX_NUM_TX_RING_BYTES);
+	}
+
+	mem = kmalloc(MAX_NUM_TX_HNDL_BYTES * SYSADPT_NUM_OF_DESC_DATA,
+		      GFP_KERNEL);
+
+	if (!mem) {
+		wiphy_err(priv->hw->wiphy, "cannot alloc mem\n");
+		dma_free_coherent(&priv->pdev->dev,
+				  MAX_NUM_TX_RING_BYTES *
+				  SYSADPT_NUM_OF_DESC_DATA,
+				  priv->desc_data[0].ptx_ring,
+				  priv->desc_data[0].pphys_tx_ring);
+		return -ENOMEM;
+	}
+
+	for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+		desc = &priv->desc_data[num];
+
+		desc->tx_hndl = (struct mwl_tx_hndl *)
+			(mem + num * MAX_NUM_TX_HNDL_BYTES);
+
+		memset(desc->tx_hndl, 0x00,
+		       MAX_NUM_TX_HNDL_BYTES);
+	}
+
+	return 0;
+}
+
+static int mwl_tx_ring_init(struct mwl_priv *priv)
+{
+	int num, i;
+	struct mwl_desc_data *desc;
+
+	for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+		skb_queue_head_init(&priv->txq[num]);
+		priv->fw_desc_cnt[num] = 0;
+
+		desc = &priv->desc_data[num];
+
+		if (desc->ptx_ring) {
+			for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
+				desc->ptx_ring[i].status =
+					cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
+				desc->ptx_ring[i].pphys_next =
+					cpu_to_le32((u32)desc->pphys_tx_ring +
+					((i + 1) * sizeof(struct mwl_tx_desc)));
+				desc->tx_hndl[i].pdesc =
+					&desc->ptx_ring[i];
+				if (i < SYSADPT_MAX_NUM_TX_DESC - 1)
+					desc->tx_hndl[i].pnext =
+						&desc->tx_hndl[i + 1];
+			}
+			desc->ptx_ring[SYSADPT_MAX_NUM_TX_DESC - 1].pphys_next =
+				cpu_to_le32((u32)desc->pphys_tx_ring);
+			desc->tx_hndl[SYSADPT_MAX_NUM_TX_DESC - 1].pnext =
+				&desc->tx_hndl[0];
+
+			desc->pstale_tx_hndl = &desc->tx_hndl[0];
+			desc->pnext_tx_hndl  = &desc->tx_hndl[0];
+		} else {
+			wiphy_err(priv->hw->wiphy, "no valid TX mem\n");
+			return -ENOMEM;
+		}
+	}
+
+	return 0;
+}
+
+static void mwl_tx_ring_cleanup(struct mwl_priv *priv)
+{
+	int cleaned_tx_desc = 0;
+	int num, i;
+	struct mwl_desc_data *desc;
+
+	for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+		skb_queue_purge(&priv->txq[num]);
+		priv->fw_desc_cnt[num] = 0;
+
+		desc = &priv->desc_data[num];
+
+		if (desc->ptx_ring) {
+			for (i = 0; i < SYSADPT_MAX_NUM_TX_DESC; i++) {
+				if (!desc->tx_hndl[i].psk_buff)
+					continue;
+
+				wiphy_info(priv->hw->wiphy,
+					   "unmapped and free'd %i 0x%p 0x%x\n",
+					   i,
+					   desc->tx_hndl[i].psk_buff->data,
+					   le32_to_cpu(
+					   desc->ptx_ring[i].pkt_ptr));
+				pci_unmap_single(priv->pdev,
+						 le32_to_cpu(
+						 desc->ptx_ring[i].pkt_ptr),
+						 desc->tx_hndl[i].psk_buff->len,
+						 PCI_DMA_TODEVICE);
+				dev_kfree_skb_any(desc->tx_hndl[i].psk_buff);
+				desc->ptx_ring[i].status =
+					cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
+				desc->ptx_ring[i].pkt_ptr = 0;
+				desc->ptx_ring[i].pkt_len = 0;
+				desc->tx_hndl[i].psk_buff = NULL;
+				cleaned_tx_desc++;
+			}
+		}
+	}
+
+	wiphy_info(priv->hw->wiphy, "cleaned %i TX descr\n", cleaned_tx_desc);
+}
+
+static void mwl_tx_ring_free(struct mwl_priv *priv)
+{
+	int num;
+
+	if (priv->desc_data[0].ptx_ring) {
+		dma_free_coherent(&priv->pdev->dev,
+				  MAX_NUM_TX_RING_BYTES *
+				  SYSADPT_NUM_OF_DESC_DATA,
+				  priv->desc_data[0].ptx_ring,
+				  priv->desc_data[0].pphys_tx_ring);
+	}
+
+	for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+		if (priv->desc_data[num].ptx_ring)
+			priv->desc_data[num].ptx_ring = NULL;
+		priv->desc_data[num].pstale_tx_hndl = NULL;
+		priv->desc_data[num].pnext_tx_hndl = NULL;
+	}
+
+	kfree(priv->desc_data[0].tx_hndl);
+}
+
+static inline void mwl_tx_add_dma_header(struct mwl_priv *priv,
+					 struct sk_buff *skb,
+					 int head_pad,
+					 int tail_pad)
+{
+	struct ieee80211_hdr *wh;
+	int hdrlen;
+	int reqd_hdrlen;
+	struct mwl_dma_data *tr;
+
+	/* Add a firmware DMA header; the firmware requires that we
+	 * present a 2-byte payload length followed by a 4-address
+	 * header (without QoS field), followed (optionally) by any
+	 * WEP/ExtIV header (but only filled in for CCMP).
+	 */
+	wh = (struct ieee80211_hdr *)skb->data;
+
+	hdrlen = ieee80211_hdrlen(wh->frame_control);
+
+	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 -= IEEE80211_QOS_CTL_LEN;
+
+	tr = (struct mwl_dma_data *)skb->data;
+
+	if (wh != &tr->wh)
+		memmove(&tr->wh, wh, hdrlen);
+
+	if (hdrlen != sizeof(tr->wh))
+		memset(((void *)&tr->wh) + hdrlen, 0, sizeof(tr->wh) - hdrlen);
+
+	/* Firmware length is the length of the fully formed "802.11
+	 * 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) + tail_pad);
+}
+
+static inline void mwl_tx_encapsulate_frame(struct mwl_priv *priv,
+					    struct sk_buff *skb,
+					    struct ieee80211_key_conf *k_conf,
+					    bool *ccmp)
+{
+	int head_pad = 0;
+	int data_pad = 0;
+
+	/* 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)
+	 */
+
+	if (k_conf) {
+		head_pad = k_conf->iv_len;
+
+		switch (k_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;
+			*ccmp = true;
+			break;
+		}
+	}
+
+	mwl_tx_add_dma_header(priv, skb, head_pad, data_pad);
+}
+
+static inline void mwl_tx_insert_ccmp_hdr(u8 *pccmp_hdr,
+					  u8 key_id, u16 iv16, u32 iv32)
+{
+	struct ccmp_hdr *ccmp_h = (struct ccmp_hdr *)pccmp_hdr;
+
+	ccmp_h->iv16 = cpu_to_le16(iv16);
+	ccmp_h->rsvd = 0;
+	ccmp_h->key_id = EXT_IV | (key_id << 6);
+	ccmp_h->iv32 = cpu_to_le32(iv32);
+}
+
+static inline int mwl_tx_tid_queue_mapping(u8 tid)
+{
+	switch (tid) {
+	case 0:
+	case 3:
+		return IEEE80211_AC_BE;
+	case 1:
+	case 2:
+		return IEEE80211_AC_BK;
+	case 4:
+	case 5:
+		return IEEE80211_AC_VI;
+	case 6:
+	case 7:
+		return IEEE80211_AC_VO;
+	default:
+		break;
+	}
+
+	return -1;
+}
+
+static inline void mwl_tx_count_packet(struct ieee80211_sta *sta, u8 tid)
+{
+	struct mwl_sta *sta_info;
+	struct mwl_tx_info *tx_stats;
+
+	if (WARN_ON(tid >= SYSADPT_MAX_TID))
+		return;
+
+	sta_info = mwl_dev_get_sta(sta);
+
+	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++;
+	}
+}
+
+static inline bool mwl_tx_available(struct mwl_priv *priv, int desc_num)
+{
+	struct mwl_tx_hndl *tx_hndl;
+
+	tx_hndl = priv->desc_data[desc_num].pnext_tx_hndl;
+
+	if (!tx_hndl->pdesc)
+		return false;
+
+	if (tx_hndl->pdesc->status != EAGLE_TXD_STATUS_IDLE) {
+		/* Interrupt F/W anyway */
+		if (tx_hndl->pdesc->status &
+		    cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED))
+			writel(MACREG_H2ARIC_BIT_PPA_READY,
+			       priv->iobase1 +
+			       MACREG_REG_H2A_INTERRUPT_EVENTS);
+		return false;
+	}
+
+	return true;
+}
+
+static inline void mwl_tx_skb(struct mwl_priv *priv, int desc_num,
+			      struct sk_buff *tx_skb)
+{
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_tx_ctrl *tx_ctrl;
+	struct mwl_tx_hndl *tx_hndl;
+	struct mwl_tx_desc *tx_desc;
+	struct ieee80211_sta *sta;
+	struct ieee80211_vif *vif;
+	struct mwl_vif *mwl_vif;
+	struct ieee80211_key_conf *k_conf;
+	bool ccmp = false;
+	struct mwl_dma_data *dma_data;
+	struct ieee80211_hdr *wh;
+	dma_addr_t dma;
+
+	if (WARN_ON(!tx_skb))
+		return;
+
+	tx_info = IEEE80211_SKB_CB(tx_skb);
+	tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+	sta = (struct ieee80211_sta *)tx_ctrl->sta;
+	vif = (struct ieee80211_vif *)tx_ctrl->vif;
+	mwl_vif = mwl_dev_get_vif(vif);
+	k_conf = (struct ieee80211_key_conf *)tx_ctrl->k_conf;
+
+	mwl_tx_encapsulate_frame(priv, tx_skb, k_conf, &ccmp);
+
+	dma_data = (struct mwl_dma_data *)tx_skb->data;
+	wh = &dma_data->wh;
+
+	if (ieee80211_is_data(wh->frame_control)) {
+		if (is_multicast_ether_addr(wh->addr1)) {
+			if (ccmp) {
+				mwl_tx_insert_ccmp_hdr(dma_data->data,
+						       mwl_vif->keyidx,
+						       mwl_vif->iv16,
+						       mwl_vif->iv32);
+				INCREASE_IV(mwl_vif->iv16, mwl_vif->iv32);
+			}
+		} else {
+			if (ccmp) {
+				if (vif->type == NL80211_IFTYPE_STATION) {
+					mwl_tx_insert_ccmp_hdr(dma_data->data,
+							       mwl_vif->keyidx,
+							       mwl_vif->iv16,
+							       mwl_vif->iv32);
+					INCREASE_IV(mwl_vif->iv16,
+						    mwl_vif->iv32);
+				} else {
+					struct mwl_sta *sta_info;
+
+					sta_info = mwl_dev_get_sta(sta);
+
+					mwl_tx_insert_ccmp_hdr(dma_data->data,
+							       0,
+							       sta_info->iv16,
+							       sta_info->iv32);
+					INCREASE_IV(sta_info->iv16,
+						    sta_info->iv32);
+				}
+			}
+		}
+	}
+
+	tx_hndl = priv->desc_data[desc_num].pnext_tx_hndl;
+	tx_hndl->psk_buff = tx_skb;
+	tx_desc = tx_hndl->pdesc;
+	tx_desc->tx_priority = tx_ctrl->tx_priority;
+	tx_desc->qos_ctrl = cpu_to_le16(tx_ctrl->qos_ctrl);
+	tx_desc->pkt_len = cpu_to_le16(tx_skb->len);
+	tx_desc->packet_info = 0;
+	tx_desc->data_rate = 0;
+	tx_desc->type = tx_ctrl->type;
+	tx_desc->xmit_control = tx_ctrl->xmit_control;
+	tx_desc->sap_pkt_info = 0;
+	dma = pci_map_single(priv->pdev, tx_skb->data,
+			     tx_skb->len, PCI_DMA_TODEVICE);
+	if (pci_dma_mapping_error(priv->pdev, dma)) {
+		dev_kfree_skb_any(tx_skb);
+		wiphy_err(priv->hw->wiphy,
+			  "failed to map pci memory!\n");
+		return;
+	}
+	tx_desc->pkt_ptr = cpu_to_le32(dma);
+	tx_desc->status = cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED);
+	/* make sure all the memory transactions done by cpu were completed */
+	wmb();	/*Data Memory Barrier*/
+	writel(MACREG_H2ARIC_BIT_PPA_READY,
+	       priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS);
+	priv->desc_data[desc_num].pnext_tx_hndl = tx_hndl->pnext;
+	priv->fw_desc_cnt[desc_num]++;
+}
+
+static inline struct sk_buff *mwl_tx_do_amsdu(struct mwl_priv *priv,
+					      int desc_num,
+					      struct sk_buff *tx_skb,
+					      struct ieee80211_tx_info *tx_info)
+{
+	struct ieee80211_sta *sta;
+	struct mwl_sta *sta_info;
+	struct mwl_tx_ctrl *tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+	struct ieee80211_tx_info *amsdu_info;
+	struct sk_buff_head *amsdu_pkts;
+	struct mwl_amsdu_frag *amsdu;
+	int amsdu_allow_size;
+	struct ieee80211_hdr *wh;
+	int wh_len;
+	u16 len;
+	u8 *data;
+
+	sta = (struct ieee80211_sta *)tx_ctrl->sta;
+	sta_info = mwl_dev_get_sta(sta);
+
+	if (!sta_info->is_amsdu_allowed)
+		return tx_skb;
+
+	wh = (struct ieee80211_hdr *)tx_skb->data;
+	if (sta_info->is_mesh_node && is_multicast_ether_addr(wh->addr3))
+		return tx_skb;
+
+	if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_4K)
+		amsdu_allow_size = SYSADPT_AMSDU_4K_MAX_SIZE;
+	else if (sta_info->amsdu_ctrl.cap == MWL_AMSDU_SIZE_8K)
+		amsdu_allow_size = SYSADPT_AMSDU_8K_MAX_SIZE;
+	else
+		return tx_skb;
+
+	spin_lock_bh(&sta_info->amsdu_lock);
+	amsdu = &sta_info->amsdu_ctrl.frag[desc_num];
+
+	if (tx_skb->len > SYSADPT_AMSDU_ALLOW_SIZE) {
+		if (amsdu->num) {
+			mwl_tx_skb(priv, desc_num, amsdu->skb);
+			amsdu->num = 0;
+			amsdu->cur_pos = NULL;
+
+			if (!mwl_tx_available(priv, desc_num)) {
+				skb_queue_head(&priv->txq[desc_num], tx_skb);
+				spin_unlock_bh(&sta_info->amsdu_lock);
+				return NULL;
+			}
+		}
+		spin_unlock_bh(&sta_info->amsdu_lock);
+		return tx_skb;
+	}
+
+	/* potential amsdu size, should add amsdu header 14 bytes +
+	 * maximum padding 3.
+	 */
+	wh_len = ieee80211_hdrlen(wh->frame_control);
+	len = tx_skb->len - wh_len + 17;
+
+	if (amsdu->num) {
+		if ((amsdu->skb->len + len) > amsdu_allow_size) {
+			mwl_tx_skb(priv, desc_num, amsdu->skb);
+			amsdu->num = 0;
+			amsdu->cur_pos = NULL;
+		}
+	}
+
+	amsdu->jiffies = jiffies;
+	len = tx_skb->len - wh_len;
+
+	if (amsdu->num == 0) {
+		struct sk_buff *newskb;
+
+		amsdu_pkts = (struct sk_buff_head *)
+			kmalloc(sizeof(*amsdu_pkts), GFP_KERNEL);
+		if (!amsdu_pkts) {
+			spin_unlock_bh(&sta_info->amsdu_lock);
+			return tx_skb;
+		}
+		newskb = dev_alloc_skb(amsdu_allow_size +
+				       SYSADPT_MIN_BYTES_HEADROOM);
+		if (!newskb) {
+			spin_unlock_bh(&sta_info->amsdu_lock);
+			kfree(amsdu_pkts);
+			return tx_skb;
+		}
+
+		data = newskb->data;
+		memcpy(data, tx_skb->data, wh_len);
+		if (sta_info->is_mesh_node) {
+			ether_addr_copy(data + wh_len, wh->addr3);
+			ether_addr_copy(data + wh_len + ETH_ALEN, wh->addr4);
+		} else {
+			ether_addr_copy(data + wh_len,
+					ieee80211_get_DA(wh));
+			ether_addr_copy(data + wh_len + ETH_ALEN,
+					ieee80211_get_SA(wh));
+		}
+		*(u8 *)(data + wh_len + ETH_HLEN - 1) = len & 0xff;
+		*(u8 *)(data + wh_len + ETH_HLEN - 2) = (len >> 8) & 0xff;
+		memcpy(data + wh_len + ETH_HLEN, tx_skb->data + wh_len, len);
+
+		skb_put(newskb, tx_skb->len + ETH_HLEN);
+		tx_ctrl->qos_ctrl |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+		amsdu_info = IEEE80211_SKB_CB(newskb);
+		memcpy(amsdu_info, tx_info, sizeof(*tx_info));
+		skb_queue_head_init(amsdu_pkts);
+		((struct mwl_tx_ctrl *)&amsdu_info->status)->amsdu_pkts =
+			(void *)amsdu_pkts;
+		amsdu->skb = newskb;
+	} else {
+		amsdu->cur_pos += amsdu->pad;
+		data = amsdu->cur_pos;
+
+		if (sta_info->is_mesh_node) {
+			ether_addr_copy(data, wh->addr3);
+			ether_addr_copy(data + ETH_ALEN, wh->addr4);
+		} else {
+			ether_addr_copy(data, ieee80211_get_DA(wh));
+			ether_addr_copy(data + ETH_ALEN, ieee80211_get_SA(wh));
+		}
+		*(u8 *)(data + ETH_HLEN - 1) = len & 0xff;
+		*(u8 *)(data + ETH_HLEN - 2) = (len >> 8) & 0xff;
+		memcpy(data + ETH_HLEN, tx_skb->data + wh_len, len);
+
+		skb_put(amsdu->skb, len + ETH_HLEN + amsdu->pad);
+		amsdu_info = IEEE80211_SKB_CB(amsdu->skb);
+		amsdu_pkts = (struct sk_buff_head *)
+			((struct mwl_tx_ctrl *)&amsdu_info->status)->amsdu_pkts;
+	}
+
+	amsdu->num++;
+	amsdu->pad = ((len + ETH_HLEN) % 4) ? (4 - (len + ETH_HLEN) % 4) : 0;
+	amsdu->cur_pos = amsdu->skb->data + amsdu->skb->len;
+	skb_queue_tail(amsdu_pkts, tx_skb);
+
+	if (amsdu->num > SYSADPT_AMSDU_PACKET_THRESHOLD) {
+		amsdu->num = 0;
+		amsdu->cur_pos = NULL;
+		spin_unlock_bh(&sta_info->amsdu_lock);
+		return amsdu->skb;
+	}
+
+	spin_unlock_bh(&sta_info->amsdu_lock);
+	return NULL;
+}
+
+static inline void mwl_tx_skbs(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	int num = SYSADPT_TX_WMM_QUEUES;
+	struct sk_buff *tx_skb;
+
+	spin_lock_bh(&priv->tx_desc_lock);
+	while (num--) {
+		while (skb_queue_len(&priv->txq[num]) > 0) {
+			struct ieee80211_tx_info *tx_info;
+			struct mwl_tx_ctrl *tx_ctrl;
+
+			if (!mwl_tx_available(priv, num))
+				break;
+
+			tx_skb = skb_dequeue(&priv->txq[num]);
+			tx_info = IEEE80211_SKB_CB(tx_skb);
+			tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+
+			if ((tx_skb->protocol != cpu_to_be16(ETH_P_PAE)) &&
+			    (tx_ctrl->tx_priority >= SYSADPT_TX_WMM_QUEUES)) {
+				tx_skb = mwl_tx_do_amsdu(priv, num,
+							 tx_skb, tx_info);
+			}
+
+			if (tx_skb)
+				mwl_tx_skb(priv, num, tx_skb);
+		}
+
+		if (skb_queue_len(&priv->txq[num]) <
+		    SYSADPT_TX_WAKE_Q_THRESHOLD) {
+			int queue;
+
+			queue = SYSADPT_TX_WMM_QUEUES - num - 1;
+			if (ieee80211_queue_stopped(hw, queue))
+				ieee80211_wake_queue(hw, queue);
+		}
+	}
+	spin_unlock_bh(&priv->tx_desc_lock);
+}
+
+static inline void mwl_tx_prepare_info(struct ieee80211_hw *hw, u32 rate,
+				       struct ieee80211_tx_info *info)
+{
+	u32 format, bandwidth, short_gi, rate_id;
+
+	ieee80211_tx_info_clear_status(info);
+
+	info->status.rates[0].idx = -1;
+	info->status.rates[0].count = 0;
+	info->status.rates[0].flags = 0;
+
+	if (rate) {
+		/* Prepare rate information */
+		format = rate & MWL_TX_RATE_FORMAT_MASK;
+		bandwidth =
+			(rate & MWL_TX_RATE_BANDWIDTH_MASK) >>
+			MWL_TX_RATE_BANDWIDTH_SHIFT;
+		short_gi = (rate & MWL_TX_RATE_SHORTGI_MASK) >>
+			MWL_TX_RATE_SHORTGI_SHIFT;
+		rate_id = (rate & MWL_TX_RATE_RATEIDMCS_MASK) >>
+			MWL_TX_RATE_RATEIDMCS_SHIFT;
+
+		info->status.rates[0].idx = rate_id;
+		if (format == TX_RATE_FORMAT_LEGACY) {
+			if (hw->conf.chandef.chan->hw_value >
+			    BAND_24_CHANNEL_NUM) {
+				info->status.rates[0].idx -= 5;
+			}
+		}
+		if (format == TX_RATE_FORMAT_11N)
+			info->status.rates[0].flags |=
+				IEEE80211_TX_RC_MCS;
+		if (format == TX_RATE_FORMAT_11AC)
+			info->status.rates[0].flags |=
+				IEEE80211_TX_RC_VHT_MCS;
+		if (bandwidth == TX_RATE_BANDWIDTH_40)
+			info->status.rates[0].flags |=
+				IEEE80211_TX_RC_40_MHZ_WIDTH;
+		if (bandwidth == TX_RATE_BANDWIDTH_80)
+			info->status.rates[0].flags |=
+				IEEE80211_TX_RC_80_MHZ_WIDTH;
+		if (short_gi == TX_RATE_INFO_SHORT_GI)
+			info->status.rates[0].flags |=
+				IEEE80211_TX_RC_SHORT_GI;
+		info->status.rates[0].count = 1;
+		info->status.rates[1].idx = -1;
+	}
+}
+
+static inline void mwl_tx_ack_amsdu_pkts(struct ieee80211_hw *hw, u32 rate,
+					 struct sk_buff_head *amsdu_pkts)
+{
+	struct sk_buff *amsdu_pkt;
+	struct ieee80211_tx_info *info;
+
+	while (skb_queue_len(amsdu_pkts) > 0) {
+		amsdu_pkt = skb_dequeue(amsdu_pkts);
+		info = IEEE80211_SKB_CB(amsdu_pkt);
+		mwl_tx_prepare_info(hw, rate, info);
+		info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+		info->flags |= IEEE80211_TX_STAT_ACK;
+		ieee80211_tx_status(hw, amsdu_pkt);
+	}
+
+	kfree(amsdu_pkts);
+}
+
+int mwl_tx_init(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+	int rc;
+
+	skb_queue_head_init(&priv->delay_q);
+
+	rc = mwl_tx_ring_alloc(priv);
+	if (rc) {
+		wiphy_err(hw->wiphy, "allocating TX ring failed\n");
+		return rc;
+	}
+
+	rc = mwl_tx_ring_init(priv);
+	if (rc) {
+		mwl_tx_ring_free(priv);
+		wiphy_err(hw->wiphy, "initializing TX ring failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+void mwl_tx_deinit(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv = hw->priv;
+
+	skb_queue_purge(&priv->delay_q);
+
+	mwl_tx_ring_cleanup(priv);
+	mwl_tx_ring_free(priv);
+}
+
+void mwl_tx_xmit(struct ieee80211_hw *hw,
+		 struct ieee80211_tx_control *control,
+		 struct sk_buff *skb)
+{
+	struct mwl_priv *priv = hw->priv;
+	int index;
+	struct ieee80211_sta *sta;
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_vif *mwl_vif;
+	struct ieee80211_hdr *wh;
+	u8 xmitcontrol;
+	u16 qos;
+	int txpriority;
+	u8 tid = 0;
+	struct mwl_ampdu_stream *stream = NULL;
+	bool start_ba_session = false;
+	bool mgmtframe = false;
+	struct ieee80211_mgmt *mgmt;
+	bool eapol_frame = false;
+	struct mwl_tx_ctrl *tx_ctrl;
+	struct ieee80211_key_conf *k_conf = NULL;
+
+	index = skb_get_queue_mapping(skb);
+	sta = control->sta;
+
+	wh = (struct ieee80211_hdr *)skb->data;
+
+	if (ieee80211_is_data_qos(wh->frame_control))
+		qos = *((u16 *)ieee80211_get_qos_ctl(wh));
+	else
+		qos = 0;
+
+	if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+		index = IEEE80211_AC_VO;
+		eapol_frame = true;
+	}
+
+	if (ieee80211_is_mgmt(wh->frame_control)) {
+		mgmtframe = true;
+		mgmt = (struct ieee80211_mgmt *)skb->data;
+	}
+
+	tx_info = IEEE80211_SKB_CB(skb);
+	mwl_vif = mwl_dev_get_vif(tx_info->control.vif);
+
+	if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+		wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+		wh->seq_ctrl |= cpu_to_le16(mwl_vif->seqno);
+		mwl_vif->seqno += 0x10;
+	}
+
+	/* Setup firmware control bit fields for each frame type. */
+	xmitcontrol = 0;
+
+	if (mgmtframe || ieee80211_is_ctl(wh->frame_control)) {
+		qos = 0;
+	} else if (ieee80211_is_data(wh->frame_control)) {
+		qos &= ~MWL_QOS_ACK_POLICY_MASK;
+
+		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+			xmitcontrol &= 0xfb;
+			qos |= MWL_QOS_ACK_POLICY_BLOCKACK;
+		} else {
+			xmitcontrol |= 0x4;
+			qos |= MWL_QOS_ACK_POLICY_NORMAL;
+		}
+
+		if (is_multicast_ether_addr(wh->addr1))
+			xmitcontrol |= EAGLE_TXD_XMITCTRL_USE_MC_RATE;
+
+		k_conf = tx_info->control.hw_key;
+	}
+
+	/* 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 (mgmtframe) {
+		u16 capab;
+
+		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)) {
+			capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab);
+			tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+			index = mwl_tx_tid_queue_mapping(tid);
+			capab |= 1;
+			mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab);
+		}
+
+		if (unlikely(ieee80211_is_action(wh->frame_control) &&
+			     mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+			     mgmt->u.action.u.addba_resp.action_code ==
+			     WLAN_ACTION_ADDBA_RESP)) {
+			capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
+			capab |= 1;
+			mgmt->u.action.u.addba_resp.capab = cpu_to_le16(capab);
+		}
+	}
+
+	index = SYSADPT_TX_WMM_QUEUES - index - 1;
+	txpriority = index;
+
+	if (sta && sta->ht_cap.ht_supported && !eapol_frame &&
+	    ieee80211_is_data_qos(wh->frame_control)) {
+		tid = qos & 0xf;
+		mwl_tx_count_packet(sta, tid);
+
+		spin_lock_bh(&priv->stream_lock);
+		stream = mwl_fwcmd_lookup_stream(hw, sta->addr, tid);
+
+		if (stream) {
+			if (stream->state == AMPDU_STREAM_ACTIVE) {
+				if (WARN_ON(!(qos &
+					    MWL_QOS_ACK_POLICY_BLOCKACK))) {
+					spin_unlock_bh(&priv->stream_lock);
+					dev_kfree_skb_any(skb);
+					return;
+				}
+
+				txpriority =
+					(SYSADPT_TX_WMM_QUEUES + stream->idx) %
+					SYSADPT_TOTAL_HW_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,
+					   "can't send packet during ADDBA\n");
+				spin_unlock_bh(&priv->stream_lock);
+				dev_kfree_skb_any(skb);
+				return;
+			}
+		} else {
+			if (mwl_fwcmd_ampdu_allowed(sta, tid)) {
+				stream = mwl_fwcmd_add_stream(hw, sta, tid);
+
+				if (stream)
+					start_ba_session = true;
+			}
+		}
+
+		spin_unlock_bh(&priv->stream_lock);
+	} else {
+		qos &= ~MWL_QOS_ACK_POLICY_MASK;
+		qos |= MWL_QOS_ACK_POLICY_NORMAL;
+	}
+
+	tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+	tx_ctrl->vif = (void *)tx_info->control.vif;
+	tx_ctrl->sta = (void *)sta;
+	tx_ctrl->k_conf = (void *)k_conf;
+	tx_ctrl->amsdu_pkts = NULL;
+	tx_ctrl->tx_priority = txpriority;
+	tx_ctrl->type = (mgmtframe ? IEEE_TYPE_MANAGEMENT : IEEE_TYPE_DATA);
+	tx_ctrl->qos_ctrl = qos;
+	tx_ctrl->xmit_control = xmitcontrol;
+
+	if (skb_queue_len(&priv->txq[index]) > priv->txq_limit)
+		ieee80211_stop_queue(hw, SYSADPT_TX_WMM_QUEUES - index - 1);
+
+	skb_queue_tail(&priv->txq[index], skb);
+
+	mwl_tx_skbs(hw);
+
+	/* Initiate the ampdu session here */
+	if (start_ba_session) {
+		spin_lock_bh(&priv->stream_lock);
+		if (mwl_fwcmd_start_stream(hw, stream))
+			mwl_fwcmd_remove_stream(hw, stream);
+		spin_unlock_bh(&priv->stream_lock);
+	}
+}
+
+void mwl_tx_del_pkts_via_vif(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv = hw->priv;
+	int num;
+	struct sk_buff *skb, *tmp;
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_tx_ctrl *tx_ctrl;
+	struct sk_buff_head *amsdu_pkts;
+
+	for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+		spin_lock_bh(&priv->txq[num].lock);
+		skb_queue_walk_safe(&priv->txq[num], skb, tmp) {
+			tx_info = IEEE80211_SKB_CB(skb);
+			tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+			if (tx_ctrl->vif == vif) {
+				amsdu_pkts = (struct sk_buff_head *)
+					tx_ctrl->amsdu_pkts;
+				if (amsdu_pkts) {
+					skb_queue_purge(amsdu_pkts);
+					kfree(amsdu_pkts);
+				}
+				__skb_unlink(skb, &priv->txq[num]);
+				dev_kfree_skb_any(skb);
+			}
+		}
+		spin_unlock_bh(&priv->txq[num].lock);
+	}
+}
+
+void mwl_tx_del_pkts_via_sta(struct ieee80211_hw *hw,
+			     struct ieee80211_sta *sta)
+{
+	struct mwl_priv *priv = hw->priv;
+	int num;
+	struct sk_buff *skb, *tmp;
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_tx_ctrl *tx_ctrl;
+	struct sk_buff_head *amsdu_pkts;
+
+	for (num = 1; num < SYSADPT_NUM_OF_DESC_DATA; num++) {
+		spin_lock_bh(&priv->txq[num].lock);
+		skb_queue_walk_safe(&priv->txq[num], skb, tmp) {
+			tx_info = IEEE80211_SKB_CB(skb);
+			tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+			if (tx_ctrl->sta == sta) {
+				amsdu_pkts = (struct sk_buff_head *)
+					tx_ctrl->amsdu_pkts;
+				if (amsdu_pkts) {
+					skb_queue_purge(amsdu_pkts);
+					kfree(amsdu_pkts);
+				}
+				__skb_unlink(skb, &priv->txq[num]);
+				dev_kfree_skb_any(skb);
+			}
+		}
+		spin_unlock_bh(&priv->txq[num].lock);
+	}
+}
+
+void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw,
+			   struct ieee80211_sta *sta, u8 tid)
+{
+	struct mwl_priv *priv = hw->priv;
+	struct mwl_sta *sta_info = mwl_dev_get_sta(sta);
+	int desc_num = SYSADPT_TX_WMM_QUEUES - tid - 1;
+	struct mwl_amsdu_frag *amsdu_frag;
+	struct sk_buff *skb, *tmp;
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_tx_ctrl *tx_ctrl;
+	struct sk_buff_head *amsdu_pkts;
+
+	spin_lock_bh(&priv->txq[desc_num].lock);
+	skb_queue_walk_safe(&priv->txq[desc_num], skb, tmp) {
+		tx_info = IEEE80211_SKB_CB(skb);
+		tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+		if (tx_ctrl->sta == sta) {
+			amsdu_pkts = (struct sk_buff_head *)
+				tx_ctrl->amsdu_pkts;
+			if (amsdu_pkts) {
+				skb_queue_purge(amsdu_pkts);
+				kfree(amsdu_pkts);
+			}
+			__skb_unlink(skb, &priv->txq[desc_num]);
+			dev_kfree_skb_any(skb);
+		}
+	}
+	spin_unlock_bh(&priv->txq[desc_num].lock);
+
+	spin_lock_bh(&sta_info->amsdu_lock);
+	amsdu_frag = &sta_info->amsdu_ctrl.frag[desc_num];
+	if (amsdu_frag->num) {
+		amsdu_frag->num = 0;
+		amsdu_frag->cur_pos = NULL;
+		if (amsdu_frag->skb) {
+			tx_info = IEEE80211_SKB_CB(amsdu_frag->skb);
+			tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+			amsdu_pkts = (struct sk_buff_head *)
+				tx_ctrl->amsdu_pkts;
+			if (amsdu_pkts) {
+				skb_queue_purge(amsdu_pkts);
+				kfree(amsdu_pkts);
+			}
+			dev_kfree_skb_any(amsdu_frag->skb);
+		}
+	}
+	spin_unlock_bh(&sta_info->amsdu_lock);
+}
+
+void mwl_tx_done(unsigned long data)
+{
+	struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+	struct mwl_priv *priv = hw->priv;
+	int num;
+	struct mwl_desc_data *desc;
+	struct mwl_tx_hndl *tx_hndl;
+	struct mwl_tx_desc *tx_desc;
+	struct sk_buff *done_skb;
+	u32 rate;
+	struct mwl_dma_data *tr;
+	struct ieee80211_tx_info *info;
+	struct mwl_tx_ctrl *tx_ctrl;
+	struct sk_buff_head *amsdu_pkts;
+	int hdrlen;
+
+	spin_lock_bh(&priv->tx_desc_lock);
+	for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) {
+		desc = &priv->desc_data[num];
+		tx_hndl = desc->pstale_tx_hndl;
+		tx_desc = tx_hndl->pdesc;
+
+		while (tx_hndl &&
+		       (tx_desc->status & cpu_to_le32(EAGLE_TXD_STATUS_OK)) &&
+		       (!(tx_desc->status &
+		       cpu_to_le32(EAGLE_TXD_STATUS_FW_OWNED)))) {
+			pci_unmap_single(priv->pdev,
+					 le32_to_cpu(tx_desc->pkt_ptr),
+					 le16_to_cpu(tx_desc->pkt_len),
+					 PCI_DMA_TODEVICE);
+			done_skb = tx_hndl->psk_buff;
+			rate = le32_to_cpu(tx_desc->rate_info);
+			tx_desc->pkt_len = 0;
+			tx_desc->status =
+				cpu_to_le32(EAGLE_TXD_STATUS_IDLE);
+			tx_hndl->psk_buff = NULL;
+			wmb(); /* memory barrier */
+
+			skb_get(done_skb);
+			skb_queue_tail(&priv->delay_q, done_skb);
+			if (skb_queue_len(&priv->delay_q) >
+			    SYSADPT_DELAY_FREE_Q_LIMIT)
+				dev_kfree_skb_any(skb_dequeue(&priv->delay_q));
+
+			tr = (struct mwl_dma_data *)done_skb->data;
+			info = IEEE80211_SKB_CB(done_skb);
+
+			if (ieee80211_is_data(tr->wh.frame_control) ||
+			    ieee80211_is_data_qos(tr->wh.frame_control)) {
+				tx_ctrl = (struct mwl_tx_ctrl *)&info->status;
+				amsdu_pkts = (struct sk_buff_head *)
+					tx_ctrl->amsdu_pkts;
+				if (amsdu_pkts) {
+					mwl_tx_ack_amsdu_pkts(hw, rate,
+							      amsdu_pkts);
+					dev_kfree_skb_any(done_skb);
+					done_skb = NULL;
+				} else {
+					mwl_tx_prepare_info(hw, rate, info);
+				}
+			} else {
+				mwl_tx_prepare_info(hw, 0, info);
+			}
+
+			if (done_skb) {
+				/* Remove H/W dma header */
+				hdrlen = ieee80211_hdrlen(tr->wh.frame_control);
+				memmove(tr->data - hdrlen, &tr->wh, hdrlen);
+				skb_pull(done_skb, sizeof(*tr) - hdrlen);
+				info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+				info->flags |= IEEE80211_TX_STAT_ACK;
+				ieee80211_tx_status(hw, done_skb);
+			}
+
+			tx_hndl = tx_hndl->pnext;
+			tx_desc = tx_hndl->pdesc;
+			priv->fw_desc_cnt[num]--;
+		}
+
+		desc->pstale_tx_hndl = tx_hndl;
+	}
+	spin_unlock_bh(&priv->tx_desc_lock);
+
+	if (priv->irq != -1) {
+		u32 status_mask;
+
+		status_mask = readl(priv->iobase1 +
+				    MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+		writel(status_mask | MACREG_A2HRIC_BIT_TX_DONE,
+		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+		mwl_tx_skbs(hw);
+	}
+
+	priv->is_tx_schedule = false;
+}
+
+void mwl_tx_flush_amsdu(unsigned long data)
+{
+	struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+	struct mwl_priv *priv = hw->priv;
+	u32 status_mask;
+	struct mwl_sta *sta_info;
+	int i;
+	struct mwl_amsdu_frag *amsdu_frag;
+
+	spin_lock(&priv->sta_lock);
+	list_for_each_entry(sta_info, &priv->sta_list, list) {
+		spin_lock(&priv->tx_desc_lock);
+		spin_lock(&sta_info->amsdu_lock);
+		for (i = 0; i < SYSADPT_TX_WMM_QUEUES; i++) {
+			amsdu_frag = &sta_info->amsdu_ctrl.frag[i];
+			if (amsdu_frag->num) {
+				if (time_after(jiffies,
+					       (amsdu_frag->jiffies + 1))) {
+					if (mwl_tx_available(priv, i)) {
+						mwl_tx_skb(priv, i,
+							   amsdu_frag->skb);
+						amsdu_frag->num = 0;
+						amsdu_frag->cur_pos = NULL;
+					}
+				}
+			}
+		}
+		spin_unlock(&sta_info->amsdu_lock);
+		spin_unlock(&priv->tx_desc_lock);
+	}
+	spin_unlock(&priv->sta_lock);
+
+	status_mask = readl(priv->iobase1 +
+			    MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+	writel(status_mask | MACREG_A2HRIC_BIT_QUE_EMPTY,
+	       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+	priv->is_qe_schedule = false;
+}
+
+void mwl_tx_del_sta_amsdu_pkts(struct ieee80211_sta *sta)
+{
+	struct mwl_sta *sta_info = mwl_dev_get_sta(sta);
+	int num;
+	struct mwl_amsdu_frag *amsdu_frag;
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_tx_ctrl *tx_ctrl;
+	struct sk_buff_head *amsdu_pkts;
+
+	spin_lock_bh(&sta_info->amsdu_lock);
+	for (num = 0; num < SYSADPT_TX_WMM_QUEUES; num++) {
+		amsdu_frag = &sta_info->amsdu_ctrl.frag[num];
+		if (amsdu_frag->num) {
+			amsdu_frag->num = 0;
+			amsdu_frag->cur_pos = NULL;
+			if (amsdu_frag->skb) {
+				tx_info = IEEE80211_SKB_CB(amsdu_frag->skb);
+				tx_ctrl = (struct mwl_tx_ctrl *)
+					&tx_info->status;
+				amsdu_pkts = (struct sk_buff_head *)
+					tx_ctrl->amsdu_pkts;
+				if (amsdu_pkts) {
+					skb_queue_purge(amsdu_pkts);
+					kfree(amsdu_pkts);
+				}
+				dev_kfree_skb_any(amsdu_frag->skb);
+			}
+		}
+	}
+	spin_unlock_bh(&sta_info->amsdu_lock);
+}
diff --git a/drivers/net/wireless/marvell/mwlwifi/tx.h b/drivers/net/wireless/marvell/mwlwifi/tx.h
new file mode 100644
index 0000000..ef1c7c5
--- /dev/null
+++ b/drivers/net/wireless/marvell/mwlwifi/tx.h
@@ -0,0 +1,36 @@ 
+/*
+ * Copyright (C) 2006-2015, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+/* Description:  This file defines transmit related functions. */
+
+#ifndef _tx_h_
+#define _tx_h_
+
+int mwl_tx_init(struct ieee80211_hw *hw);
+void mwl_tx_deinit(struct ieee80211_hw *hw);
+void mwl_tx_xmit(struct ieee80211_hw *hw,
+		 struct ieee80211_tx_control *control,
+		 struct sk_buff *skb);
+void mwl_tx_del_pkts_via_vif(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif);
+void mwl_tx_del_pkts_via_sta(struct ieee80211_hw *hw,
+			     struct ieee80211_sta *sta);
+void mwl_tx_del_ampdu_pkts(struct ieee80211_hw *hw,
+			   struct ieee80211_sta *sta, u8 tid);
+void mwl_tx_done(unsigned long data);
+void mwl_tx_flush_amsdu(unsigned long data);
+void mwl_tx_del_sta_amsdu_pkts(struct ieee80211_sta *sta);
+
+#endif /* _tx_h_ */