diff mbox

[v5] Add new mac80211 driver mwlwifi.

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

Commit Message

David Lin July 3, 2015, 6:10 a.m. UTC
The Linux driver for WRT1900AC. The work was initially developed as part of
openwrt effort and maintained on https://github.com/kaloz/mwlwifi.

This is still work in progress, with 8864 chipset more mature and tested,
while 8897 for the similar use case is added recently.

Signed-off-by: David Lin <dlin@marvell.com>
---
 drivers/net/wireless/Kconfig             |    1 +
 drivers/net/wireless/Makefile            |    2 +
 drivers/net/wireless/mwlwifi/Kconfig     |   24 +
 drivers/net/wireless/mwlwifi/MAINTAINERS |    5 +
 drivers/net/wireless/mwlwifi/Makefile    |   11 +
 drivers/net/wireless/mwlwifi/dev.h       |  435 ++++++
 drivers/net/wireless/mwlwifi/fwcmd.c     | 2278 ++++++++++++++++++++++++++++++
 drivers/net/wireless/mwlwifi/fwcmd.h     |  175 +++
 drivers/net/wireless/mwlwifi/fwdl.c      |  183 +++
 drivers/net/wireless/mwlwifi/fwdl.h      |   27 +
 drivers/net/wireless/mwlwifi/hostcmd.h   |  753 ++++++++++
 drivers/net/wireless/mwlwifi/isr.c       |  148 ++
 drivers/net/wireless/mwlwifi/isr.h       |   26 +
 drivers/net/wireless/mwlwifi/mac80211.c  |  739 ++++++++++
 drivers/net/wireless/mwlwifi/mac80211.h  |   25 +
 drivers/net/wireless/mwlwifi/main.c      |  856 +++++++++++
 drivers/net/wireless/mwlwifi/rx.c        |  519 +++++++
 drivers/net/wireless/mwlwifi/rx.h        |   25 +
 drivers/net/wireless/mwlwifi/sysadpt.h   |   67 +
 drivers/net/wireless/mwlwifi/tx.c        |  834 +++++++++++
 drivers/net/wireless/mwlwifi/tx.h        |   28 +
 21 files changed, 7161 insertions(+)
 create mode 100644 drivers/net/wireless/mwlwifi/Kconfig
 create mode 100644 drivers/net/wireless/mwlwifi/MAINTAINERS
 create mode 100644 drivers/net/wireless/mwlwifi/Makefile
 create mode 100644 drivers/net/wireless/mwlwifi/dev.h
 create mode 100644 drivers/net/wireless/mwlwifi/fwcmd.c
 create mode 100644 drivers/net/wireless/mwlwifi/fwcmd.h
 create mode 100644 drivers/net/wireless/mwlwifi/fwdl.c
 create mode 100644 drivers/net/wireless/mwlwifi/fwdl.h
 create mode 100644 drivers/net/wireless/mwlwifi/hostcmd.h
 create mode 100644 drivers/net/wireless/mwlwifi/isr.c
 create mode 100644 drivers/net/wireless/mwlwifi/isr.h
 create mode 100644 drivers/net/wireless/mwlwifi/mac80211.c
 create mode 100644 drivers/net/wireless/mwlwifi/mac80211.h
 create mode 100644 drivers/net/wireless/mwlwifi/main.c
 create mode 100644 drivers/net/wireless/mwlwifi/rx.c
 create mode 100644 drivers/net/wireless/mwlwifi/rx.h
 create mode 100644 drivers/net/wireless/mwlwifi/sysadpt.h
 create mode 100644 drivers/net/wireless/mwlwifi/tx.c
 create mode 100644 drivers/net/wireless/mwlwifi/tx.h

Comments

Johannes Berg July 3, 2015, 9:51 a.m. UTC | #1
> +/* Bit definitio for MACREG_REG_A2H_INTERRUPT_CAUSE (A2HRIC) */

typo

> +/* Bit definitio for MACREG_REG_H2A_INTERRUPT_CAUSE (H2ARIC) */

same here

> +struct mwl_chip_info {
> +	char *part_name;
> +	char *fw_image;
> +};

const char *?

> +struct mwl_tx_hndl {
> +	struct sk_buff *psk_buff;
> +	u8 *sta_info;

u8 * seems very strange for sta_info?

> +	struct mwl_tx_desc *pdesc;
> +	struct mwl_tx_hndl *pnext;
> +};

You probably shouldn't hand-write your own linked list
implementation... You could possibly even put all this into the skb->cb
and get rid of the extra struct entirely, just using an skb list?

> +struct mwl_rx_hndl {
> +	struct sk_buff *psk_buff;    /* associated sk_buff for Linux  
     */
> +	struct mwl_rx_desc *pdesc;
> +	struct mwl_rx_hndl *pnext;
> +};

Ditto here.


> +	struct mwl_tx_pwr_tbl tx_pwr_tbl[SYSADPT_MAX_NUM_CHANNELS];
> +	u32 cdd;                     /* 0: off, 1: on */

bool?

> +	spinlock_t sta_lock;         /* for private sta info        
 */
> +	struct list_head sta_list;   /* List of stations            
 */

Could consider using the mac80211 station iteration
ieee80211_iterate_stations_atomic().

> +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];
> +	u16 ie_wmm_len;               /* Keep WMM IE */
> +	u8 *ie_wmm_ptr;
> +	u16 ie_rsn_len;               /* Keep WPA IE */
> +	u8 *ie_rsn_ptr;
> +	u16 ie_rsn48_len;             /* Keep WPA2 IE */
> +	u8 *ie_rsn48_ptr;
> +	u16 ie_ht_len;                /* Keep HT IE */
> +	u8 *ie_ht_ptr;
> +	u8 ie_list_ht[148];
> +	u16 ie_vht_len;               /* Keep VHT IE */
> +	u8 *ie_vht_ptr;
> +	u8 ie_list_vht[24];
> +};

That struct is packed really inefficiently with pointers and u16s
interleaved. Those u16s can also be u8s.

> +struct mwl_vif {
> +	struct list_head list;

Could also consider using iterate_active_interfaces() and friends.

> +	struct ieee80211_vif *vif;

and you should probably embed the struct into the vif->priv, then you
don't need a vif pointer in it (use container_of)

> +	u16 seqno;       /* Non AMPDU sequence number assigned by
driver.  */

That seems a bit strange - why not just leave it up to mac80211 then?

> +	/* Indicate if this is station mode */
> +	bool is_sta;

use vif->type instead

> +struct mwl_amsdu_frag {
> +	struct sk_buff *skb;
> +	u8 pad;
> +	u8 *cur_pos;
> +	u8 num;
> +	u32 jiffies;

unsigned long

also struct ordering is really about as bad as it could be with lots of
padding

> +struct mwl_sta {
> +	struct list_head list;
> +	struct ieee80211_sta *sta;

same here - embed the struct into sta->priv and get rid of the sta
pointer

> +/* Transmission information to transmit a socket buffer. */
> +struct mwl_tx_ctrl {
> +	void *sta;
> +	void *vif;
> +	void *k_conf;

void pointers don't seem so great
But perhaps you do want them to avoid mistakes, but you should add a
comment then.
If you're not sure what mistakes I'm talking about - consider an
interface or station that's removed while the packet is pending TX.

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

Oh wait, so you *do* embed the structs - you can use container_of() to
go the other way then and get rid of the pointers.

> +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 {
> +		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));
> +		return -EIO;
> +	}
> +
> +	mdelay(3);

Both mdelay(1) and mdelay(3) are EXTREMELY long for atomic context,
this is made much worse by the iteration counter starting at 10000,
which means that this function can, in the failure case, block the
system for 10 seconds!
That seems excessive, particularly when it's called from atomic
context.

Do you really need that btw? might be far better to sleep for some
event/interrupt here. Almost all mac80211 API functions can sleep, and
you probably (hopefully) aren't getting here for TX/RX.


> +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(&priv->fwcmd_lock);

Maybe then that could also be a mutex instead.

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

This is kinda messed up - why aren't you using something like channel
->hw_channel or whatever it's called?

> +static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif
*mwl_vif)
> +{
> +	struct hostcmd_cmd_set_ies *pcmd;
> +
> +	if (!mwl_vif->beacon_info.valid)
> +		return -EINVAL;
> +
> +	if (mwl_vif->beacon_info.ie_ht_len > sizeof(pcmd->ie_list_ht))
> +		goto einval;
> +
> +	if (mwl_vif->beacon_info.ie_vht_len > sizeof(pcmd
->ie_list_vht))
> +		goto einval;
> +
> +	pcmd = (struct hostcmd_cmd_set_ies *)&priv->pcmd_buf[0];
> +
> +	spin_lock(&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);
> +
> +	pcmd->ie_list_len_ht = cpu_to_le16(mwl_vif
->beacon_info.ie_ht_len);
> +	memcpy(pcmd->ie_list_ht, mwl_vif->beacon_info.ie_ht_ptr,
> +	       mwl_vif->beacon_info.ie_ht_len);
> +
> +	pcmd->ie_list_len_vht = cpu_to_le16(mwl_vif
->beacon_info.ie_vht_len);
> +	memcpy(pcmd->ie_list_vht, mwl_vif->beacon_info.ie_vht_ptr,
> +	       mwl_vif->beacon_info.ie_vht_len);
> +
> +	if (priv->chip_type == MWL8897) {
> +		pcmd->ie_list_len_proprietary =
> +			cpu_to_le16(mwl_vif->beacon_info.ie_wmm_len);
> +		memcpy(pcmd->ie_list_proprietary,
> +		       mwl_vif->beacon_info.ie_wmm_ptr,
> +		       mwl_vif->beacon_info.ie_wmm_len);
> +	}

Why would the firmware even care about the split into the various IEs?

You're potentially losing information here by treating *only* the WMM
IE as the "remaining" - you really should try harder to preserve the
IEs coming from hostapd completely.

> +void mwl_fwcmd_int_disable(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv;
> +
> +	priv = hw->priv;

You can roll that into one line btw, in many many places, to reduce the
length of your code.

> +#if SYSADPT_NUM_OF_DESC_DATA > 3

It doesn't seem right for this to be a compile-time flag? If it's
needed for different devices then it should be a run-time choice, if
it's just a constant why bother with the #if, and if it's some sort of
compile-time option then shouldn't it be a CONFIG_*?


> +int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv;
> +	struct hostcmd_cmd_set_hw_spec *pcmd;
> +	int i;
> +
> +	priv = hw->priv;
> +
> +	pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0];
> +
> +	spin_lock(&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);
> +#if SYSADPT_NUM_OF_DESC_DATA > 3
> +	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
> +		pcmd->wcb_base[i] =
> +			cpu_to_le32(priv->desc_data[i].pphys_tx_ring);
> +#endif

Also here, with the additional wrinkle that you'll get a compiler
warning (unused variable 'i') when it's <= 3.

> +static int mwl_mac80211_start(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv;
> +	int rc;
> +
> +	priv = hw->priv;
> +
> +	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");
> +		return rc;
> +	}
> +	priv->irq = priv->pdev->irq;

Why do you do this every time you bring up the device? It's smarter to
do this once for registration.

> +	/* Enable TX reclaim and RX tasklets. */
> +	tasklet_enable(&priv->tx_task);
> +	tasklet_enable(&priv->rx_task);
> +
> +	/* Enable interrupts */
> +	mwl_fwcmd_int_enable(hw);

Obviously you don't have to *enable* interrupts, until here, but re
-registering all the time seems strange.

> +	/* Disable TX reclaim and RX tasklets. */
> +	tasklet_disable(&priv->tx_task);
> +	tasklet_disable(&priv->rx_task);
> +
> +	/* Return all skbs to mac80211 */
> +	mwl_tx_done((unsigned long)hw);

Err, why would you have an internal function that takes an 'unsigned
long' instead of a properly typed pointer??

> +	if (!macid--) {

That's ... awkward. Better rewrite it to check first and then decrement
later.

> +	if (vif->type == NL80211_IFTYPE_STATION) {
> +		ether_addr_copy(mwl_vif->sta_mac, vif->addr);
> +		mwl_vif->is_sta = true;
> +		mwl_fwcmd_bss_start(hw, vif, true);
> +		mwl_fwcmd_set_infra_mode(hw, vif);
> +		mwl_fwcmd_set_mac_addr_client(hw, vif, vif->addr);
> +	}
> +
> +	if (vif->type == NL80211_IFTYPE_AP) {
> +		ether_addr_copy(mwl_vif->bssid, vif->addr);
> +		mwl_fwcmd_set_new_stn_add_self(hw, vif);
> +	}

switch?

> +	if (vif->type == NL80211_IFTYPE_STATION)
> +		mwl_fwcmd_remove_mac_addr(hw, vif, vif->addr);
> +
> +	if (vif->type == NL80211_IFTYPE_AP)
> +		mwl_fwcmd_set_new_stn_del(hw, vif, vif->addr);

ditto

> +	wiphy_debug(hw->wiphy, "interface: %d, change: 0x%x\n",
> +		    vif->type, changed);
> +
> +	if (vif->type == NL80211_IFTYPE_STATION)
> +		mwl_mac80211_bss_info_changed_sta(hw, vif, info,
changed);
> +
> +	if (vif->type == NL80211_IFTYPE_AP)
> +		mwl_mac80211_bss_info_changed_ap(hw, vif, info,
changed);

ditto

> +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)
> +{
[...]
> +	spin_lock(&priv->stream_lock);
[...]
> +			stream = mwl_fwcmd_add_stream(hw, sta, tid);

This is about the only place that actually sends a command while it
can't sleep. It seems you should be able to restructure this function
to avoid that, so that you don't have to use a spinlock for command
sending and don't need all that busy-polling but can properly go to
sleep while waiting for the firmware to respond to a command.

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

All the callbacks that you use can sleep (apart from TX where you don't
send firmware commands), so really all you have to do is restructure
the aggregation code slightly.

> diff --git a/drivers/net/wireless/mwlwifi/mac80211.h
b/drivers/net/wireless/mwlwifi/mac80211.h
> new file mode 100644
> index 0000000..0267dd85
> --- /dev/null
> +++ b/drivers/net/wireless/mwlwifi/mac80211.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 mac80211 related functions. */
> +
> +#ifndef _mac80211_h_
> +#define _mac80211_h_
> +
> +#include <net/mac80211.h>
> +
> +extern const struct ieee80211_ops mwl_mac80211_ops;
> +
> +#endif /* _mac80211_h_ */

That file seems somewhat pointless :)
Just put the one line into dev.h?

> +	wiphy_info(priv->hw->wiphy, "priv->iobase1 = %p\n", priv
->iobase1);

At "info" level, that seems a bit overkill.

> +	priv->pcmd_buf =
> +		(unsigned short *)dma_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_iounmap_iobase1;
> +	}
> +
> +	wiphy_info(priv->hw->wiphy,
> +		   "priv->pcmd_buf = %p  priv->pphys_cmd_buf = %p\n",
> +		   priv->pcmd_buf,
> +		   (void *)priv->pphys_cmd_buf);

Ditto. Nobody will ever want to see this.

Also, there's usually no need to print anything on allocation failures
as they're very noisy already.

> +     rc = pci_set_dma_mask(pdev, 0xffffffff);

You should use one of the DMA mask constants.

> +	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;
> +			}
> +
> +			if (skb_linearize(rx_hndl->psk_buff)) {
> +				dev_kfree_skb_any(rx_hndl->psk_buff);
> +				rx_hndl->psk_buff = NULL;
> +				wiphy_err(priv->hw->wiphy,
> +					  "need linearize memory\n");
> +				return -ENOMEM;
> +			}

This is still completely pointless.

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

You need to check the return value (properly - use the right function,
not != NULL!)

> +	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;
> +
> +			if (skb_shinfo(rx_hndl->psk_buff)->nr_frags)
> +				skb_shinfo(rx_hndl->psk_buff)
->nr_frags = 0;
> +
> +			if (skb_shinfo(rx_hndl->psk_buff)->frag_list)
> +				skb_shinfo(rx_hndl->psk_buff)
->frag_list = NULL;

How do you think this would make any sense??


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

The code formatting for this condition is very very strange.

> +static int mwl_rx_refill(struct mwl_priv *priv, struct mwl_rx_hndl
*rx_hndl)
> +{
> +	struct mwl_desc_data *desc;
> +
> +	desc = &priv->desc_data[0];
> +
> +	rx_hndl->psk_buff = dev_alloc_skb(desc->rx_buf_size);
> +
> +	if (!rx_hndl->psk_buff)
> +		goto nomem;
> +
> +	if (skb_linearize(rx_hndl->psk_buff)) {
> +		dev_kfree_skb_any(rx_hndl->psk_buff);
> +		wiphy_err(priv->hw->wiphy, "need linearize memory\n");
> +		goto nomem;
> +	}

Still make no sense.

> +	rx_hndl->pdesc->pphys_buff_data =
> +		cpu_to_le32(pci_map_single(priv->pdev,
> +					   rx_hndl->psk_buff->data,
> +					   desc->rx_buf_size,
> +					   PCI_DMA_BIDIRECTIONAL));

Also need to check the return value. BIDIRECTIONAL also seems wrong.


> +int mwl_rx_init(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv;
> +	int rc;
> +
> +	priv = hw->priv;
> +
> +	rc = mwl_rx_ring_alloc(priv);
> +	if (rc) {
> +		wiphy_err(hw->wiphy, "allocating RX ring failed\n");
> +	} else {
> +		rc = mwl_rx_ring_init(priv);
> +		if (rc) {
> +			mwl_rx_ring_free(priv);
> +			wiphy_err(hw->wiphy,
> +				  "initializing RX ring failed\n");
> +		}
> +	}

Better write this as 

 rc = alloc()
 if (rc)
   return;
 rc = init()
 if (rc)
   return;

etc.

> +			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
> +				 * mwl8k Firmware header is sent
> +				 * to the host.
> +				 *
> +				 * We need to add four bytes of
> +				 * key information.  In it
> +				 * MAC80211 expects keyidx set to
> +				 * 0 for triggering Counter
> +				 * Measure of MMIC failure.
> +				 */

Umm. That's only true for PTKs.

> +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 = (u8 *)dma_alloc_coherent(&priv->pdev->dev,
> +		MAX_NUM_TX_RING_BYTES * SYSADPT_NUM_OF_DESC_DATA,
> +		&desc->pphys_tx_ring, GFP_KERNEL);

That cast is pointless, the function returns void *.

> +static inline void mwl_tx_insert_ccmp_hdr(u8 *pccmp_hdr,
> +					  u8 key_id, u16 iv16, u32
iv32)
> +{
> +	*((u16 *)pccmp_hdr) = iv16;
> +	pccmp_hdr[2] = 0;
> +	pccmp_hdr[3] = EXT_IV | (key_id << 6);
> +	*((u32 *)&pccmp_hdr[4]) = iv32;
> +}

Why don't you let mac80211 do this? You also have clear alignment and
endian issues here, so mac80211 will do it better...

> +	if (tx_stats->start_time == 0)
> +		tx_stats->start_time = jiffies;

Btw, don't remember if I commented on this before - but you can't store
jiffies in a u32, need unsigned long.


> +	if (tx_hndl->pdesc->status !=
> +	    EAGLE_TXD_STATUS_IDLE) {

what's with the linebreak?


> +	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 (mwl_vif->is_sta) {
> +					mwl_tx_insert_ccmp_hdr(dma_dat
a->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_dat
a->data,
> +							       0,
> +							      
 sta_info->iv16,
> +							      
 sta_info->iv32);
> +					INCREASE_IV(sta_info->iv16,
> +						    sta_info->iv32);
> +				}
> +			}
> +		}
> +	}

All of this is very very dubious. Why not let mac80211 handle it?

> +	tx_desc->pkt_ptr =
> +		cpu_to_le32(pci_map_single(priv->pdev, tx_skb->data,
> +					   tx_skb->len,
PCI_DMA_TODEVICE));

missing error check

> +int mwl_tx_init(struct ieee80211_hw *hw)
> +{
> +	struct mwl_priv *priv;
> +	int rc;
> +
> +	priv = hw->priv;
> +
> +	skb_queue_head_init(&priv->delay_q);
> +
> +	rc = mwl_tx_ring_alloc(priv);
> +	if (rc) {
> +		wiphy_err(hw->wiphy, "allocating TX ring failed\n");
> +	} else {
> +		rc = mwl_tx_ring_init(priv);
> +		if (rc) {
> +			mwl_tx_ring_free(priv);
> +			wiphy_err(hw->wiphy, "initializing TX ring
failed\n");
> +		}
> +	}
> +
> +	return rc;
> +}

Same comment as on RX.

> +	if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
> +		index = IEEE80211_AC_VO;
> +		eapol_frame = true;
> +	}

There's a tx_info flag for this that's a bit more generic.

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
Jonas Gorski July 4, 2015, 10:40 a.m. UTC | #2
Hi,

On Fri, Jul 3, 2015 at 8:10 AM, David Lin <dlin@marvell.com> wrote:
> The Linux driver for WRT1900AC. The work was initially developed as part of
> openwrt effort and maintained on https://github.com/kaloz/mwlwifi.
>
> This is still work in progress, with 8864 chipset more mature and tested,
> while 8897 for the similar use case is added recently.
>
> Signed-off-by: David Lin <dlin@marvell.com>
> ---
>  drivers/net/wireless/Kconfig             |    1 +
>  drivers/net/wireless/Makefile            |    2 +
>  drivers/net/wireless/mwlwifi/Kconfig     |   24 +
>  drivers/net/wireless/mwlwifi/MAINTAINERS |    5 +
>  drivers/net/wireless/mwlwifi/Makefile    |   11 +
>  drivers/net/wireless/mwlwifi/dev.h       |  435 ++++++
>  drivers/net/wireless/mwlwifi/fwcmd.c     | 2278 ++++++++++++++++++++++++++++++
>  drivers/net/wireless/mwlwifi/fwcmd.h     |  175 +++
>  drivers/net/wireless/mwlwifi/fwdl.c      |  183 +++
>  drivers/net/wireless/mwlwifi/fwdl.h      |   27 +
>  drivers/net/wireless/mwlwifi/hostcmd.h   |  753 ++++++++++
>  drivers/net/wireless/mwlwifi/isr.c       |  148 ++
>  drivers/net/wireless/mwlwifi/isr.h       |   26 +
>  drivers/net/wireless/mwlwifi/mac80211.c  |  739 ++++++++++
>  drivers/net/wireless/mwlwifi/mac80211.h  |   25 +
>  drivers/net/wireless/mwlwifi/main.c      |  856 +++++++++++
>  drivers/net/wireless/mwlwifi/rx.c        |  519 +++++++
>  drivers/net/wireless/mwlwifi/rx.h        |   25 +
>  drivers/net/wireless/mwlwifi/sysadpt.h   |   67 +
>  drivers/net/wireless/mwlwifi/tx.c        |  834 +++++++++++
>  drivers/net/wireless/mwlwifi/tx.h        |   28 +
>  21 files changed, 7161 insertions(+)
>  create mode 100644 drivers/net/wireless/mwlwifi/Kconfig
>  create mode 100644 drivers/net/wireless/mwlwifi/MAINTAINERS
>  create mode 100644 drivers/net/wireless/mwlwifi/Makefile
>  create mode 100644 drivers/net/wireless/mwlwifi/dev.h
>  create mode 100644 drivers/net/wireless/mwlwifi/fwcmd.c
>  create mode 100644 drivers/net/wireless/mwlwifi/fwcmd.h
>  create mode 100644 drivers/net/wireless/mwlwifi/fwdl.c
>  create mode 100644 drivers/net/wireless/mwlwifi/fwdl.h
>  create mode 100644 drivers/net/wireless/mwlwifi/hostcmd.h
>  create mode 100644 drivers/net/wireless/mwlwifi/isr.c
>  create mode 100644 drivers/net/wireless/mwlwifi/isr.h
>  create mode 100644 drivers/net/wireless/mwlwifi/mac80211.c
>  create mode 100644 drivers/net/wireless/mwlwifi/mac80211.h
>  create mode 100644 drivers/net/wireless/mwlwifi/main.c
>  create mode 100644 drivers/net/wireless/mwlwifi/rx.c
>  create mode 100644 drivers/net/wireless/mwlwifi/rx.h
>  create mode 100644 drivers/net/wireless/mwlwifi/sysadpt.h
>  create mode 100644 drivers/net/wireless/mwlwifi/tx.c
>  create mode 100644 drivers/net/wireless/mwlwifi/tx.h
>
> diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
> index a63ab2e..1c60845 100644
> --- a/drivers/net/wireless/Kconfig
> +++ b/drivers/net/wireless/Kconfig
> @@ -284,5 +284,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/mwlwifi/Kconfig"
>
>  endif # WLAN
> diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
> index 6b9e729..1fe0f0d 100644
> --- a/drivers/net/wireless/Makefile
> +++ b/drivers/net/wireless/Makefile
> @@ -62,3 +62,5 @@ obj-$(CONFIG_BRCMSMAC)        += brcm80211/
>
>  obj-$(CONFIG_CW1200)   += cw1200/
>  obj-$(CONFIG_RSI_91X)  += rsi/
> +
> +obj-$(CONFIG_MWLWIFI)  += mwlwifi/
> diff --git a/drivers/net/wireless/mwlwifi/Kconfig b/drivers/net/wireless/mwlwifi/Kconfig
> new file mode 100644
> index 0000000..3732223
> --- /dev/null
> +++ b/drivers/net/wireless/mwlwifi/Kconfig
> @@ -0,0 +1,24 @@
> +config MWLWIFI
> +       tristate "Marvell Wireless Wi-Fi driver (mwlwifi)"

Do you also have wired Wi-Fi cards? ;P

The description seems very generic despite only supporting two chips.
Currently we already have two other marvell drivers claiming to
support "marvell" wireless:

config MWL8K
        tristate "Marvell 88W8xxx PCI/PCIe Wireless support"

config MWIFIEX
        tristate "Marvell WiFi-Ex Driver"

I think it would be good to have some more precise description here
(something like "Marvell 88W8864/8897 PCIe driver with AP support").

> +       depends on PCI && MAC80211
> +       select FW_LOADER
> +       select OF

Please depend on OF instead of selecting it. Unless something changed
recently, forcing OF to y will cause build failures for arches that do
not support OF yet, breaking e.g. allmodconfigs build tests.

> +       ---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/mwlwifi/MAINTAINERS b/drivers/net/wireless/mwlwifi/MAINTAINERS
> new file mode 100644
> index 0000000..5706ce9
> --- /dev/null
> +++ b/drivers/net/wireless/mwlwifi/MAINTAINERS
> @@ -0,0 +1,5 @@
> +MARVELL MWLWIFI WIRELESS DRIVER
> +M:     David Lin <dlin@marvell.com>
> +L:     linux-wireless@vger.kernel.org
> +S:     Maintained
> +F:     drivers/net/wireless/mwlwifi/

Please add this to the top directory MAINTAINERS instead of adding a
separate file (unless adding your own MAINTAINERS is the new thing).


Regards
Jonas
--
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 July 5, 2015, 10:33 a.m. UTC | #3
I'm not so sure about the OF thing - at least in general this seems
right and many drivers are selecting OF. If that causes issues on
certain platforms then perhaps those platforms should fix the issues,
or the whole OF should be changed to depends on?

> > --- /dev/null
> > +++ b/drivers/net/wireless/mwlwifi/MAINTAINERS
> > @@ -0,0 +1,5 @@
> > +MARVELL MWLWIFI WIRELESS DRIVER
> > +M:     David Lin <dlin@marvell.com>
> > +L:     linux-wireless@vger.kernel.org
> > +S:     Maintained
> > +F:     drivers/net/wireless/mwlwifi/
> 
> Please add this to the top directory MAINTAINERS instead of adding a
> separate file (unless adding your own MAINTAINERS is the new thing).
> 

Indeed, good catch.

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
Jonas Gorski July 5, 2015, 10:57 a.m. UTC | #4
On Sun, Jul 5, 2015 at 12:33 PM, Johannes Berg
<johannes@sipsolutions.net> wrote:
> I'm not so sure about the OF thing - at least in general this seems
> right and many drivers are selecting OF. If that causes issues on
> certain platforms then perhaps those platforms should fix the issues,
> or the whole OF should be changed to depends on?

At least in linus' current HEAD I don't see any drivers selecting OF;
only a few that select OF subsystems:

# grep -r --include=Kconfig "select OF" drivers/
drivers/of/Kconfig:     select OF_EARLY_FLATTREE
drivers/of/Kconfig:     select OF_RESOLVE
drivers/of/Kconfig:     select OF_FLATTREE
drivers/of/Kconfig:     select OF_ADDRESS_PCI if PCI
drivers/of/Kconfig:     select OF_DYNAMIC
drivers/of/Kconfig:     select OF_RESOLVE
drivers/gpio/Kconfig:   select OF_GPIO
drivers/pinctrl/mediatek/Kconfig:       select OF_GPIO
drivers/pinctrl/Kconfig:        select OF_GPIO
drivers/pinctrl/Kconfig:        select OF_GPIO
drivers/pinctrl/Kconfig:        select OF_GPIO
drivers/pinctrl/nomadik/Kconfig:        select OF_GPIO
drivers/net/ethernet/freescale/Kconfig: select OF_MDIO
drivers/gpu/drm/tilcdc/Kconfig: select OF_RESOLVE
drivers/gpu/drm/tilcdc/Kconfig: select OF_OVERLAY

There are plenty that depend on OF though:

# grep -r --include=Kconfig "depends on OF" drivers/ | wc -l
159

(I know this won't catch all and a few that it shouldn't ;)

Of course arches/platforms selecting OF is fine and expected:

# grep -r --include=Kconfig "select OF$" arch/ | wc -l
15


Regards
Jonas
--
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 July 7, 2015, 1:47 a.m. UTC | #5
PkpvbmFzIEdvcnNraSB3cm90ZToNCj4gDQo+IEhpLA0KPiANCj4gT24gRnJpLCBKdWwgMywgMjAx
NSBhdCA4OjEwIEFNLCBEYXZpZCBMaW4gPGRsaW5AbWFydmVsbC5jb20+IHdyb3RlOg0KPiA+IFRo
ZSBMaW51eCBkcml2ZXIgZm9yIFdSVDE5MDBBQy4gVGhlIHdvcmsgd2FzIGluaXRpYWxseSBkZXZl
bG9wZWQgYXMNCj4gPiBwYXJ0IG9mIG9wZW53cnQgZWZmb3J0IGFuZCBtYWludGFpbmVkIG9uIGh0
dHBzOi8vZ2l0aHViLmNvbS9rYWxvei9td2x3aWZpLg0KPiA+DQo+ID4gVGhpcyBpcyBzdGlsbCB3
b3JrIGluIHByb2dyZXNzLCB3aXRoIDg4NjQgY2hpcHNldCBtb3JlIG1hdHVyZSBhbmQNCj4gPiB0
ZXN0ZWQsIHdoaWxlIDg4OTcgZm9yIHRoZSBzaW1pbGFyIHVzZSBjYXNlIGlzIGFkZGVkIHJlY2Vu
dGx5Lg0KPiA+DQo+ID4gU2lnbmVkLW9mZi1ieTogRGF2aWQgTGluIDxkbGluQG1hcnZlbGwuY29t
Pg0KPiA+IC0tLQ0KPiA+ICBkcml2ZXJzL25ldC93aXJlbGVzcy9LY29uZmlnICAgICAgICAgICAg
IHwgICAgMSArDQo+ID4gIGRyaXZlcnMvbmV0L3dpcmVsZXNzL01ha2VmaWxlICAgICAgICAgICAg
fCAgICAyICsNCj4gPiAgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9LY29uZmlnICAgICB8
ICAgMjQgKw0KPiA+ICBkcml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL01BSU5UQUlORVJTIHwg
ICAgNSArDQo+ID4gIGRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvTWFrZWZpbGUgICAgfCAg
IDExICsNCj4gPiAgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9kZXYuaCAgICAgICB8ICA0
MzUgKysrKysrDQo+ID4gIGRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvZndjbWQuYyAgICAg
fCAyMjc4DQo+ICsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKw0KPiA+ICBkcml2ZXJzL25l
dC93aXJlbGVzcy9td2x3aWZpL2Z3Y21kLmggICAgIHwgIDE3NSArKysNCj4gPiAgZHJpdmVycy9u
ZXQvd2lyZWxlc3MvbXdsd2lmaS9md2RsLmMgICAgICB8ICAxODMgKysrDQo+ID4gIGRyaXZlcnMv
bmV0L3dpcmVsZXNzL213bHdpZmkvZndkbC5oICAgICAgfCAgIDI3ICsNCj4gPiAgZHJpdmVycy9u
ZXQvd2lyZWxlc3MvbXdsd2lmaS9ob3N0Y21kLmggICB8ICA3NTMgKysrKysrKysrKw0KPiA+ICBk
cml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL2lzci5jICAgICAgIHwgIDE0OCArKw0KPiA+ICBk
cml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL2lzci5oICAgICAgIHwgICAyNiArDQo+ID4gIGRy
aXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvbWFjODAyMTEuYyAgfCAgNzM5ICsrKysrKysrKysN
Cj4gPiAgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9tYWM4MDIxMS5oICB8ICAgMjUgKw0K
PiA+ICBkcml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL21haW4uYyAgICAgIHwgIDg1NiArKysr
KysrKysrKw0KPiA+ICBkcml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL3J4LmMgICAgICAgIHwg
IDUxOSArKysrKysrDQo+ID4gIGRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvcnguaCAgICAg
ICAgfCAgIDI1ICsNCj4gPiAgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9zeXNhZHB0Lmgg
ICB8ICAgNjcgKw0KPiA+ICBkcml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL3R4LmMgICAgICAg
IHwgIDgzNCArKysrKysrKysrKw0KPiA+ICBkcml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL3R4
LmggICAgICAgIHwgICAyOCArDQo+ID4gIDIxIGZpbGVzIGNoYW5nZWQsIDcxNjEgaW5zZXJ0aW9u
cygrKQ0KPiA+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lm
aS9LY29uZmlnDQo+ID4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL25ldC93aXJlbGVzcy9t
d2x3aWZpL01BSU5UQUlORVJTDQo+ID4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL25ldC93
aXJlbGVzcy9td2x3aWZpL01ha2VmaWxlDQo+ID4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJz
L25ldC93aXJlbGVzcy9td2x3aWZpL2Rldi5oDQo+ID4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2
ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL2Z3Y21kLmMNCj4gPiAgY3JlYXRlIG1vZGUgMTAwNjQ0
IGRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvZndjbWQuaA0KPiA+ICBjcmVhdGUgbW9kZSAx
MDA2NDQgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9md2RsLmMNCj4gPiAgY3JlYXRlIG1v
ZGUgMTAwNjQ0IGRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvZndkbC5oDQo+ID4gIGNyZWF0
ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL2hvc3RjbWQuaA0KPiA+
ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9pc3IuYw0K
PiA+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9pc3Iu
aA0KPiA+ICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9t
YWM4MDIxMS5jDQo+ID4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL25ldC93aXJlbGVzcy9t
d2x3aWZpL21hYzgwMjExLmgNCj4gPiAgY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvbmV0L3dp
cmVsZXNzL213bHdpZmkvbWFpbi5jDQo+ID4gIGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL25l
dC93aXJlbGVzcy9td2x3aWZpL3J4LmMgIGNyZWF0ZSBtb2RlDQo+ID4gMTAwNjQ0IGRyaXZlcnMv
bmV0L3dpcmVsZXNzL213bHdpZmkvcnguaCAgY3JlYXRlIG1vZGUgMTAwNjQ0DQo+ID4gZHJpdmVy
cy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9zeXNhZHB0LmgNCj4gPiAgY3JlYXRlIG1vZGUgMTAwNjQ0
IGRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvdHguYyAgY3JlYXRlIG1vZGUNCj4gPiAxMDA2
NDQgZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS90eC5oDQo+ID4NCj4gPiBkaWZmIC0tZ2l0
IGEvZHJpdmVycy9uZXQvd2lyZWxlc3MvS2NvbmZpZw0KPiA+IGIvZHJpdmVycy9uZXQvd2lyZWxl
c3MvS2NvbmZpZyBpbmRleCBhNjNhYjJlLi4xYzYwODQ1IDEwMDY0NA0KPiA+IC0tLSBhL2RyaXZl
cnMvbmV0L3dpcmVsZXNzL0tjb25maWcNCj4gPiArKysgYi9kcml2ZXJzL25ldC93aXJlbGVzcy9L
Y29uZmlnDQo+ID4gQEAgLTI4NCw1ICsyODQsNiBAQCBzb3VyY2UgImRyaXZlcnMvbmV0L3dpcmVs
ZXNzL3pkMTIxMXJ3L0tjb25maWciDQo+ID4gIHNvdXJjZSAiZHJpdmVycy9uZXQvd2lyZWxlc3Mv
bXdpZmlleC9LY29uZmlnIg0KPiA+ICBzb3VyY2UgImRyaXZlcnMvbmV0L3dpcmVsZXNzL2N3MTIw
MC9LY29uZmlnIg0KPiA+ICBzb3VyY2UgImRyaXZlcnMvbmV0L3dpcmVsZXNzL3JzaS9LY29uZmln
Ig0KPiA+ICtzb3VyY2UgImRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvS2NvbmZpZyINCj4g
Pg0KPiA+ICBlbmRpZiAjIFdMQU4NCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9uZXQvd2lyZWxl
c3MvTWFrZWZpbGUNCj4gPiBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL01ha2VmaWxlIGluZGV4IDZi
OWU3MjkuLjFmZTBmMGQgMTAwNjQ0DQo+ID4gLS0tIGEvZHJpdmVycy9uZXQvd2lyZWxlc3MvTWFr
ZWZpbGUNCj4gPiArKysgYi9kcml2ZXJzL25ldC93aXJlbGVzcy9NYWtlZmlsZQ0KPiA+IEBAIC02
MiwzICs2Miw1IEBAIG9iai0kKENPTkZJR19CUkNNU01BQykgICAgICAgICs9IGJyY204MDIxMS8N
Cj4gPg0KPiA+ICBvYmotJChDT05GSUdfQ1cxMjAwKSAgICs9IGN3MTIwMC8NCj4gPiAgb2JqLSQo
Q09ORklHX1JTSV85MVgpICArPSByc2kvDQo+ID4gKw0KPiA+ICtvYmotJChDT05GSUdfTVdMV0lG
SSkgICs9IG13bHdpZmkvDQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvbmV0L3dpcmVsZXNzL213
bHdpZmkvS2NvbmZpZw0KPiA+IGIvZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9LY29uZmln
DQo+ID4gbmV3IGZpbGUgbW9kZSAxMDA2NDQNCj4gPiBpbmRleCAwMDAwMDAwLi4zNzMyMjIzDQo+
ID4gLS0tIC9kZXYvbnVsbA0KPiA+ICsrKyBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkv
S2NvbmZpZw0KPiA+IEBAIC0wLDAgKzEsMjQgQEANCj4gPiArY29uZmlnIE1XTFdJRkkNCj4gPiAr
ICAgICAgIHRyaXN0YXRlICJNYXJ2ZWxsIFdpcmVsZXNzIFdpLUZpIGRyaXZlciAobXdsd2lmaSki
DQo+IA0KPiBEbyB5b3UgYWxzbyBoYXZlIHdpcmVkIFdpLUZpIGNhcmRzPyA7UA0KPiANCj4gVGhl
IGRlc2NyaXB0aW9uIHNlZW1zIHZlcnkgZ2VuZXJpYyBkZXNwaXRlIG9ubHkgc3VwcG9ydGluZyB0
d28gY2hpcHMuDQo+IEN1cnJlbnRseSB3ZSBhbHJlYWR5IGhhdmUgdHdvIG90aGVyIG1hcnZlbGwg
ZHJpdmVycyBjbGFpbWluZyB0byBzdXBwb3J0DQo+ICJtYXJ2ZWxsIiB3aXJlbGVzczoNCj4gDQo+
IGNvbmZpZyBNV0w4Sw0KPiAgICAgICAgIHRyaXN0YXRlICJNYXJ2ZWxsIDg4Vzh4eHggUENJL1BD
SWUgV2lyZWxlc3Mgc3VwcG9ydCINCj4gDQo+IGNvbmZpZyBNV0lGSUVYDQo+ICAgICAgICAgdHJp
c3RhdGUgIk1hcnZlbGwgV2lGaS1FeCBEcml2ZXIiDQo+IA0KPiBJIHRoaW5rIGl0IHdvdWxkIGJl
IGdvb2QgdG8gaGF2ZSBzb21lIG1vcmUgcHJlY2lzZSBkZXNjcmlwdGlvbiBoZXJlDQo+IChzb21l
dGhpbmcgbGlrZSAiTWFydmVsbCA4OFc4ODY0Lzg4OTcgUENJZSBkcml2ZXIgd2l0aCBBUCBzdXBw
b3J0IikuDQo+DQoNCldpbGwgY2hhbmdlIHRoZSB0aXRsZSBmcm9tICJNYXJ2ZWxsIFdpcmVsZXNz
IFdpLUZpIGRyaXZlciAobXdsd2lmaSkiIHRvICJNYXJ2ZWxsIDg4Vzg4NjQvODhXODg5NyBQQ0ll
IGRyaXZlciB3aXRoIEFQIHN1cHBvcnQiLg0KDQo+IA0KPiA+ICsgICAgICAgZGVwZW5kcyBvbiBQ
Q0kgJiYgTUFDODAyMTENCj4gPiArICAgICAgIHNlbGVjdCBGV19MT0FERVINCj4gPiArICAgICAg
IHNlbGVjdCBPRg0KPiANCj4gUGxlYXNlIGRlcGVuZCBvbiBPRiBpbnN0ZWFkIG9mIHNlbGVjdGlu
ZyBpdC4gVW5sZXNzIHNvbWV0aGluZyBjaGFuZ2VkDQo+IHJlY2VudGx5LCBmb3JjaW5nIE9GIHRv
IHkgd2lsbCBjYXVzZSBidWlsZCBmYWlsdXJlcyBmb3IgYXJjaGVzIHRoYXQgZG8gbm90IHN1cHBv
cnQNCj4gT0YgeWV0LCBicmVha2luZyBlLmcuIGFsbG1vZGNvbmZpZ3MgYnVpbGQgdGVzdHMuDQo+
DQoNCldpbGwgbW9kaWZ5IHRoZSBkcml2ZXIgdG8gc3RpbGwgd29yayBldmVuIGlmIHRhcmdldCBh
cmNoaXRlY3R1cmUgZG9lcyBub3Qgc3VwcG9ydCBPRi4NCg0KPiANCj4gPiArICAgICAgIC0tLWhl
bHAtLS0NCj4gPiArICAgICAgICAgICAgICAgU2VsZWN0IHRvIGJ1aWxkIHRoZSBkcml2ZXIgc3Vw
cG9ydGluZyB0aGU6DQo+ID4gKw0KPiA+ICsgICAgICAgICAgICAgICBNYXJ2ZWxsIFdpcmVsZXNz
IFdpLUZpIDg4Vzg4NjQgbW9kdWxlcw0KPiA+ICsgICAgICAgICAgICAgICBNYXJ2ZWxsIFdpcmVs
ZXNzIFdpLUZpIDg4Vzg4OTcgbW9kdWxlcw0KPiA+ICsNCj4gPiArICAgICAgICAgICAgICAgVGhp
cyBkcml2ZXIgdXNlcyB0aGUga2VybmVsJ3MgbWFjODAyMTEgc3Vic3lzdGVtLg0KPiA+ICsNCj4g
PiArICAgICAgICAgICAgICAgSWYgeW91IHdhbnQgdG8gY29tcGlsZSB0aGUgZHJpdmVyIGFzIGEg
bW9kdWxlICg9IGNvZGUNCj4gd2hpY2ggY2FuIGJlDQo+ID4gKyAgICAgICAgICAgICAgIGluc2Vy
dGVkIGluIGFuZCByZW1vdmVkIGZyb20gdGhlIHJ1bm5pbmcga2VybmVsDQo+IHdoZW5ldmVyIHlv
dSB3YW50KSwNCj4gPiArICAgICAgICAgICAgICAgc2F5IE0gaGVyZSBhbmQgcmVhZA0KPiA8Zmls
ZTpEb2N1bWVudGF0aW9uL2tidWlsZC9tb2R1bGVzLnR4dD4uICBUaGUNCj4gPiArICAgICAgICAg
ICAgICAgbW9kdWxlIHdpbGwgYmUgY2FsbGVkIG13bHdpZmkuDQo+ID4gKw0KPiA+ICsgICAgICAg
ICAgICAgICBOT1RFOiBTZWxlY3RpbmcgdGhpcyBkcml2ZXIgbWF5IGNhdXNlIGNvbmZsaWN0IHdp
dGgNCj4gTVdJRklFWCBkcml2ZXINCj4gPiArICAgICAgICAgICAgICAgdGhhdCBhbHNvIG9wZXJh
dGVzIG9uIHRoZSBzYW1lIHBhcnQgbnVtYmVyIDg4Vzg4OTcuDQo+IFVzZXJzIHNob3VsZA0KPiA+
ICsgICAgICAgICAgICAgICBzZWxlY3QgZWl0aGVyIE1XSUZJRVggb3IgTVdMV0lGSSwgbm90IGJv
dGguIE1XSUZJRVggaXMNCj4gZnVsbG1hYywNCj4gPiArICAgICAgICAgICAgICAgc3VwcG9ydGlu
ZyBtb3JlIGNvbXByZWhlbnNpdmUgY2xpZW50IGZ1bmN0aW9ucyBmb3INCj4gbGFwdG9wcy9lbWJl
ZGRlZA0KPiA+ICsgICAgICAgICAgICAgICBkZXZpY2VzLiBNV0xXSUZJIGlzIG1hYzgwMjExLWJh
c2VkIGZvciBmdWxsDQo+IEFQL1dpcmVsZXNzIEJyaWRnZS4NCj4gPiArDQo+ID4gZGlmZiAtLWdp
dCBhL2RyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvTUFJTlRBSU5FUlMNCj4gPiBiL2RyaXZl
cnMvbmV0L3dpcmVsZXNzL213bHdpZmkvTUFJTlRBSU5FUlMNCj4gPiBuZXcgZmlsZSBtb2RlIDEw
MDY0NA0KPiA+IGluZGV4IDAwMDAwMDAuLjU3MDZjZTkNCj4gPiAtLS0gL2Rldi9udWxsDQo+ID4g
KysrIGIvZHJpdmVycy9uZXQvd2lyZWxlc3MvbXdsd2lmaS9NQUlOVEFJTkVSUw0KPiA+IEBAIC0w
LDAgKzEsNSBAQA0KPiA+ICtNQVJWRUxMIE1XTFdJRkkgV0lSRUxFU1MgRFJJVkVSDQo+ID4gK006
ICAgICBEYXZpZCBMaW4gPGRsaW5AbWFydmVsbC5jb20+DQo+ID4gK0w6ICAgICBsaW51eC13aXJl
bGVzc0B2Z2VyLmtlcm5lbC5vcmcNCj4gPiArUzogICAgIE1haW50YWluZWQNCj4gPiArRjogICAg
IGRyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvDQo+IA0KPiBQbGVhc2UgYWRkIHRoaXMgdG8g
dGhlIHRvcCBkaXJlY3RvcnkgTUFJTlRBSU5FUlMgaW5zdGVhZCBvZiBhZGRpbmcgYSBzZXBhcmF0
ZQ0KPiBmaWxlICh1bmxlc3MgYWRkaW5nIHlvdXIgb3duIE1BSU5UQUlORVJTIGlzIHRoZSBuZXcg
dGhpbmcpLg0KPiANCg0KV2lsbCBtb2RpZnkgdG9wIE1BSU5UQUlORVIgZmlsZS4NCg0KPiANCj4g
UmVnYXJkcw0KPiBKb25hcw0KDQpUaGFua3MsDQpEYXZpZA0K
--
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
Jonas Gorski July 7, 2015, 9:38 a.m. UTC | #6
On Tue, Jul 7, 2015 at 3:47 AM, David Lin <dlin@marvell.com> wrote:
>>Jonas Gorski wrote:
>>
>> Hi,
>>
>> On Fri, Jul 3, 2015 at 8:10 AM, David Lin <dlin@marvell.com> wrote:

(snip)

>> > diff --git a/drivers/net/wireless/mwlwifi/Kconfig
>> > b/drivers/net/wireless/mwlwifi/Kconfig
>> > new file mode 100644
>> > index 0000000..3732223
>> > --- /dev/null
>> > +++ b/drivers/net/wireless/mwlwifi/Kconfig
>> > @@ -0,0 +1,24 @@
>> > +config MWLWIFI
>> > +       tristate "Marvell Wireless Wi-Fi driver (mwlwifi)"
>>
>> Do you also have wired Wi-Fi cards? ;P
>>
>> The description seems very generic despite only supporting two chips.
>> Currently we already have two other marvell drivers claiming to support
>> "marvell" wireless:
>>
>> config MWL8K
>>         tristate "Marvell 88W8xxx PCI/PCIe Wireless support"
>>
>> config MWIFIEX
>>         tristate "Marvell WiFi-Ex Driver"
>>
>> I think it would be good to have some more precise description here
>> (something like "Marvell 88W8864/8897 PCIe driver with AP support").
>>
>
> Will change the title from "Marvell Wireless Wi-Fi driver (mwlwifi)" to "Marvell 88W8864/88W8897 PCIe driver with AP support".

As Imre pointed out, mwifiex also supports AP mode, so this is not a
good description.


Jonas
--
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
Imre Kaloz July 7, 2015, 9:50 a.m. UTC | #7
On Tue, 07 Jul 2015 11:38:32 +0200, Jonas Gorski <jogo@openwrt.org> wrote:

<snip>

>>> I think it would be good to have some more precise description here
>>> (something like "Marvell 88W8864/8897 PCIe driver with AP support").
>>>
>>
>> Will change the title from "Marvell Wireless Wi-Fi driver (mwlwifi)" to  
>> "Marvell 88W8864/88W8897 PCIe driver with AP support".
>
> As Imre pointed out, mwifiex also supports AP mode, so this is not a
> good description.

On the short run, changing the description of the drivers could work, but  
I would suggest dropping (at least) 8897 support from mwifiex in favor of  
mwlwifi. If supporting the 8766 isn't complicated, I would drop PCIe  
support completely from there.

David, Chor Teck, could you shed some light on the future driver strategy?  
Does Marvell have any plans to add support for new chips to mwifiex, or do  
you plan to add usb support to mwlwifi later?


Cheers,

Imre
--
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
Chor Teck Law July 7, 2015, 1:48 p.m. UTC | #8
> -----Original Message-----

> From: Imre Kaloz [mailto:kaloz@openwrt.org]

> Sent: Tuesday, July 07, 2015 2:50 AM

> To: David Lin; Jonas Gorski

> Cc: Johannes Berg; linux-wireless@vger.kernel.org; Chor Teck Law; Pete

> Hsieh

> Subject: Re: [PATCH v5] Add new mac80211 driver mwlwifi.

> 

> On Tue, 07 Jul 2015 11:38:32 +0200, Jonas Gorski <jogo@openwrt.org>

> wrote:

> 

> <snip>

> 

> >>> I think it would be good to have some more precise description here

> >>> (something like "Marvell 88W8864/8897 PCIe driver with AP

> support").

> >>>

> >>

> >> Will change the title from "Marvell Wireless Wi-Fi driver (mwlwifi)"

> >> to "Marvell 88W8864/88W8897 PCIe driver with AP support".

> >

> > As Imre pointed out, mwifiex also supports AP mode, so this is not a

> > good description.


We have explained a few times on this... Pls read about the difference of mwifiex vs mwlwifi. There is a NOTE added to the description of KConfig of mwlwifi.

Would the community prefer this description instead: "Marvell 88W8864/88W8897 driver with MAC80211 support"?

> 

> On the short run, changing the description of the drivers could work,

> but I would suggest dropping (at least) 8897 support from mwifiex in

> favor of mwlwifi. If supporting the 8766 isn't complicated, I would

> drop PCIe support completely from there.

> 

> David, Chor Teck, could you shed some light on the future driver

> strategy?


The mwlwifi as it is today, only supports said PCIe devices. There is no near term plan for any other devices. But we cannot speak for future. Let's comment based on the current state.
Jonas Gorski July 7, 2015, 2:20 p.m. UTC | #9
On Tue, Jul 7, 2015 at 3:48 PM, Chor Teck Law <ctlaw@marvell.com> wrote:
>
>
>> -----Original Message-----
>> From: Imre Kaloz [mailto:kaloz@openwrt.org]
>> Sent: Tuesday, July 07, 2015 2:50 AM
>> To: David Lin; Jonas Gorski
>> Cc: Johannes Berg; linux-wireless@vger.kernel.org; Chor Teck Law; Pete
>> Hsieh
>> Subject: Re: [PATCH v5] Add new mac80211 driver mwlwifi.
>>
>> On Tue, 07 Jul 2015 11:38:32 +0200, Jonas Gorski <jogo@openwrt.org>
>> wrote:
>>
>> <snip>
>>
>> >>> I think it would be good to have some more precise description here
>> >>> (something like "Marvell 88W8864/8897 PCIe driver with AP
>> support").
>> >>>
>> >>
>> >> Will change the title from "Marvell Wireless Wi-Fi driver (mwlwifi)"
>> >> to "Marvell 88W8864/88W8897 PCIe driver with AP support".
>> >
>> > As Imre pointed out, mwifiex also supports AP mode, so this is not a
>> > good description.
>
> We have explained a few times on this... Pls read about the difference of mwifiex vs mwlwifi. There is a NOTE added to the description of KConfig of mwlwifi.
>
> Would the community prefer this description instead: "Marvell 88W8864/88W8897 driver with MAC80211 support"?

Let's keep the name as is and do a cleanup / naming discussion
seperate of that. This should not block the acceptance of the driver.


Jonas
--
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
Imre Kaloz July 7, 2015, 2:35 p.m. UTC | #10
On Tue, 07 Jul 2015 15:48:38 +0200, Chor Teck Law <ctlaw@marvell.com>  
wrote:

>> >> Will change the title from "Marvell Wireless Wi-Fi driver (mwlwifi)"
>> >> to "Marvell 88W8864/88W8897 PCIe driver with AP support".
>> >
>> > As Imre pointed out, mwifiex also supports AP mode, so this is not a
>> > good description.
>
> We have explained a few times on this... Pls read about the difference  
> of mwifiex vs mwlwifi. There is a NOTE added to the description of  
> KConfig of mwlwifi.
>
> Would the community prefer this description instead: "Marvell  
> 88W8864/88W8897 driver with MAC80211 support"?

I have a different suggestion, please read below.

>> On the short run, changing the description of the drivers could work,
>> but I would suggest dropping (at least) 8897 support from mwifiex in
>> favor of mwlwifi. If supporting the 8766 isn't complicated, I would
>> drop PCIe support completely from there.
>>
>> David, Chor Teck, could you shed some light on the future driver
>> strategy?
>
> The mwlwifi as it is today, only supports said PCIe devices. There is no  
> near term plan for any other devices. But we cannot speak for future.  
> Let's comment based on the current state.

Sure thing, what I was talking about is more like making the driver  
selection less confusing for the enduser.

What I would propose is something along the lines (I have to cheat a bit,  
I know):

mwlwifi: "Marvell Avastar (802.11ac) PCIe support"
mwifiex: "Marvell Avastar (802.11n) support"
mwl8k :  "Marvell TopDog (802.11n) support"



Imre

PS:

Cheating as in mwl8k supports the 8764 Avastar radio and mwifiex supports  
11ac, but well...
--
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
Chor Teck Law July 7, 2015, 2:56 p.m. UTC | #11
> -----Original Message-----

> From: Imre Kaloz [mailto:kaloz@openwrt.org]

> Sent: Tuesday, July 07, 2015 7:36 AM

> To: David Lin; Jonas Gorski; Chor Teck Law

> Cc: Johannes Berg; linux-wireless@vger.kernel.org; Pete Hsieh

> Subject: Re: [PATCH v5] Add new mac80211 driver mwlwifi.

> 

> On Tue, 07 Jul 2015 15:48:38 +0200, Chor Teck Law <ctlaw@marvell.com>

> wrote:

> 

> >> >> Will change the title from "Marvell Wireless Wi-Fi driver

> (mwlwifi)"

> >> >> to "Marvell 88W8864/88W8897 PCIe driver with AP support".

> >> >

> >> > As Imre pointed out, mwifiex also supports AP mode, so this is not

> >> > a good description.

> >

> > We have explained a few times on this... Pls read about the

> difference

> > of mwifiex vs mwlwifi. There is a NOTE added to the description of

> > KConfig of mwlwifi.

> >

> > Would the community prefer this description instead: "Marvell

> > 88W8864/88W8897 driver with MAC80211 support"?

> 

> I have a different suggestion, please read below.

> 

> >> On the short run, changing the description of the drivers could

> work,

> >> but I would suggest dropping (at least) 8897 support from mwifiex in

> >> favor of mwlwifi. If supporting the 8766 isn't complicated, I would

> >> drop PCIe support completely from there.

> >>

> >> David, Chor Teck, could you shed some light on the future driver

> >> strategy?

> >

> > The mwlwifi as it is today, only supports said PCIe devices. There is

> > no near term plan for any other devices. But we cannot speak for

> future.

> > Let's comment based on the current state.

> 

> Sure thing, what I was talking about is more like making the driver

> selection less confusing for the enduser.

> 

> What I would propose is something along the lines (I have to cheat a

> bit, I know):

> 

> mwlwifi: "Marvell Avastar (802.11ac) PCIe support"

> mwifiex: "Marvell Avastar (802.11n) support"

> mwl8k :  "Marvell TopDog (802.11n) support"


Thanks for scratching your head on this. We cannot change mwifiex because of its broader installation on embedded devices and laptops. And it is definitely more widely used as 11ac than 11n.

We can add Avastar if preferred (not required but ok). Main thing about mwlwifi is that it differs from others for its mac80211 compatibility. Should we use 
"Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211 compatible)"?

> 

> 

> Imre

> 

> PS:

> 

> Cheating as in mwl8k supports the 8764 Avastar radio and mwifiex

> supports 11ac, but well...
David Lin July 8, 2015, 12:14 a.m. UTC | #12
PiBKb2hhbm5lcyBCZXJnIHdyb3RlOg0KPiANCj4gPiArLyogQml0IGRlZmluaXRpbyBmb3IgTUFD
UkVHX1JFR19BMkhfSU5URVJSVVBUX0NBVVNFIChBMkhSSUMpICovDQo+IA0KPiB0eXBvDQo+DQoN
CldpbGwgY29ycmVjdCBpdC4NCg0KPiANCj4gPiArLyogQml0IGRlZmluaXRpbyBmb3IgTUFDUkVH
X1JFR19IMkFfSU5URVJSVVBUX0NBVVNFIChIMkFSSUMpICovDQo+IA0KPiBzYW1lIGhlcmUNCj4N
Cg0KV2lsbCBjb3JyZWN0IGl0Lg0KDQo+DQo+ID4gK3N0cnVjdCBtd2xfY2hpcF9pbmZvIHsNCj4g
PiArCWNoYXIgKnBhcnRfbmFtZTsNCj4gPiArCWNoYXIgKmZ3X2ltYWdlOw0KPiA+ICt9Ow0KPiAN
Cj4gY29uc3QgY2hhciAqPw0KPg0KDQpXaWxsIG1vZGlmeSBpdC4NCg0KPg0KPiA+ICtzdHJ1Y3Qg
bXdsX3R4X2huZGwgew0KPiA+ICsJc3RydWN0IHNrX2J1ZmYgKnBza19idWZmOw0KPiA+ICsJdTgg
KnN0YV9pbmZvOw0KPiANCj4gdTggKiBzZWVtcyB2ZXJ5IHN0cmFuZ2UgZm9yIHN0YV9pbmZvPw0K
Pg0KDQpXaWxsIGNoYW5nZSBpdCB0byAidm9pZCAqIi4NCg0KPiANCj4gPiArCXN0cnVjdCBtd2xf
dHhfZGVzYyAqcGRlc2M7DQo+ID4gKwlzdHJ1Y3QgbXdsX3R4X2huZGwgKnBuZXh0Ow0KPiA+ICt9
Ow0KPiANCj4gWW91IHByb2JhYmx5IHNob3VsZG4ndCBoYW5kLXdyaXRlIHlvdXIgb3duIGxpbmtl
ZCBsaXN0IGltcGxlbWVudGF0aW9uLi4uIFlvdQ0KPiBjb3VsZCBwb3NzaWJseSBldmVuIHB1dCBh
bGwgdGhpcyBpbnRvIHRoZSBza2ItPmNiIGFuZCBnZXQgcmlkIG9mIHRoZSBleHRyYSBzdHJ1Y3QN
Cj4gZW50aXJlbHksIGp1c3QgdXNpbmcgYW4gc2tiIGxpc3Q/DQo+IA0KPiA+ICtzdHJ1Y3QgbXds
X3J4X2huZGwgew0KPiA+ICsJc3RydWN0IHNrX2J1ZmYgKnBza19idWZmOyAgICAvKiBhc3NvY2lh
dGVkIHNrX2J1ZmYgZm9yIExpbnV4DQo+ICAgICAgKi8NCj4gPiArCXN0cnVjdCBtd2xfcnhfZGVz
YyAqcGRlc2M7DQo+ID4gKwlzdHJ1Y3QgbXdsX3J4X2huZGwgKnBuZXh0Ow0KPiA+ICt9Ow0KPiAN
Cj4gRGl0dG8gaGVyZS4NCj4gDQoNClRoZXNlIHR3byBzdHJ1Y3R1cmVzIGFyZSB1c2VkIHRvIGJp
bmRpbmcgc29ja2V0IGJ1ZmZlciBhbmQgZmlybXdhcmUgZGVzY3JpcHRvci4gSXQgc2hvdWxkIGJl
IGJldHRlciB0byBsZWF2ZSB0aGVzZSBmaWVsZHMgaGVyZSBpbnN0ZWFkIG9mIG1vdmluZyB0aGVt
IGludG8gc2tiLT5jYi4NCg0KPiANCj4gPiArCXN0cnVjdCBtd2xfdHhfcHdyX3RibCB0eF9wd3Jf
dGJsW1NZU0FEUFRfTUFYX05VTV9DSEFOTkVMU107DQo+ID4gKwl1MzIgY2RkOyAgICAgICAgICAg
ICAgICAgICAgIC8qIDA6IG9mZiwgMTogb24gKi8NCj4gDQo+IGJvb2w/DQo+DQoNCldpbGwgbW9k
aWZ5IGl0Lg0KDQo+IA0KPiA+ICsJc3BpbmxvY2tfdCBzdGFfbG9jazsgICAgICAgICAvKiBmb3Ig
cHJpdmF0ZSBzdGEgaW5mbw0KPiAgKi8NCj4gPiArCXN0cnVjdCBsaXN0X2hlYWQgc3RhX2xpc3Q7
ICAgLyogTGlzdCBvZiBzdGF0aW9ucw0KPiAgKi8NCj4gDQo+IENvdWxkIGNvbnNpZGVyIHVzaW5n
IHRoZSBtYWM4MDIxMSBzdGF0aW9uIGl0ZXJhdGlvbg0KPiBpZWVlODAyMTFfaXRlcmF0ZV9zdGF0
aW9uc19hdG9taWMoKS4NCj4NCg0KSXQgaXMgYmV0dGVyIHRvIGtlZXAgcHJpdmF0ZSBzdGF0aW9u
IGluZm9ybWF0aW9uIGxpc3QgaW4gbXdsd2lmaSBkcml2ZXIuDQoNCj4NCj4gPiArc3RydWN0IGJl
YWNvbl9pbmZvIHsNCj4gPiArCWJvb2wgdmFsaWQ7DQo+ID4gKwl1MTYgY2FwX2luZm87DQo+ID4g
Kwl1OCBiX3JhdGVfc2V0W1NZU0FEUFRfTUFYX0RBVEFfUkFURVNfR107DQo+ID4gKwl1OCBvcF9y
YXRlX3NldFtTWVNBRFBUX01BWF9EQVRBX1JBVEVTX0ddOw0KPiA+ICsJdTE2IGllX3dtbV9sZW47
ICAgICAgICAgICAgICAgLyogS2VlcCBXTU0gSUUgKi8NCj4gPiArCXU4ICppZV93bW1fcHRyOw0K
PiA+ICsJdTE2IGllX3Jzbl9sZW47ICAgICAgICAgICAgICAgLyogS2VlcCBXUEEgSUUgKi8NCj4g
PiArCXU4ICppZV9yc25fcHRyOw0KPiA+ICsJdTE2IGllX3JzbjQ4X2xlbjsgICAgICAgICAgICAg
LyogS2VlcCBXUEEyIElFICovDQo+ID4gKwl1OCAqaWVfcnNuNDhfcHRyOw0KPiA+ICsJdTE2IGll
X2h0X2xlbjsgICAgICAgICAgICAgICAgLyogS2VlcCBIVCBJRSAqLw0KPiA+ICsJdTggKmllX2h0
X3B0cjsNCj4gPiArCXU4IGllX2xpc3RfaHRbMTQ4XTsNCj4gPiArCXUxNiBpZV92aHRfbGVuOyAg
ICAgICAgICAgICAgIC8qIEtlZXAgVkhUIElFICovDQo+ID4gKwl1OCAqaWVfdmh0X3B0cjsNCj4g
PiArCXU4IGllX2xpc3Rfdmh0WzI0XTsNCj4gPiArfTsNCj4gDQo+IFRoYXQgc3RydWN0IGlzIHBh
Y2tlZCByZWFsbHkgaW5lZmZpY2llbnRseSB3aXRoIHBvaW50ZXJzIGFuZCB1MTZzIGludGVybGVh
dmVkLg0KPiBUaG9zZSB1MTZzIGNhbiBhbHNvIGJlIHU4cy4NCj4NCg0KV2lsbCByZWFycmFuZ2Ug
dGhlbS4NCg0KPiANCj4gPiArc3RydWN0IG13bF92aWYgew0KPiA+ICsJc3RydWN0IGxpc3RfaGVh
ZCBsaXN0Ow0KPiANCj4gQ291bGQgYWxzbyBjb25zaWRlciB1c2luZyBpdGVyYXRlX2FjdGl2ZV9p
bnRlcmZhY2VzKCkgYW5kIGZyaWVuZHMuDQo+DQoNCkl0IGlzIGJldHRlciB0byBrZWVwIHByaXZh
dGUgaW50ZXJmYWNlIGluZm9ybWF0aW9uIGxpc3QgaW4gbXdsd2lmaSBkcml2ZXIuDQoNCj4NCj4g
PiArCXN0cnVjdCBpZWVlODAyMTFfdmlmICp2aWY7DQo+IA0KPiBhbmQgeW91IHNob3VsZCBwcm9i
YWJseSBlbWJlZCB0aGUgc3RydWN0IGludG8gdGhlIHZpZi0+cHJpdiwgdGhlbiB5b3UgZG9uJ3QN
Cj4gbmVlZCBhIHZpZiBwb2ludGVyIGluIGl0ICh1c2UgY29udGFpbmVyX29mKQ0KPg0KDQpXaWxs
IG1vZGlmeSBpdC4NCg0KPiANCj4gPiArCXUxNiBzZXFubzsgICAgICAgLyogTm9uIEFNUERVIHNl
cXVlbmNlIG51bWJlciBhc3NpZ25lZCBieQ0KPiBkcml2ZXIuICAqLw0KPiANCj4gVGhhdCBzZWVt
cyBhIGJpdCBzdHJhbmdlIC0gd2h5IG5vdCBqdXN0IGxlYXZlIGl0IHVwIHRvIG1hYzgwMjExIHRo
ZW4/DQo+IA0KPiA+ICsJLyogSW5kaWNhdGUgaWYgdGhpcyBpcyBzdGF0aW9uIG1vZGUgKi8NCj4g
PiArCWJvb2wgaXNfc3RhOw0KPiANCj4gdXNlIHZpZi0+dHlwZSBpbnN0ZWFkDQo+DQoNCldpbGwg
bW9kaWZ5IGl0Lg0KDQo+IA0KPiA+ICtzdHJ1Y3QgbXdsX2Ftc2R1X2ZyYWcgew0KPiA+ICsJc3Ry
dWN0IHNrX2J1ZmYgKnNrYjsNCj4gPiArCXU4IHBhZDsNCj4gPiArCXU4ICpjdXJfcG9zOw0KPiA+
ICsJdTggbnVtOw0KPiA+ICsJdTMyIGppZmZpZXM7DQo+IA0KPiB1bnNpZ25lZCBsb25nDQo+IA0K
PiBhbHNvIHN0cnVjdCBvcmRlcmluZyBpcyByZWFsbHkgYWJvdXQgYXMgYmFkIGFzIGl0IGNvdWxk
IGJlIHdpdGggbG90cyBvZiBwYWRkaW5nDQo+IA0KDQpXaWxsIG1vZGlmeSBpdC4NCg0KPiA+ICtz
dHJ1Y3QgbXdsX3N0YSB7DQo+ID4gKwlzdHJ1Y3QgbGlzdF9oZWFkIGxpc3Q7DQo+ID4gKwlzdHJ1
Y3QgaWVlZTgwMjExX3N0YSAqc3RhOw0KPiANCj4gc2FtZSBoZXJlIC0gZW1iZWQgdGhlIHN0cnVj
dCBpbnRvIHN0YS0+cHJpdiBhbmQgZ2V0IHJpZCBvZiB0aGUgc3RhIHBvaW50ZXINCj4NCg0KV2ls
bCBtb2RpZnkgaXQuDQoNCj4gDQo+ID4gKy8qIFRyYW5zbWlzc2lvbiBpbmZvcm1hdGlvbiB0byB0
cmFuc21pdCBhIHNvY2tldCBidWZmZXIuICovIHN0cnVjdA0KPiA+ICttd2xfdHhfY3RybCB7DQo+
ID4gKwl2b2lkICpzdGE7DQo+ID4gKwl2b2lkICp2aWY7DQo+ID4gKwl2b2lkICprX2NvbmY7DQo+
IA0KPiB2b2lkIHBvaW50ZXJzIGRvbid0IHNlZW0gc28gZ3JlYXQNCj4gQnV0IHBlcmhhcHMgeW91
IGRvIHdhbnQgdGhlbSB0byBhdm9pZCBtaXN0YWtlcywgYnV0IHlvdSBzaG91bGQgYWRkIGENCj4g
Y29tbWVudCB0aGVuLg0KPiBJZiB5b3UncmUgbm90IHN1cmUgd2hhdCBtaXN0YWtlcyBJJ20gdGFs
a2luZyBhYm91dCAtIGNvbnNpZGVyIGFuIGludGVyZmFjZSBvcg0KPiBzdGF0aW9uIHRoYXQncyBy
ZW1vdmVkIHdoaWxlIHRoZSBwYWNrZXQgaXMgcGVuZGluZyBUWC4NCj4NCg0KV2hlbiBpbnRlcmZh
Y2Ugb3Igc3RhdGlvbiBpcyByZW1vdmVkLCByZWxhdGVkIHBlbmRpbmcgc29ja2V0IGJ1ZmZlcnMg
d2lsbCBiZSByZW1vdmVkLg0KDQo+DQo+ID4gK3N0YXRpYyBpbmxpbmUgc3RydWN0IG13bF92aWYg
Km13bF9kZXZfZ2V0X3ZpZihjb25zdCBzdHJ1Y3QNCj4gaWVlZTgwMjExX3ZpZiAqdmlmKQ0KPiA+
ICt7DQo+ID4gKwlyZXR1cm4gKHN0cnVjdCBtd2xfdmlmICopJnZpZi0+ZHJ2X3ByaXY7IH0NCj4g
PiArDQo+ID4gK3N0YXRpYyBpbmxpbmUgc3RydWN0IG13bF9zdGEgKm13bF9kZXZfZ2V0X3N0YShj
b25zdCBzdHJ1Y3QNCj4gaWVlZTgwMjExX3N0YSAqc3RhKQ0KPiA+ICt7DQo+ID4gKwlyZXR1cm4g
KHN0cnVjdCBtd2xfc3RhICopJnN0YS0+ZHJ2X3ByaXY7IH0NCj4gDQo+IE9oIHdhaXQsIHNvIHlv
dSAqZG8qIGVtYmVkIHRoZSBzdHJ1Y3RzIC0geW91IGNhbiB1c2UgY29udGFpbmVyX29mKCkgdG8g
Z28gdGhlDQo+IG90aGVyIHdheSB0aGVuIGFuZCBnZXQgcmlkIG9mIHRoZSBwb2ludGVycy4NCj4g
DQoNCldpbGwgbW9kaWZ5IGl0Lg0KDQo+DQo+ID4gK3N0YXRpYyBpbnQgbXdsX2Z3Y21kX3dhaXRf
Y29tcGxldGUoc3RydWN0IG13bF9wcml2ICpwcml2LCB1bnNpZ25lZA0KPiBzaG9ydCBjbWQpDQo+
ID4gK3sNCj4gPiArCXVuc2lnbmVkIGludCBjdXJyX2l0ZXJhdGlvbiA9IE1BWF9XQUlUX0ZXX0NP
TVBMRVRFX0lURVJBVElPTlM7DQo+ID4gKw0KPiA+ICsJdW5zaWduZWQgc2hvcnQgaW50X2NvZGUg
PSAwOw0KPiA+ICsNCj4gPiArCWRvIHsNCj4gPiArCQlpbnRfY29kZSA9IGxlMTZfdG9fY3B1KCoo
KF9fbGUxNiAqKSZwcml2DQo+IC0+cGNtZF9idWZbMF0pKTsNCj4gPiArCQltZGVsYXkoMSk7DQo+
ID4gKwl9IHdoaWxlICgoaW50X2NvZGUgIT0gY21kKSAmJiAoLS1jdXJyX2l0ZXJhdGlvbikpOw0K
PiA+ICsNCj4gPiArCWlmIChjdXJyX2l0ZXJhdGlvbiA9PSAwKSB7DQo+ID4gKwkJd2lwaHlfZXJy
KHByaXYtPmh3LT53aXBoeSwgImNtZCAweCUwNHg9JXMgdGltZWQNCj4gb3V0XG4iLA0KPiA+ICsJ
CQkgIGNtZCwgbXdsX2Z3Y21kX2dldF9jbWRfc3RyaW5nKGNtZCkpOw0KPiA+ICsJCXJldHVybiAt
RUlPOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCW1kZWxheSgzKTsNCj4gDQo+IEJvdGggbWRlbGF5
KDEpIGFuZCBtZGVsYXkoMykgYXJlIEVYVFJFTUVMWSBsb25nIGZvciBhdG9taWMgY29udGV4dCwg
dGhpcyBpcw0KPiBtYWRlIG11Y2ggd29yc2UgYnkgdGhlIGl0ZXJhdGlvbiBjb3VudGVyIHN0YXJ0
aW5nIGF0IDEwMDAwLCB3aGljaCBtZWFucw0KPiB0aGF0IHRoaXMgZnVuY3Rpb24gY2FuLCBpbiB0
aGUgZmFpbHVyZSBjYXNlLCBibG9jayB0aGUgc3lzdGVtIGZvciAxMCBzZWNvbmRzIQ0KPiBUaGF0
IHNlZW1zIGV4Y2Vzc2l2ZSwgcGFydGljdWxhcmx5IHdoZW4gaXQncyBjYWxsZWQgZnJvbSBhdG9t
aWMgY29udGV4dC4NCj4gDQo+IERvIHlvdSByZWFsbHkgbmVlZCB0aGF0IGJ0dz8gbWlnaHQgYmUg
ZmFyIGJldHRlciB0byBzbGVlcCBmb3Igc29tZQ0KPiBldmVudC9pbnRlcnJ1cHQgaGVyZS4gQWxt
b3N0IGFsbCBtYWM4MDIxMSBBUEkgZnVuY3Rpb25zIGNhbiBzbGVlcCwgYW5kIHlvdQ0KPiBwcm9i
YWJseSAoaG9wZWZ1bGx5KSBhcmVuJ3QgZ2V0dGluZyBoZXJlIGZvciBUWC9SWC4NCj4gDQoNCkkg
dGhpbmsgdGhleSBhcmUgbmVlZGVkLCB0aG91Z2ggaXQgc2hvdWxkIGdvIHRocm91Z2ggdmVyeSBm
YXN0IGFuZCBuZXZlciBmYWlsIG9uIGEgaGVhbHRoeSBzeXN0ZW0uIFRoZSBudW1iZXIgb2YgaXRl
cmF0aW9ucyBkZXBlbmQgb24gaHcgdHlwZS9pbnRlcmZhY2Ugc3BlZWQuIFdlIGNhbiByZWR1Y2Ug
dGhlIG1heCBpdGVyYXRpb25zIGJhc2VkIG9uIHN5c3RlbXMgdGhhdCB3ZSBoYXZlIHRyaWVkIGFu
ZCBnbyBmcm9tIHRoZXJlLg0KDQo+IA0KPiA+ICtzdGF0aWMgaW50IG13bF9md2NtZF9nZXRfdHhf
cG93ZXJzKHN0cnVjdCBtd2xfcHJpdiAqcHJpdiwgdTE2DQo+ICpwb3dsaXN0LCB1MTYgY2gsDQo+
ID4gKwkJCQkgICB1MTYgYmFuZCwgdTE2IHdpZHRoLCB1MTYgc3ViX2NoKSB7DQo+ID4gKwlzdHJ1
Y3QgaG9zdGNtZF9jbWRfODAyXzExX3R4X3Bvd2VyICpwY21kOw0KPiA+ICsJaW50IGk7DQo+ID4g
Kw0KPiA+ICsJcGNtZCA9IChzdHJ1Y3QgaG9zdGNtZF9jbWRfODAyXzExX3R4X3Bvd2VyICopJnBy
aXYNCj4gLT5wY21kX2J1ZlswXTsNCj4gPiArDQo+ID4gKwlzcGluX2xvY2soJnByaXYtPmZ3Y21k
X2xvY2spOw0KPiANCj4gTWF5YmUgdGhlbiB0aGF0IGNvdWxkIGFsc28gYmUgYSBtdXRleCBpbnN0
ZWFkLg0KPg0KDQpXaWxsIG5vdCBjaGFuZ2UgaXQuDQoNCj4gDQo+ID4gK3N0YXRpYyB1OCBtd2xf
ZndjbWRfZ2V0XzgwbV9wcmlfY2hubF9vZmZzZXQodTggY2hhbm5lbCkgew0KPiA+ICsJdTggYWN0
X3ByaW1hcnkgPSBBQ1RfUFJJTUFSWV9DSEFOXzA7DQo+ID4gKw0KPiA+ICsJc3dpdGNoIChjaGFu
bmVsKSB7DQo+ID4gKwljYXNlIDM2Og0KPiA+ICsJCWFjdF9wcmltYXJ5ID0gQUNUX1BSSU1BUllf
Q0hBTl8wOw0KPiA+ICsJCWJyZWFrOw0KPiANCj4gVGhpcyBpcyBraW5kYSBtZXNzZWQgdXAgLSB3
aHkgYXJlbid0IHlvdSB1c2luZyBzb21ldGhpbmcgbGlrZSBjaGFubmVsDQo+IC0+aHdfY2hhbm5l
bCBvciB3aGF0ZXZlciBpdCdzIGNhbGxlZD8NCj4NCg0KUGxlYXNlIGNoZWNrIG13bF9md2NtZF9z
ZXRfcmZfY2hhbm5lbCgpLCB0aGUgbXdsX2Z3Y21kX2dldF84MG1fcHJpX2Nobmxfb2Zmc2V0KCkg
aXMgb25seSB1c2VkIHRvIGNvbnZlcnQgaHdfdmFsdWUgdG8gY29uc3RhbnRzIHVzZWQgYnkgRlcg
Y29tbWFuZC4NCg0KPg0KPiA+ICtzdGF0aWMgaW50IG13bF9md2NtZF9zZXRfaWVzKHN0cnVjdCBt
d2xfcHJpdiAqcHJpdiwgc3RydWN0IG13bF92aWYNCj4gKm13bF92aWYpDQo+ID4gK3sNCj4gPiAr
CXN0cnVjdCBob3N0Y21kX2NtZF9zZXRfaWVzICpwY21kOw0KPiA+ICsNCj4gPiArCWlmICghbXds
X3ZpZi0+YmVhY29uX2luZm8udmFsaWQpDQo+ID4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4gKw0K
PiA+ICsJaWYgKG13bF92aWYtPmJlYWNvbl9pbmZvLmllX2h0X2xlbiA+IHNpemVvZihwY21kLT5p
ZV9saXN0X2h0KSkNCj4gPiArCQlnb3RvIGVpbnZhbDsNCj4gPiArDQo+ID4gKwlpZiAobXdsX3Zp
Zi0+YmVhY29uX2luZm8uaWVfdmh0X2xlbiA+IHNpemVvZihwY21kDQo+IC0+aWVfbGlzdF92aHQp
KQ0KPiA+ICsJCWdvdG8gZWludmFsOw0KPiA+ICsNCj4gPiArCXBjbWQgPSAoc3RydWN0IGhvc3Rj
bWRfY21kX3NldF9pZXMgKikmcHJpdi0+cGNtZF9idWZbMF07DQo+ID4gKw0KPiA+ICsJc3Bpbl9s
b2NrKCZwcml2LT5md2NtZF9sb2NrKTsNCj4gPiArDQo+ID4gKwltZW1zZXQocGNtZCwgMHgwMCwg
c2l6ZW9mKCpwY21kKSk7DQo+ID4gKwlwY21kLT5jbWRfaGRyLmNtZCA9IGNwdV90b19sZTE2KEhP
U1RDTURfQ01EX1NFVF9JRVMpOw0KPiA+ICsJcGNtZC0+Y21kX2hkci5sZW4gPSBjcHVfdG9fbGUx
NihzaXplb2YoKnBjbWQpKTsNCj4gPiArCXBjbWQtPmNtZF9oZHIubWFjaWQgPSBtd2xfdmlmLT5t
YWNpZDsNCj4gPiArDQo+ID4gKwlwY21kLT5hY3Rpb24gPSBjcHVfdG9fbGUxNihIT1NUQ01EX0FD
VF9HRU5fU0VUKTsNCj4gPiArDQo+ID4gKwlwY21kLT5pZV9saXN0X2xlbl9odCA9IGNwdV90b19s
ZTE2KG13bF92aWYNCj4gLT5iZWFjb25faW5mby5pZV9odF9sZW4pOw0KPiA+ICsJbWVtY3B5KHBj
bWQtPmllX2xpc3RfaHQsIG13bF92aWYtPmJlYWNvbl9pbmZvLmllX2h0X3B0ciwNCj4gPiArCSAg
ICAgICBtd2xfdmlmLT5iZWFjb25faW5mby5pZV9odF9sZW4pOw0KPiA+ICsNCj4gPiArCXBjbWQt
PmllX2xpc3RfbGVuX3ZodCA9IGNwdV90b19sZTE2KG13bF92aWYNCj4gLT5iZWFjb25faW5mby5p
ZV92aHRfbGVuKTsNCj4gPiArCW1lbWNweShwY21kLT5pZV9saXN0X3ZodCwgbXdsX3ZpZi0+YmVh
Y29uX2luZm8uaWVfdmh0X3B0ciwNCj4gPiArCSAgICAgICBtd2xfdmlmLT5iZWFjb25faW5mby5p
ZV92aHRfbGVuKTsNCj4gPiArDQo+ID4gKwlpZiAocHJpdi0+Y2hpcF90eXBlID09IE1XTDg4OTcp
IHsNCj4gPiArCQlwY21kLT5pZV9saXN0X2xlbl9wcm9wcmlldGFyeSA9DQo+ID4gKwkJCWNwdV90
b19sZTE2KG13bF92aWYtPmJlYWNvbl9pbmZvLmllX3dtbV9sZW4pOw0KPiA+ICsJCW1lbWNweShw
Y21kLT5pZV9saXN0X3Byb3ByaWV0YXJ5LA0KPiA+ICsJCSAgICAgICBtd2xfdmlmLT5iZWFjb25f
aW5mby5pZV93bW1fcHRyLA0KPiA+ICsJCSAgICAgICBtd2xfdmlmLT5iZWFjb25faW5mby5pZV93
bW1fbGVuKTsNCj4gPiArCX0NCj4gDQo+IFdoeSB3b3VsZCB0aGUgZmlybXdhcmUgZXZlbiBjYXJl
IGFib3V0IHRoZSBzcGxpdCBpbnRvIHRoZSB2YXJpb3VzIElFcz8NCj4gDQo+IFlvdSdyZSBwb3Rl
bnRpYWxseSBsb3NpbmcgaW5mb3JtYXRpb24gaGVyZSBieSB0cmVhdGluZyAqb25seSogdGhlIFdN
TSBJRSBhcw0KPiB0aGUgInJlbWFpbmluZyIgLSB5b3UgcmVhbGx5IHNob3VsZCB0cnkgaGFyZGVy
IHRvIHByZXNlcnZlIHRoZSBJRXMgY29taW5nIGZyb20NCj4gaG9zdGFwZCBjb21wbGV0ZWx5Lg0K
Pg0KDQpUaGlzIGlzIHRoZSBpbnRlcmZhY2Ugd2l0aCBvdXIgZmlybXdhcmUuDQoNCj4gDQo+ID4g
K3ZvaWQgbXdsX2Z3Y21kX2ludF9kaXNhYmxlKHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3KSB7DQo+
ID4gKwlzdHJ1Y3QgbXdsX3ByaXYgKnByaXY7DQo+ID4gKw0KPiA+ICsJcHJpdiA9IGh3LT5wcml2
Ow0KPiANCj4gWW91IGNhbiByb2xsIHRoYXQgaW50byBvbmUgbGluZSBidHcsIGluIG1hbnkgbWFu
eSBwbGFjZXMsIHRvIHJlZHVjZSB0aGUgbGVuZ3RoDQo+IG9mIHlvdXIgY29kZS4NCj4NCg0KV2ls
bCBtb2RpZnkgaXQuDQoNCj4gDQo+ID4gKyNpZiBTWVNBRFBUX05VTV9PRl9ERVNDX0RBVEEgPiAz
DQo+IA0KPiBJdCBkb2Vzbid0IHNlZW0gcmlnaHQgZm9yIHRoaXMgdG8gYmUgYSBjb21waWxlLXRp
bWUgZmxhZz8gSWYgaXQncyBuZWVkZWQgZm9yDQo+IGRpZmZlcmVudCBkZXZpY2VzIHRoZW4gaXQg
c2hvdWxkIGJlIGEgcnVuLXRpbWUgY2hvaWNlLCBpZiBpdCdzIGp1c3QgYSBjb25zdGFudCB3aHkN
Cj4gYm90aGVyIHdpdGggdGhlICNpZiwgYW5kIGlmIGl0J3Mgc29tZSBzb3J0IG9mIGNvbXBpbGUt
dGltZSBvcHRpb24gdGhlbiBzaG91bGRuJ3QNCj4gaXQgYmUgYSBDT05GSUdfKj8NCj4gDQoNCldp
bGwgcmVtb3ZlIGl0Lg0KDQo+IA0KPiA+ICtpbnQgbXdsX2Z3Y21kX3NldF9od19zcGVjcyhzdHJ1
Y3QgaWVlZTgwMjExX2h3ICpodykgew0KPiA+ICsJc3RydWN0IG13bF9wcml2ICpwcml2Ow0KPiA+
ICsJc3RydWN0IGhvc3RjbWRfY21kX3NldF9od19zcGVjICpwY21kOw0KPiA+ICsJaW50IGk7DQo+
ID4gKw0KPiA+ICsJcHJpdiA9IGh3LT5wcml2Ow0KPiA+ICsNCj4gPiArCXBjbWQgPSAoc3RydWN0
IGhvc3RjbWRfY21kX3NldF9od19zcGVjICopJnByaXYtPnBjbWRfYnVmWzBdOw0KPiA+ICsNCj4g
PiArCXNwaW5fbG9jaygmcHJpdi0+ZndjbWRfbG9jayk7DQo+ID4gKw0KPiA+ICsJbWVtc2V0KHBj
bWQsIDB4MDAsIHNpemVvZigqcGNtZCkpOw0KPiA+ICsJcGNtZC0+Y21kX2hkci5jbWQgPSBjcHVf
dG9fbGUxNihIT1NUQ01EX0NNRF9TRVRfSFdfU1BFQyk7DQo+ID4gKwlwY21kLT5jbWRfaGRyLmxl
biA9IGNwdV90b19sZTE2KHNpemVvZigqcGNtZCkpOw0KPiA+ICsJcGNtZC0+d2NiX2Jhc2VbMF0g
PSBjcHVfdG9fbGUzMihwcml2DQo+IC0+ZGVzY19kYXRhWzBdLnBwaHlzX3R4X3JpbmcpOw0KPiA+
ICsjaWYgU1lTQURQVF9OVU1fT0ZfREVTQ19EQVRBID4gMw0KPiA+ICsJZm9yIChpID0gMTsgaSA8
IFNZU0FEUFRfVE9UQUxfVFhfUVVFVUVTOyBpKyspDQo+ID4gKwkJcGNtZC0+d2NiX2Jhc2VbaV0g
PQ0KPiA+ICsJCQljcHVfdG9fbGUzMihwcml2LT5kZXNjX2RhdGFbaV0ucHBoeXNfdHhfcmluZyk7
DQo+ID4gKyNlbmRpZg0KPiANCj4gQWxzbyBoZXJlLCB3aXRoIHRoZSBhZGRpdGlvbmFsIHdyaW5r
bGUgdGhhdCB5b3UnbGwgZ2V0IGEgY29tcGlsZXIgd2FybmluZw0KPiAodW51c2VkIHZhcmlhYmxl
ICdpJykgd2hlbiBpdCdzIDw9IDMuDQo+DQoNCldpbGwgcmVtb3ZlIGl0Lg0KDQo+IA0KPiA+ICtz
dGF0aWMgaW50IG13bF9tYWM4MDIxMV9zdGFydChzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodykgew0K
PiA+ICsJc3RydWN0IG13bF9wcml2ICpwcml2Ow0KPiA+ICsJaW50IHJjOw0KPiA+ICsNCj4gPiAr
CXByaXYgPSBody0+cHJpdjsNCj4gPiArDQo+ID4gKwlyYyA9IHJlcXVlc3RfaXJxKHByaXYtPnBk
ZXYtPmlycSwgbXdsX2lzciwNCj4gPiArCQkJIElSUUZfU0hBUkVELCBNV0xfRFJWX05BTUUsIGh3
KTsNCj4gPiArCWlmIChyYykgew0KPiA+ICsJCXByaXYtPmlycSA9IC0xOw0KPiA+ICsJCXdpcGh5
X2Vycihody0+d2lwaHksICJmYWlsIHRvIHJlZ2lzdGVyIElSUQ0KPiBoYW5kbGVyXG4iKTsNCj4g
PiArCQlyZXR1cm4gcmM7DQo+ID4gKwl9DQo+ID4gKwlwcml2LT5pcnEgPSBwcml2LT5wZGV2LT5p
cnE7DQo+IA0KPiBXaHkgZG8geW91IGRvIHRoaXMgZXZlcnkgdGltZSB5b3UgYnJpbmcgdXAgdGhl
IGRldmljZT8gSXQncyBzbWFydGVyIHRvIGRvIHRoaXMNCj4gb25jZSBmb3IgcmVnaXN0cmF0aW9u
Lg0KPg0KDQpXaWxsIG1vZGlmeSBpdC4NCg0KPiANCj4gPiArCS8qIEVuYWJsZSBUWCByZWNsYWlt
IGFuZCBSWCB0YXNrbGV0cy4gKi8NCj4gPiArCXRhc2tsZXRfZW5hYmxlKCZwcml2LT50eF90YXNr
KTsNCj4gPiArCXRhc2tsZXRfZW5hYmxlKCZwcml2LT5yeF90YXNrKTsNCj4gPiArDQo+ID4gKwkv
KiBFbmFibGUgaW50ZXJydXB0cyAqLw0KPiA+ICsJbXdsX2Z3Y21kX2ludF9lbmFibGUoaHcpOw0K
PiANCj4gT2J2aW91c2x5IHlvdSBkb24ndCBoYXZlIHRvICplbmFibGUqIGludGVycnVwdHMsIHVu
dGlsIGhlcmUsIGJ1dCByZSAtcmVnaXN0ZXJpbmcNCj4gYWxsIHRoZSB0aW1lIHNlZW1zIHN0cmFu
Z2UuDQo+IA0KPiA+ICsJLyogRGlzYWJsZSBUWCByZWNsYWltIGFuZCBSWCB0YXNrbGV0cy4gKi8N
Cj4gPiArCXRhc2tsZXRfZGlzYWJsZSgmcHJpdi0+dHhfdGFzayk7DQo+ID4gKwl0YXNrbGV0X2Rp
c2FibGUoJnByaXYtPnJ4X3Rhc2spOw0KPiA+ICsNCj4gPiArCS8qIFJldHVybiBhbGwgc2ticyB0
byBtYWM4MDIxMSAqLw0KPiA+ICsJbXdsX3R4X2RvbmUoKHVuc2lnbmVkIGxvbmcpaHcpOw0KPiAN
Cj4gRXJyLCB3aHkgd291bGQgeW91IGhhdmUgYW4gaW50ZXJuYWwgZnVuY3Rpb24gdGhhdCB0YWtl
cyBhbiAndW5zaWduZWQgbG9uZycNCj4gaW5zdGVhZCBvZiBhIHByb3Blcmx5IHR5cGVkIHBvaW50
ZXI/Pw0KPg0KDQp0YXNrbGV0X2luaXQoJnByaXYtPnR4X3Rhc2ssICh2b2lkICopbXdsX3R4X2Rv
bmUsICh1bnNpZ25lZCBsb25nKWh3KTsgPT4gbXdsX3R4X2RvbmUoKSBpcyBhbHNvIHVzZWQgaGVy
ZS4NCg0KPiANCj4gPiArCWlmICghbWFjaWQtLSkgew0KPiANCj4gVGhhdCdzIC4uLiBhd2t3YXJk
LiBCZXR0ZXIgcmV3cml0ZSBpdCB0byBjaGVjayBmaXJzdCBhbmQgdGhlbiBkZWNyZW1lbnQgbGF0
ZXIuDQo+DQoNCldpbGwgbW9kaWZ5IGl0Lg0KDQo+IA0KPiA+ICsJaWYgKHZpZi0+dHlwZSA9PSBO
TDgwMjExX0lGVFlQRV9TVEFUSU9OKSB7DQo+ID4gKwkJZXRoZXJfYWRkcl9jb3B5KG13bF92aWYt
PnN0YV9tYWMsIHZpZi0+YWRkcik7DQo+ID4gKwkJbXdsX3ZpZi0+aXNfc3RhID0gdHJ1ZTsNCj4g
PiArCQltd2xfZndjbWRfYnNzX3N0YXJ0KGh3LCB2aWYsIHRydWUpOw0KPiA+ICsJCW13bF9md2Nt
ZF9zZXRfaW5mcmFfbW9kZShodywgdmlmKTsNCj4gPiArCQltd2xfZndjbWRfc2V0X21hY19hZGRy
X2NsaWVudChodywgdmlmLCB2aWYtPmFkZHIpOw0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCWlmICh2
aWYtPnR5cGUgPT0gTkw4MDIxMV9JRlRZUEVfQVApIHsNCj4gPiArCQlldGhlcl9hZGRyX2NvcHko
bXdsX3ZpZi0+YnNzaWQsIHZpZi0+YWRkcik7DQo+ID4gKwkJbXdsX2Z3Y21kX3NldF9uZXdfc3Ru
X2FkZF9zZWxmKGh3LCB2aWYpOw0KPiA+ICsJfQ0KPiANCj4gc3dpdGNoPw0KPiANCj4gPiArCWlm
ICh2aWYtPnR5cGUgPT0gTkw4MDIxMV9JRlRZUEVfU1RBVElPTikNCj4gPiArCQltd2xfZndjbWRf
cmVtb3ZlX21hY19hZGRyKGh3LCB2aWYsIHZpZi0+YWRkcik7DQo+ID4gKw0KPiA+ICsJaWYgKHZp
Zi0+dHlwZSA9PSBOTDgwMjExX0lGVFlQRV9BUCkNCj4gPiArCQltd2xfZndjbWRfc2V0X25ld19z
dG5fZGVsKGh3LCB2aWYsIHZpZi0+YWRkcik7DQo+IA0KPiBkaXR0bw0KPiANCj4gPiArCXdpcGh5
X2RlYnVnKGh3LT53aXBoeSwgImludGVyZmFjZTogJWQsIGNoYW5nZTogMHgleFxuIiwNCj4gPiAr
CQkgICAgdmlmLT50eXBlLCBjaGFuZ2VkKTsNCj4gPiArDQo+ID4gKwlpZiAodmlmLT50eXBlID09
IE5MODAyMTFfSUZUWVBFX1NUQVRJT04pDQo+ID4gKwkJbXdsX21hYzgwMjExX2Jzc19pbmZvX2No
YW5nZWRfc3RhKGh3LCB2aWYsIGluZm8sDQo+IGNoYW5nZWQpOw0KPiA+ICsNCj4gPiArCWlmICh2
aWYtPnR5cGUgPT0gTkw4MDIxMV9JRlRZUEVfQVApDQo+ID4gKwkJbXdsX21hYzgwMjExX2Jzc19p
bmZvX2NoYW5nZWRfYXAoaHcsIHZpZiwgaW5mbywNCj4gY2hhbmdlZCk7DQo+IA0KPiBkaXR0bw0K
Pg0KDQpXaWxsIG1vZGlmeSBpdC4NCg0KPiANCj4gPiArc3RhdGljIGludCBtd2xfbWFjODAyMTFf
YW1wZHVfYWN0aW9uKHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3LA0KPiA+ICsJCQkJICAgICBzdHJ1
Y3QgaWVlZTgwMjExX3ZpZiAqdmlmLA0KPiA+ICsJCQkJICAgICBlbnVtIGllZWU4MDIxMV9hbXBk
dV9tbG1lX2FjdGlvbg0KPiBhY3Rpb24sDQo+ID4gKwkJCQkgICAgIHN0cnVjdCBpZWVlODAyMTFf
c3RhICpzdGEsDQo+ID4gKwkJCQkgICAgIHUxNiB0aWQsIHUxNiAqc3NuLCB1OCBidWZfc2l6ZSkg
ew0KPiBbLi4uXQ0KPiA+ICsJc3Bpbl9sb2NrKCZwcml2LT5zdHJlYW1fbG9jayk7DQo+IFsuLi5d
DQo+ID4gKwkJCXN0cmVhbSA9IG13bF9md2NtZF9hZGRfc3RyZWFtKGh3LCBzdGEsIHRpZCk7DQo+
IA0KPiBUaGlzIGlzIGFib3V0IHRoZSBvbmx5IHBsYWNlIHRoYXQgYWN0dWFsbHkgc2VuZHMgYSBj
b21tYW5kIHdoaWxlIGl0IGNhbid0IHNsZWVwLg0KPiBJdCBzZWVtcyB5b3Ugc2hvdWxkIGJlIGFi
bGUgdG8gcmVzdHJ1Y3R1cmUgdGhpcyBmdW5jdGlvbiB0byBhdm9pZCB0aGF0LCBzbyB0aGF0DQo+
IHlvdSBkb24ndCBoYXZlIHRvIHVzZSBhIHNwaW5sb2NrIGZvciBjb21tYW5kIHNlbmRpbmcgYW5k
IGRvbid0IG5lZWQgYWxsIHRoYXQNCj4gYnVzeS1wb2xsaW5nIGJ1dCBjYW4gcHJvcGVybHkgZ28g
dG8gc2xlZXAgd2hpbGUgd2FpdGluZyBmb3IgdGhlIGZpcm13YXJlIHRvDQo+IHJlc3BvbmQgdG8g
YSBjb21tYW5kLg0KPg0KPiANCj4gPiArY29uc3Qgc3RydWN0IGllZWU4MDIxMV9vcHMgbXdsX21h
YzgwMjExX29wcyA9IHsNCj4gPiArCS50eCAgICAgICAgICAgICAgICAgPSBtd2xfbWFjODAyMTFf
dHgsDQo+ID4gKwkuc3RhcnQgICAgICAgICAgICAgID0gbXdsX21hYzgwMjExX3N0YXJ0LA0KPiA+
ICsJLnN0b3AgICAgICAgICAgICAgICA9IG13bF9tYWM4MDIxMV9zdG9wLA0KPiA+ICsJLmFkZF9p
bnRlcmZhY2UgICAgICA9IG13bF9tYWM4MDIxMV9hZGRfaW50ZXJmYWNlLA0KPiA+ICsJLnJlbW92
ZV9pbnRlcmZhY2UgICA9IG13bF9tYWM4MDIxMV9yZW1vdmVfaW50ZXJmYWNlLA0KPiA+ICsJLmNv
bmZpZyAgICAgICAgICAgICA9IG13bF9tYWM4MDIxMV9jb25maWcsDQo+ID4gKwkuYnNzX2luZm9f
Y2hhbmdlZCAgID0gbXdsX21hYzgwMjExX2Jzc19pbmZvX2NoYW5nZWQsDQo+ID4gKwkuY29uZmln
dXJlX2ZpbHRlciAgID0gbXdsX21hYzgwMjExX2NvbmZpZ3VyZV9maWx0ZXIsDQo+ID4gKwkuc2V0
X2tleSAgICAgICAgICAgID0gbXdsX21hYzgwMjExX3NldF9rZXksDQo+ID4gKwkuc2V0X3J0c190
aHJlc2hvbGQgID0gbXdsX21hYzgwMjExX3NldF9ydHNfdGhyZXNob2xkLA0KPiA+ICsJLnN0YV9h
ZGQgICAgICAgICAgICA9IG13bF9tYWM4MDIxMV9zdGFfYWRkLA0KPiA+ICsJLnN0YV9yZW1vdmUg
ICAgICAgICA9IG13bF9tYWM4MDIxMV9zdGFfcmVtb3ZlLA0KPiA+ICsJLmNvbmZfdHggICAgICAg
ICAgICA9IG13bF9tYWM4MDIxMV9jb25mX3R4LA0KPiA+ICsJLmdldF9zdGF0cyAgICAgICAgICA9
IG13bF9tYWM4MDIxMV9nZXRfc3RhdHMsDQo+ID4gKwkuZ2V0X3N1cnZleSAgICAgICAgID0gbXds
X21hYzgwMjExX2dldF9zdXJ2ZXksDQo+ID4gKwkuYW1wZHVfYWN0aW9uICAgICAgID0gbXdsX21h
YzgwMjExX2FtcGR1X2FjdGlvbiwNCj4gPiArfTsNCj4gDQo+IEFsbCB0aGUgY2FsbGJhY2tzIHRo
YXQgeW91IHVzZSBjYW4gc2xlZXAgKGFwYXJ0IGZyb20gVFggd2hlcmUgeW91IGRvbid0IHNlbmQN
Cj4gZmlybXdhcmUgY29tbWFuZHMpLCBzbyByZWFsbHkgYWxsIHlvdSBoYXZlIHRvIGRvIGlzIHJl
c3RydWN0dXJlIHRoZQ0KPiBhZ2dyZWdhdGlvbiBjb2RlIHNsaWdodGx5Lg0KPg0KPiA+IGRpZmYg
LS1naXQgYS9kcml2ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL21hYzgwMjExLmgNCj4gYi9kcml2
ZXJzL25ldC93aXJlbGVzcy9td2x3aWZpL21hYzgwMjExLmgNCj4gPiBuZXcgZmlsZSBtb2RlIDEw
MDY0NA0KPiA+IGluZGV4IDAwMDAwMDAuLjAyNjdkZDg1DQo+ID4gLS0tIC9kZXYvbnVsbA0KPiA+
ICsrKyBiL2RyaXZlcnMvbmV0L3dpcmVsZXNzL213bHdpZmkvbWFjODAyMTEuaA0KPiA+IEBAIC0w
LDAgKzEsMjUgQEANCj4gPiArLyoNCj4gPiArICogQ29weXJpZ2h0IChDKSAyMDA2LTIwMTUsIE1h
cnZlbGwgSW50ZXJuYXRpb25hbCBMdGQuDQo+ID4gKyAqDQo+ID4gKyAqIFRoaXMgc29mdHdhcmUg
ZmlsZSAodGhlICJGaWxlIikgaXMgZGlzdHJpYnV0ZWQgYnkgTWFydmVsbA0KPiBJbnRlcm5hdGlv
bmFsDQo+ID4gKyAqIEx0ZC4gdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJs
aWMgTGljZW5zZSBWZXJzaW9uIDIsDQo+IEp1bmUgMTk5MQ0KPiA+ICsgKiAodGhlICJMaWNlbnNl
IikuICBZb3UgbWF5IHVzZSwgcmVkaXN0cmlidXRlIGFuZC9vciBtb2RpZnkgdGhpcw0KPiBGaWxl
IGluDQo+ID4gKyAqIGFjY29yZGFuY2Ugd2l0aCB0aGUgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2Yg
dGhlIExpY2Vuc2UsIGEgY29weQ0KPiBvZiB3aGljaA0KPiA+ICsgKiBpcyBhdmFpbGFibGUgYnkg
d3JpdGluZyB0byB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBJbmMuDQo+ID4gKyAqDQo+
ID4gKyAqIFRIRSBGSUxFIElTIERJU1RSSUJVVEVEIEFTLUlTLCBXSVRIT1VUIFdBUlJBTlRZIE9G
IEFOWSBLSU5ELCBBTkQNCj4gVEhFDQo+ID4gKyAqIElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJD
SEFOVEFCSUxJVFkgT1IgRklUTkVTUyBGT1IgQQ0KPiBQQVJUSUNVTEFSDQo+IFBVUlBPU0UNCj4g
PiArICogQVJFIEVYUFJFU1NMWSBESVNDTEFJTUVELiAgVGhlIExpY2Vuc2UgcHJvdmlkZXMgYWRk
aXRpb25hbA0KPiBkZXRhaWxzIGFib3V0DQo+ID4gKyAqIHRoaXMgd2FycmFudHkgZGlzY2xhaW1l
ci4NCj4gPiArICovDQo+ID4gKw0KPiA+ICsvKiBEZXNjcmlwdGlvbjogIFRoaXMgZmlsZSBkZWZp
bmVzIG1hYzgwMjExIHJlbGF0ZWQgZnVuY3Rpb25zLiAqLw0KPiA+ICsNCj4gPiArI2lmbmRlZiBf
bWFjODAyMTFfaF8NCj4gPiArI2RlZmluZSBfbWFjODAyMTFfaF8NCj4gPiArDQo+ID4gKyNpbmNs
dWRlIDxuZXQvbWFjODAyMTEuaD4NCj4gPiArDQo+ID4gK2V4dGVybiBjb25zdCBzdHJ1Y3QgaWVl
ZTgwMjExX29wcyBtd2xfbWFjODAyMTFfb3BzOw0KPiA+ICsNCj4gPiArI2VuZGlmIC8qIF9tYWM4
MDIxMV9oXyAqLw0KPiANCj4gVGhhdCBmaWxlIHNlZW1zIHNvbWV3aGF0IHBvaW50bGVzcyA6KQ0K
PiBKdXN0IHB1dCB0aGUgb25lIGxpbmUgaW50byBkZXYuaD8NCj4NCg0KV2lsbCBtb2RpZnkgaXQu
DQoNCj4gDQo+ID4gKwl3aXBoeV9pbmZvKHByaXYtPmh3LT53aXBoeSwgInByaXYtPmlvYmFzZTEg
PSAlcFxuIiwgcHJpdg0KPiAtPmlvYmFzZTEpOw0KPiANCj4gQXQgImluZm8iIGxldmVsLCB0aGF0
IHNlZW1zIGEgYml0IG92ZXJraWxsLg0KPg0KDQpXaWxsIGNoYW5nZSBpdCB0byB3aXBoeV9kZWJ1
Zy4NCg0KPiANCj4gPiArCXByaXYtPnBjbWRfYnVmID0NCj4gPiArCQkodW5zaWduZWQgc2hvcnQg
KilkbWFfYWxsb2NfY29oZXJlbnQoJnByaXYtPnBkZXYtPmRldiwNCj4gPiArCQkJCQkJICAgICBD
TURfQlVGX1NJWkUsDQo+ID4gKwkJCQkJCSAgICAgJnByaXYNCj4gLT5wcGh5c19jbWRfYnVmLA0K
PiA+ICsJCQkJCQkgICAgIEdGUF9LRVJORUwpOw0KPiA+ICsNCj4gPiArCWlmICghcHJpdi0+cGNt
ZF9idWYpIHsNCj4gPiArCQl3aXBoeV9lcnIocHJpdi0+aHctPndpcGh5LA0KPiA+ICsJCQkgICIl
czogY2Fubm90IGFsbG9jIG1lbW9yeSBmb3IgY29tbWFuZA0KPiBidWZmZXJcbiIsDQo+ID4gKwkJ
CSAgTVdMX0RSVl9OQU1FKTsNCj4gPiArCQlnb3RvIGVycl9pb3VubWFwX2lvYmFzZTE7DQo+ID4g
Kwl9DQo+ID4gKw0KPiA+ICsJd2lwaHlfaW5mbyhwcml2LT5ody0+d2lwaHksDQo+ID4gKwkJICAg
InByaXYtPnBjbWRfYnVmID0gJXAgIHByaXYtPnBwaHlzX2NtZF9idWYgPSAlcFxuIiwNCj4gPiAr
CQkgICBwcml2LT5wY21kX2J1ZiwNCj4gPiArCQkgICAodm9pZCAqKXByaXYtPnBwaHlzX2NtZF9i
dWYpOw0KPiANCj4gRGl0dG8uIE5vYm9keSB3aWxsIGV2ZXIgd2FudCB0byBzZWUgdGhpcy4NCj4g
DQo+IEFsc28sIHRoZXJlJ3MgdXN1YWxseSBubyBuZWVkIHRvIHByaW50IGFueXRoaW5nIG9uIGFs
bG9jYXRpb24gZmFpbHVyZXMgYXMgdGhleSdyZQ0KPiB2ZXJ5IG5vaXN5IGFscmVhZHkuDQo+DQoN
CldpbGwgcmVtb3ZlIGl0Lg0KDQo+IA0KPiA+ICsgICAgIHJjID0gcGNpX3NldF9kbWFfbWFzayhw
ZGV2LCAweGZmZmZmZmZmKTsNCj4gDQo+IFlvdSBzaG91bGQgdXNlIG9uZSBvZiB0aGUgRE1BIG1h
c2sgY29uc3RhbnRzLg0KPiANCg0KV2lsbCBtb2RpZnkgaXQuDQoNCj4NCj4gPiArCWlmIChkZXNj
LT5wcnhfcmluZykgew0KPiA+ICsJCWRlc2MtPnJ4X2J1Zl9zaXplID0gU1lTQURQVF9NQVhfQUdH
Ul9TSVpFOw0KPiA+ICsNCj4gPiArCQlmb3IgKGkgPSAwOyBpIDwgU1lTQURQVF9NQVhfTlVNX1JY
X0RFU0M7IGkrKykgew0KPiA+ICsJCQlyeF9obmRsID0gJmRlc2MtPnJ4X2huZGxbaV07DQo+ID4g
KwkJCXJ4X2huZGwtPnBza19idWZmID0NCj4gPiArCQkJCWRldl9hbGxvY19za2IoZGVzYy0+cnhf
YnVmX3NpemUpOw0KPiA+ICsNCj4gPiArCQkJaWYgKCFyeF9obmRsLT5wc2tfYnVmZikgew0KPiA+
ICsJCQkJd2lwaHlfZXJyKHByaXYtPmh3LT53aXBoeSwNCj4gPiArCQkJCQkgICJyeGRlc2MgJWk6
IG5vIHNrYnVmZg0KPiBhdmFpbGFibGVcbiIsDQo+ID4gKwkJCQkJICBpKTsNCj4gPiArCQkJCXJl
dHVybiAtRU5PTUVNOw0KPiA+ICsJCQl9DQo+ID4gKw0KPiA+ICsJCQlpZiAoc2tiX2xpbmVhcml6
ZShyeF9obmRsLT5wc2tfYnVmZikpIHsNCj4gPiArCQkJCWRldl9rZnJlZV9za2JfYW55KHJ4X2hu
ZGwtPnBza19idWZmKTsNCj4gPiArCQkJCXJ4X2huZGwtPnBza19idWZmID0gTlVMTDsNCj4gPiAr
CQkJCXdpcGh5X2Vycihwcml2LT5ody0+d2lwaHksDQo+ID4gKwkJCQkJICAibmVlZCBsaW5lYXJp
emUgbWVtb3J5XG4iKTsNCj4gPiArCQkJCXJldHVybiAtRU5PTUVNOw0KPiA+ICsJCQl9DQo+IA0K
PiBUaGlzIGlzIHN0aWxsIGNvbXBsZXRlbHkgcG9pbnRsZXNzLg0KPiANCj4gPiArCQkJc2tiX3Jl
c2VydmUocnhfaG5kbC0+cHNrX2J1ZmYsDQo+ID4gKwkJCQkgICAgU1lTQURQVF9NSU5fQllURVNf
SEVBRFJPT00pOw0KPiA+ICsJCQlkZXNjLT5wcnhfcmluZ1tpXS5yeF9jb250cm9sID0NCj4gPiAr
CQkJCUVBR0xFX1JYRF9DVFJMX0RSSVZFUl9PV047DQo+ID4gKwkJCWRlc2MtPnByeF9yaW5nW2ld
LnN0YXR1cyA9DQo+IEVBR0xFX1JYRF9TVEFUVVNfT0s7DQo+ID4gKwkJCWRlc2MtPnByeF9yaW5n
W2ldLnFvc19jdHJsID0gMHgwMDAwOw0KPiA+ICsJCQlkZXNjLT5wcnhfcmluZ1tpXS5jaGFubmVs
ID0gMHgwMDsNCj4gPiArCQkJZGVzYy0+cHJ4X3JpbmdbaV0ucnNzaSA9IDB4MDA7DQo+ID4gKwkJ
CWRlc2MtPnByeF9yaW5nW2ldLnBrdF9sZW4gPQ0KPiA+ICsJCQkJY3B1X3RvX2xlMTYoU1lTQURQ
VF9NQVhfQUdHUl9TSVpFKTsNCj4gPiArCQkJZG1hID0gcGNpX21hcF9zaW5nbGUocHJpdi0+cGRl
diwNCj4gPiArCQkJCQkgICAgIHJ4X2huZGwtPnBza19idWZmLT5kYXRhLA0KPiA+ICsJCQkJCSAg
ICAgZGVzYy0+cnhfYnVmX3NpemUsDQo+ID4gKwkJCQkJICAgICBQQ0lfRE1BX0ZST01ERVZJQ0Up
Ow0KPiANCj4gWW91IG5lZWQgdG8gY2hlY2sgdGhlIHJldHVybiB2YWx1ZSAocHJvcGVybHkgLSB1
c2UgdGhlIHJpZ2h0IGZ1bmN0aW9uLCBub3QgIT0NCj4gTlVMTCEpDQo+DQoNCldpbGwgbW9kaWZ5
IGl0Lg0KDQo+IA0KPiA+ICsJaWYgKGRlc2MtPnByeF9yaW5nKSB7DQo+ID4gKwkJZm9yIChpID0g
MDsgaSA8IFNZU0FEUFRfTUFYX05VTV9SWF9ERVNDOyBpKyspIHsNCj4gPiArCQkJcnhfaG5kbCA9
ICZkZXNjLT5yeF9obmRsW2ldOw0KPiA+ICsJCQlpZiAoIXJ4X2huZGwtPnBza19idWZmKQ0KPiA+
ICsJCQkJY29udGludWU7DQo+ID4gKw0KPiA+ICsJCQlpZiAoc2tiX3NoaW5mbyhyeF9obmRsLT5w
c2tfYnVmZiktPm5yX2ZyYWdzKQ0KPiA+ICsJCQkJc2tiX3NoaW5mbyhyeF9obmRsLT5wc2tfYnVm
ZikNCj4gLT5ucl9mcmFncyA9IDA7DQo+ID4gKw0KPiA+ICsJCQlpZiAoc2tiX3NoaW5mbyhyeF9o
bmRsLT5wc2tfYnVmZiktPmZyYWdfbGlzdCkNCj4gPiArCQkJCXNrYl9zaGluZm8ocnhfaG5kbC0+
cHNrX2J1ZmYpDQo+IC0+ZnJhZ19saXN0ID0gTlVMTDsNCj4gDQo+IEhvdyBkbyB5b3UgdGhpbmsg
dGhpcyB3b3VsZCBtYWtlIGFueSBzZW5zZT8/DQo+DQoNCldpbGwgY29ycmVjdCBpdC4NCg0KPiAN
Cj4gPiArCQlpZiAocGRlc2MtPnN0YXR1cyAhPSBHRU5FUkFMX0RFQ1JZUFRfRVJSKSB7DQo+ID4g
KwkJCWlmICgoKHBkZXNjLT5zdGF0dXMgJiAofkRFQ1JZUFRfRVJSX01BU0spKSAmDQo+ID4gKwkJ
CSAgICBUS0lQX0RFQ1JZUFRfTUlDX0VSUikgJiYgISgocGRlc2MtPnN0YXR1cw0KPiAmDQo+ID4g
KwkJCSAgICAoV0VQX0RFQ1JZUFRfSUNWX0VSUiB8DQo+IFRLSVBfREVDUllQVF9JQ1ZfRVJSKSkp
KSB7DQo+IA0KPiBUaGUgY29kZSBmb3JtYXR0aW5nIGZvciB0aGlzIGNvbmRpdGlvbiBpcyB2ZXJ5
IHZlcnkgc3RyYW5nZS4NCj4gDQo+ID4gK3N0YXRpYyBpbnQgbXdsX3J4X3JlZmlsbChzdHJ1Y3Qg
bXdsX3ByaXYgKnByaXYsIHN0cnVjdCBtd2xfcnhfaG5kbA0KPiAqcnhfaG5kbCkNCj4gPiArew0K
PiA+ICsJc3RydWN0IG13bF9kZXNjX2RhdGEgKmRlc2M7DQo+ID4gKw0KPiA+ICsJZGVzYyA9ICZw
cml2LT5kZXNjX2RhdGFbMF07DQo+ID4gKw0KPiA+ICsJcnhfaG5kbC0+cHNrX2J1ZmYgPSBkZXZf
YWxsb2Nfc2tiKGRlc2MtPnJ4X2J1Zl9zaXplKTsNCj4gPiArDQo+ID4gKwlpZiAoIXJ4X2huZGwt
PnBza19idWZmKQ0KPiA+ICsJCWdvdG8gbm9tZW07DQo+ID4gKw0KPiA+ICsJaWYgKHNrYl9saW5l
YXJpemUocnhfaG5kbC0+cHNrX2J1ZmYpKSB7DQo+ID4gKwkJZGV2X2tmcmVlX3NrYl9hbnkocnhf
aG5kbC0+cHNrX2J1ZmYpOw0KPiA+ICsJCXdpcGh5X2Vycihwcml2LT5ody0+d2lwaHksICJuZWVk
IGxpbmVhcml6ZSBtZW1vcnlcbiIpOw0KPiA+ICsJCWdvdG8gbm9tZW07DQo+ID4gKwl9DQo+IA0K
PiBTdGlsbCBtYWtlIG5vIHNlbnNlLg0KPiANCj4gPiArCXJ4X2huZGwtPnBkZXNjLT5wcGh5c19i
dWZmX2RhdGEgPQ0KPiA+ICsJCWNwdV90b19sZTMyKHBjaV9tYXBfc2luZ2xlKHByaXYtPnBkZXYs
DQo+ID4gKwkJCQkJICAgcnhfaG5kbC0+cHNrX2J1ZmYtPmRhdGEsDQo+ID4gKwkJCQkJICAgZGVz
Yy0+cnhfYnVmX3NpemUsDQo+ID4gKwkJCQkJICAgUENJX0RNQV9CSURJUkVDVElPTkFMKSk7DQo+
IA0KPiBBbHNvIG5lZWQgdG8gY2hlY2sgdGhlIHJldHVybiB2YWx1ZS4gQklESVJFQ1RJT05BTCBh
bHNvIHNlZW1zIHdyb25nLg0KPiANCg0KV2lsbCBtb2RpZnkgaXQuDQoNCj4gDQo+ID4gK2ludCBt
d2xfcnhfaW5pdChzdHJ1Y3QgaWVlZTgwMjExX2h3ICpodykgew0KPiA+ICsJc3RydWN0IG13bF9w
cml2ICpwcml2Ow0KPiA+ICsJaW50IHJjOw0KPiA+ICsNCj4gPiArCXByaXYgPSBody0+cHJpdjsN
Cj4gPiArDQo+ID4gKwlyYyA9IG13bF9yeF9yaW5nX2FsbG9jKHByaXYpOw0KPiA+ICsJaWYgKHJj
KSB7DQo+ID4gKwkJd2lwaHlfZXJyKGh3LT53aXBoeSwgImFsbG9jYXRpbmcgUlggcmluZyBmYWls
ZWRcbiIpOw0KPiA+ICsJfSBlbHNlIHsNCj4gPiArCQlyYyA9IG13bF9yeF9yaW5nX2luaXQocHJp
dik7DQo+ID4gKwkJaWYgKHJjKSB7DQo+ID4gKwkJCW13bF9yeF9yaW5nX2ZyZWUocHJpdik7DQo+
ID4gKwkJCXdpcGh5X2Vycihody0+d2lwaHksDQo+ID4gKwkJCQkgICJpbml0aWFsaXppbmcgUlgg
cmluZyBmYWlsZWRcbiIpOw0KPiA+ICsJCX0NCj4gPiArCX0NCj4gDQo+IEJldHRlciB3cml0ZSB0
aGlzIGFzDQo+IA0KPiAgcmMgPSBhbGxvYygpDQo+ICBpZiAocmMpDQo+ICAgIHJldHVybjsNCj4g
IHJjID0gaW5pdCgpDQo+ICBpZiAocmMpDQo+ICAgIHJldHVybjsNCj4gDQo+IGV0Yy4NCj4gDQoN
CldpbGwgbW9kaWZ5IGl0Lg0KDQo+ID4gKwkJCWlmIChpZWVlODAyMTFfaGFzX3RvZHMod2gtPmZy
YW1lX2NvbnRyb2wpKQ0KPiA+ICsJCQkJbXdsX3ZpZiA9IG13bF9yeF9maW5kX3ZpZl9ic3MocHJp
diwgd2gNCj4gLT5hZGRyMSk7DQo+ID4gKwkJCWVsc2UNCj4gPiArCQkJCW13bF92aWYgPSBtd2xf
cnhfZmluZF92aWZfYnNzKHByaXYsIHdoDQo+IC0+YWRkcjIpOw0KPiA+ICsNCj4gPiArCQkJaWYg
KG13bF92aWYgJiYgbXdsX3ZpZi0+aXNfaHdfY3J5cHRvX2VuYWJsZWQpDQo+IHsNCj4gPiArCQkJ
CS8qIFdoZW4gTU1JQyBFUlJPUiBpcyBlbmNvdW50ZXJlZA0KPiA+ICsJCQkJICogYnkgdGhlIGZp
cm13YXJlLCBwYXlsb2FkIGlzDQo+ID4gKwkJCQkgKiBkcm9wcGVkIGFuZCBvbmx5IDMyIGJ5dGVz
IG9mDQo+ID4gKwkJCQkgKiBtd2w4ayBGaXJtd2FyZSBoZWFkZXIgaXMgc2VudA0KPiA+ICsJCQkJ
ICogdG8gdGhlIGhvc3QuDQo+ID4gKwkJCQkgKg0KPiA+ICsJCQkJICogV2UgbmVlZCB0byBhZGQg
Zm91ciBieXRlcyBvZg0KPiA+ICsJCQkJICoga2V5IGluZm9ybWF0aW9uLiAgSW4gaXQNCj4gPiAr
CQkJCSAqIE1BQzgwMjExIGV4cGVjdHMga2V5aWR4IHNldCB0bw0KPiA+ICsJCQkJICogMCBmb3Ig
dHJpZ2dlcmluZyBDb3VudGVyDQo+ID4gKwkJCQkgKiBNZWFzdXJlIG9mIE1NSUMgZmFpbHVyZS4N
Cj4gPiArCQkJCSAqLw0KPiANCj4gVW1tLiBUaGF0J3Mgb25seSB0cnVlIGZvciBQVEtzLg0KPiAN
Cj4gPiArc3RhdGljIGludCBtd2xfdHhfcmluZ19hbGxvYyhzdHJ1Y3QgbXdsX3ByaXYgKnByaXYp
IHsNCj4gPiArCXN0cnVjdCBtd2xfZGVzY19kYXRhICpkZXNjOw0KPiA+ICsJaW50IG51bTsNCj4g
PiArCXU4ICptZW07DQo+ID4gKw0KPiA+ICsJZGVzYyA9ICZwcml2LT5kZXNjX2RhdGFbMF07DQo+
ID4gKw0KPiA+ICsJbWVtID0gKHU4ICopZG1hX2FsbG9jX2NvaGVyZW50KCZwcml2LT5wZGV2LT5k
ZXYsDQo+ID4gKwkJTUFYX05VTV9UWF9SSU5HX0JZVEVTICogU1lTQURQVF9OVU1fT0ZfREVTQ19E
QVRBLA0KPiA+ICsJCSZkZXNjLT5wcGh5c190eF9yaW5nLCBHRlBfS0VSTkVMKTsNCj4gDQo+IFRo
YXQgY2FzdCBpcyBwb2ludGxlc3MsIHRoZSBmdW5jdGlvbiByZXR1cm5zIHZvaWQgKi4NCj4gDQo+
ID4gK3N0YXRpYyBpbmxpbmUgdm9pZCBtd2xfdHhfaW5zZXJ0X2NjbXBfaGRyKHU4ICpwY2NtcF9o
ZHIsDQo+ID4gKwkJCQkJICB1OCBrZXlfaWQsIHUxNiBpdjE2LCB1MzINCj4gaXYzMikNCj4gPiAr
ew0KPiA+ICsJKigodTE2ICopcGNjbXBfaGRyKSA9IGl2MTY7DQo+ID4gKwlwY2NtcF9oZHJbMl0g
PSAwOw0KPiA+ICsJcGNjbXBfaGRyWzNdID0gRVhUX0lWIHwgKGtleV9pZCA8PCA2KTsNCj4gPiAr
CSooKHUzMiAqKSZwY2NtcF9oZHJbNF0pID0gaXYzMjsNCj4gPiArfQ0KPiANCj4gV2h5IGRvbid0
IHlvdSBsZXQgbWFjODAyMTEgZG8gdGhpcz8gWW91IGFsc28gaGF2ZSBjbGVhciBhbGlnbm1lbnQg
YW5kDQo+IGVuZGlhbiBpc3N1ZXMgaGVyZSwgc28gbWFjODAyMTEgd2lsbCBkbyBpdCBiZXR0ZXIu
Li4NCj4NCg0KV2lsbCBmaXggYWxpZ25tZW50IGFuZCBlbmRpYW4gcHJvYmxlbS4gQ29udmVyc2lv
biBvZiBkYXRhIHBhY2tldCB0byB3b3JrIHdpdGggZmlybXdhcmUgaXMgZG9uZSBhbmQgd2VsbCB0
ZXN0ZWQuIE1heWJlIHdlIGNhbiBjaGVjayBob3cgdG8gbGV0IG1hYzgwMjExIGRvIHRoaXMgbGF0
ZXIuDQoNCj4gDQo+ID4gKwlpZiAodHhfc3RhdHMtPnN0YXJ0X3RpbWUgPT0gMCkNCj4gPiArCQl0
eF9zdGF0cy0+c3RhcnRfdGltZSA9IGppZmZpZXM7DQo+IA0KPiBCdHcsIGRvbid0IHJlbWVtYmVy
IGlmIEkgY29tbWVudGVkIG9uIHRoaXMgYmVmb3JlIC0gYnV0IHlvdSBjYW4ndCBzdG9yZSBqaWZm
aWVzDQo+IGluIGEgdTMyLCBuZWVkIHVuc2lnbmVkIGxvbmcuDQo+IA0KDQpXaWxsIG1vZGlmeSBp
dC4NCg0KPiANCj4gPiArCWlmICh0eF9obmRsLT5wZGVzYy0+c3RhdHVzICE9DQo+ID4gKwkgICAg
RUFHTEVfVFhEX1NUQVRVU19JRExFKSB7DQo+IA0KPiB3aGF0J3Mgd2l0aCB0aGUgbGluZWJyZWFr
Pw0KPiANCg0KV2lsbCByZW1vdmUgaXQuDQoNCj4gDQo+ID4gKwlpZiAoaWVlZTgwMjExX2lzX2Rh
dGEod2gtPmZyYW1lX2NvbnRyb2wpKSB7DQo+ID4gKwkJaWYgKGlzX211bHRpY2FzdF9ldGhlcl9h
ZGRyKHdoLT5hZGRyMSkpIHsNCj4gPiArCQkJaWYgKGNjbXApIHsNCj4gPiArCQkJCW13bF90eF9p
bnNlcnRfY2NtcF9oZHIoZG1hX2RhdGEtPmRhdGEsDQo+ID4gKwkJCQkJCSAgICAgICBtd2xfdmlm
DQo+IC0+a2V5aWR4LA0KPiA+ICsJCQkJCQkgICAgICAgbXdsX3ZpZi0+aXYxNiwNCj4gPiArCQkJ
CQkJICAgICAgIG13bF92aWYtPml2MzIpOw0KPiA+ICsJCQkJSU5DUkVBU0VfSVYobXdsX3ZpZi0+
aXYxNiwgbXdsX3ZpZg0KPiAtPml2MzIpOw0KPiA+ICsJCQl9DQo+ID4gKwkJfSBlbHNlIHsNCj4g
PiArCQkJaWYgKGNjbXApIHsNCj4gPiArCQkJCWlmIChtd2xfdmlmLT5pc19zdGEpIHsNCj4gPiAr
CQkJCQltd2xfdHhfaW5zZXJ0X2NjbXBfaGRyKGRtYV9kYXQNCj4gYS0+ZGF0YSwNCj4gPiArCQkJ
CQkJCSAgICAgICBtd2xfdmlmDQo+IC0+a2V5aWR4LA0KPiA+ICsJCQkJCQkJICAgICAgIG13bF92
aWYNCj4gLT5pdjE2LA0KPiA+ICsJCQkJCQkJICAgICAgIG13bF92aWYNCj4gLT5pdjMyKTsNCj4g
PiArCQkJCQlJTkNSRUFTRV9JVihtd2xfdmlmLT5pdjE2LA0KPiA+ICsJCQkJCQkgICAgbXdsX3Zp
Zi0+aXYzMik7DQo+ID4gKwkJCQl9IGVsc2Ugew0KPiA+ICsJCQkJCXN0cnVjdCBtd2xfc3RhICpz
dGFfaW5mbzsNCj4gPiArDQo+ID4gKwkJCQkJc3RhX2luZm8gPQ0KPiBtd2xfZGV2X2dldF9zdGEo
c3RhKTsNCj4gPiArDQo+ID4gKwkJCQkJbXdsX3R4X2luc2VydF9jY21wX2hkcihkbWFfZGF0DQo+
IGEtPmRhdGEsDQo+ID4gKwkJCQkJCQkgICAgICAgMCwNCj4gPiArDQo+ICBzdGFfaW5mby0+aXYx
NiwNCj4gPiArDQo+ICBzdGFfaW5mby0+aXYzMik7DQo+ID4gKwkJCQkJSU5DUkVBU0VfSVYoc3Rh
X2luZm8tPml2MTYsDQo+ID4gKwkJCQkJCSAgICBzdGFfaW5mby0+aXYzMik7DQo+ID4gKwkJCQl9
DQo+ID4gKwkJCX0NCj4gPiArCQl9DQo+ID4gKwl9DQo+IA0KPiBBbGwgb2YgdGhpcyBpcyB2ZXJ5
IHZlcnkgZHViaW91cy4gV2h5IG5vdCBsZXQgbWFjODAyMTEgaGFuZGxlIGl0Pw0KDQpDb252ZXJz
aW9uIG9mIGRhdGEgcGFja2V0IHRvIHdvcmsgd2l0aCBmaXJtd2FyZSBpcyBkb25lIGFuZCB3ZWxs
IHRlc3RlZC4gTWF5YmUgd2UgY2FuIGNoZWNrIGhvdyB0byBsZXQgbWFjODAyMTEgZG8gdGhpcyBs
YXRlci4NCg0KPiANCj4gPiArCXR4X2Rlc2MtPnBrdF9wdHIgPQ0KPiA+ICsJCWNwdV90b19sZTMy
KHBjaV9tYXBfc2luZ2xlKHByaXYtPnBkZXYsIHR4X3NrYi0+ZGF0YSwNCj4gPiArCQkJCQkgICB0
eF9za2ItPmxlbiwNCj4gUENJX0RNQV9UT0RFVklDRSkpOw0KPiANCj4gbWlzc2luZyBlcnJvciBj
aGVjaw0KPiANCj4gPiAraW50IG13bF90eF9pbml0KHN0cnVjdCBpZWVlODAyMTFfaHcgKmh3KSB7
DQo+ID4gKwlzdHJ1Y3QgbXdsX3ByaXYgKnByaXY7DQo+ID4gKwlpbnQgcmM7DQo+ID4gKw0KPiA+
ICsJcHJpdiA9IGh3LT5wcml2Ow0KPiA+ICsNCj4gPiArCXNrYl9xdWV1ZV9oZWFkX2luaXQoJnBy
aXYtPmRlbGF5X3EpOw0KPiA+ICsNCj4gPiArCXJjID0gbXdsX3R4X3JpbmdfYWxsb2MocHJpdik7
DQo+ID4gKwlpZiAocmMpIHsNCj4gPiArCQl3aXBoeV9lcnIoaHctPndpcGh5LCAiYWxsb2NhdGlu
ZyBUWCByaW5nIGZhaWxlZFxuIik7DQo+ID4gKwl9IGVsc2Ugew0KPiA+ICsJCXJjID0gbXdsX3R4
X3JpbmdfaW5pdChwcml2KTsNCj4gPiArCQlpZiAocmMpIHsNCj4gPiArCQkJbXdsX3R4X3Jpbmdf
ZnJlZShwcml2KTsNCj4gPiArCQkJd2lwaHlfZXJyKGh3LT53aXBoeSwgImluaXRpYWxpemluZyBU
WCByaW5nDQo+IGZhaWxlZFxuIik7DQo+ID4gKwkJfQ0KPiA+ICsJfQ0KPiA+ICsNCj4gPiArCXJl
dHVybiByYzsNCj4gPiArfQ0KPiANCj4gU2FtZSBjb21tZW50IGFzIG9uIFJYLg0KPiANCg0KV2ls
bCBtb2RpZnkgaXQuDQoNCj4NCj4gPiArCWlmIChza2ItPnByb3RvY29sID09IGNwdV90b19iZTE2
KEVUSF9QX1BBRSkpIHsNCj4gPiArCQlpbmRleCA9IElFRUU4MDIxMV9BQ19WTzsNCj4gPiArCQll
YXBvbF9mcmFtZSA9IHRydWU7DQo+ID4gKwl9DQo+IA0KPiBUaGVyZSdzIGEgdHhfaW5mbyBmbGFn
IGZvciB0aGlzIHRoYXQncyBhIGJpdCBtb3JlIGdlbmVyaWMuDQo+DQoNCk1heWJlIG1vZGlmeSBp
dCBsYXRlci4NCg0KPg0KPiBKb2hhbm5lcw0KDQpUaGFua3MsDQpEYXZpZA0K
--
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
Maxime Bizon July 9, 2015, 3:27 p.m. UTC | #13
On Fri, 2015-07-03 at 06:10 +0000, David Lin wrote:

Hello,

> The Linux driver for WRT1900AC. The work was initially developed as
> part of openwrt effort and maintained on
> https://github.com/kaloz/mwlwifi.

I see *massive* code duplication with the mwl8k driver.

This is, I guess, not a surprise since the supported chipset are an
evolution of the ones supported by mwl8k, just look at how close/similar
the descriptor format is.

The problem is that each new Marvell chipset comes with a new firmware
version, and there is no effort to have a common firmware API or code
base that supports multiple chipsets.

That pushes the bloat on the kernel side, do we really agree on that ?
Kalle Valo Aug. 4, 2015, 5:02 p.m. UTC | #14
Maxime Bizon <mbizon@freebox.fr> writes:

> On Fri, 2015-07-03 at 06:10 +0000, David Lin wrote:
>
> Hello,
>
>> The Linux driver for WRT1900AC. The work was initially developed as
>> part of openwrt effort and maintained on
>> https://github.com/kaloz/mwlwifi.
>
> I see *massive* code duplication with the mwl8k driver.

I haven't looked at the driver myself yet. Do you have any estimates how
much duplication there is?

> This is, I guess, not a surprise since the supported chipset are an
> evolution of the ones supported by mwl8k, just look at how close/similar
> the descriptor format is.
>
> The problem is that each new Marvell chipset comes with a new firmware
> version, and there is no effort to have a common firmware API or code
> base that supports multiple chipsets.
>
> That pushes the bloat on the kernel side, do we really agree on that ?

That is a very good question. The kernel should not be a dumping ground
for random drivers which duplicate lots of code but this is a sort of
gray area and depends on the case.

Did Marvell reply to this? I didn't find any comments about this.
Chor Teck Law Aug. 4, 2015, 6:11 p.m. UTC | #15
Hi Kalle,

> -----Original Message-----
> From: Kalle Valo [mailto:kvalo@codeaurora.org]
> Sent: Tuesday, August 04, 2015 10:02 AM
> 
> Maxime Bizon <mbizon@freebox.fr> writes:
> 
> > On Fri, 2015-07-03 at 06:10 +0000, David Lin wrote:
> >
> > Hello,
> >
> >> The Linux driver for WRT1900AC. The work was initially developed as
> >> part of openwrt effort and maintained on
> >> https://github.com/kaloz/mwlwifi.
> >
> > I see *massive* code duplication with the mwl8k driver.
> 
> I haven't looked at the driver myself yet. Do you have any estimates
> how much duplication there is?
> 

mwl8k was a driver for chips few generations older sponsored by Marvell. We did leverage part of driver framework that is still applicable. However, for the current development, the firmware API specs, chip capabilities and bandwidth requirements have changed in order to handle newer technologies and features.

> > This is, I guess, not a surprise since the supported chipset are an
> > evolution of the ones supported by mwl8k, just look at how
> > close/similar the descriptor format is.
> >
> > The problem is that each new Marvell chipset comes with a new
> firmware
> > version, and there is no effort to have a common firmware API or code
> > base that supports multiple chipsets.
> >
> > That pushes the bloat on the kernel side, do we really agree on that
> ?
> 
> That is a very good question. The kernel should not be a dumping ground
> for random drivers which duplicate lots of code but this is a sort of
> gray area and depends on the case.
>

No, we are not treating the submission as dumping! We would not have responded with effort to meet the requests/feedback if so. We appreciate and have taken the feedbacks seriously to complete to patch6. In fact the submission was requested by some community members who has seen the benefit of its evolvement on openwrt github, and they would like to see the new driver consolidated into the wireless mainline.

Due to hardware, firmware, specs and requirements change over time, it is not feasible for us to revisit generations old products or making sure new changes are backward compatible with it. (If desired, we welcome the community to take any new useful changes that are independent of chip rev to other similar branches.)

Lastly, I do not think we are creating a precedence with different generation of drivers supporting different families of chips. 
 
>
> Did Marvell reply to this? I didn't find any comments about this.
>

Sorry, we have missed that email earlier... I hope the above response will suffice to get accepted. Best regards,



 
> --
> 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
Maxime Bizon Aug. 4, 2015, 10:09 p.m. UTC | #16
On Tuesday 04 Aug 2015 à 20:02:23 (+0300), Kalle Valo wrote:

> I haven't looked at the driver myself yet. Do you have any estimates how
> much duplication there is?

ok the thing is I happen to know a bit about the chipset internals and
so it's easy for me to know that having a common driver is do-able.

so I may be biased, and I guess someone else should have a quick
glance at both driver code.
Maxime Bizon Aug. 4, 2015, 10:49 p.m. UTC | #17
On Tuesday 04 Aug 2015 à 18:11:29 (+0000), Chor Teck Law wrote:

Hi Chor,

> mwl8k was a driver for chips few generations older sponsored by
> Marvell. We did leverage part of driver framework that is still
> applicable. However, for the current development, the firmware API
> specs, chip capabilities and bandwidth requirements have changed in
> order to handle newer technologies and features.

this is a fullmac driver, what differs between them is:
 - how you boot the device / load firmware
 - how you send commands to it
 - datapath


from a quick glance at your new driver

 1) firmware loading works the same way

 2) "hostcmd" are used to communicate with the firmware, of course new
   commands are needed for newer standards, but a common API could be
   used for common basic operations

 3) rx datapath looks the same (look at struct mwl_rx_desc vs mwl8k_rxd_ap)

 4) AMPDU code is copied/pasted from mwl8k driver, even the original
   comments have not been updated:

+  /* Defer calling mwl8k_start_stream so that the current


> No, we are not treating the submission as dumping! We would not have
> responded with effort to meet the requests/feedback if so. We
> appreciate and have taken the feedbacks seriously to complete to
> patch6. In fact the submission was requested by some community
> members who has seen the benefit of its evolvement on openwrt
> github, and they would like to see the new driver consolidated into
> the wireless mainline.

judging from the mwl8k experience (hello, unhappy customer here),
mainline is indeed the goal, but after that, nothing.

driver & firmware left at version 1, unless customer (me in that case)
made an explicit bug report or feature request.

> Due to hardware, firmware, specs and requirements change over time,
> it is not feasible for us to revisit generations old products or
> making sure new changes are backward compatible with it. (If
> desired, we welcome the community to take any new useful changes
> that are independent of chip rev to other similar branches.)

no hardware specs, no firmware source or docs, community can only make
blind guesses.

also see my answer beyond.

> Lastly, I do not think we are creating a precedence with different
> generation of drivers supporting different families of chips.

these are firmware based chipsets, not hard-wired. having a common
driver for two completely different set of chipsets is indeed stupid,
and makes it unreadable.

but from my POV, your pattern is: build new chipset, obsolete previous
chipset, fork current firmware, make uncompatible changes to both
firmware & host driver to accomodate new chipset peculiarities.

and eventually, if a customer requests a mainline linux driver, then
write one... this is what I'd call "dumping"
Chor Teck Law Aug. 5, 2015, 8:39 a.m. UTC | #18
> 
> On Tuesday 04 Aug 2015 à 20:02:23 (+0300), Kalle Valo wrote:
> 
> > I haven't looked at the driver myself yet. Do you have any estimates
> > how much duplication there is?
> 

I do think that someone interested in this thread should compare the two drivers to decide if they want to accept mwlwifi (or we let it develop closed door). 

You will find the differences with some leverage. To make your comparison easier, here is the summary (excluding the patch1-patch6 improvements as per review feedbacks from Johannes and community):

mwlwifi functions leveraged from mwl8k:
- 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 (but added with 11ac rate map/reports)

All others are different/new. Including some listed below:
- Different/new 11ac chipsets, including combo BT devices
- Added support for 11ac related settings and functions
- Added support for concurrent AP+STA functionalities with single firmware per chip
- Additional firmware APIs 
- Added functions to convert newly supported settings from mac80211 to firmware
- Different Tx datapath 
- Different Rx low level datapath (though data structure for receiving is same)
- Reorganized the files for future scalability and features addition (more to come)
- Addressed all (sort of) review feedbacks from patch1-patch6 of this submission

Files to compare:
- Mwlwifi: dev.h, fwcmd.h, fwdl.h, hostcmd.h, isr.h, rx.h, sysadpt.h, tx.h
fwcmd.c, fwdl.c, isr.c, mac80211.c, main.c, rx.c, tx.c
- mwl8k: mwl8k.c


Changes (based on feedbacks) since initial submission:

PATCH: mwlwifi version 10.3.0.2 (Initial submission)

1. Rename all files from mwl_xxx.x to xxx.x.
2. Remove debug.c and debug.h.
3. Use wiphy_xxxxx() to print out messages.
4. Take off unnecessary BUG_ON() checking.
5. Remove comments used to explain the purpose of the code block and take off static function pre-declaration.
6. Typo error, unnecessary comments, prefer comment style and use BIT MACRO instead of constant.
7. Use __le16 and __le32 for data structure related to F/W communication.
8. Take off MACRO for cpu_to_le16 and cpu_to_le32. Use them directly.
9. Fixing warning when using parse to check endian (C=2 CF="-D__CHECK_ENDIAN__").
10. Remove unused constants.
11. Remove MACRO for mdelay() and use it directly.
12. Revise one infinite loop code segment.
13. Rewrite one code segement to use switch.
14. Use static inline function mwl_dev_get_vif to replace MACRO MWL_VIF.
15. Use static inline function mwl_dev_get_sta to replace MACRO MWL_STA.
16. Remove MACRO IEEE80211_KEY_CONF.
17. Remove IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE and IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE.
18. RemoveMACRO for SPIN LOCK and let spin lock be attached to related data structure.
19. Define constants for power initization instead of using value 1 and 2.
20. Take off unused constants.
21. Use switch to rewrite two code segments.
22. Refine parameters for function mwl_tx_xmit().
23. Use external constant variable to export mac80211 function array.
24.Move isr related functions to isr.c and isr.h.
25. Remove bitfields from Tx and Rx descriptor data structure.
26. Use mask and shift operation on these fields to make sure they are endian independent.
27. Remove bitfields from F/W command data structure.
28. Use mask and shift operation on these fields to make sure they are endian independent.
29. Fix problem: "Tx rate information does not display correctly".

>>> PATCH v2: mwlwifi version 10.3.0.3

1. Remove IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454.
2. Add IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991.
3. Change copy right header and module license to GPL v2.
4. Replace copy right header with the one used by mac80211 in order to pass the checking of checkpatch.pl.
5. Separate F/W related structure and host driver related structure (mwl_tx_desc and mwl_tx_hndl).
6. Move host driver related fields from mwl_tx_desc to mwl_tx_hndl and define original field as __le32. In this way, F/W related structure will always be 32-bits alignment no matter host is 32-bits or 64-bits.
7. Let driver handle on mwl_tx_hndl.
8. Modify related tx.c to use this new data structure and remove MACROs as suggested from linux wireless.
9. Follow one line comment as suggested by linux wireless.
10. Create hostcmd.h and move related constants and structure to this header file.
11. Removce unused MACROs.

>>> PATCH v3: mwlwifi version 10.3.0.4

1. Change copyright header.
2. Fix typo error.
3. Replace wiphy_info() with wiphy_debug() for debug messages.
4. Add file MAINTAINERS.
5. Use proper spin lock.
6. Add "-D__CHECK_ENDIAN__" to Makefile.
7. Remove warnings from sparse.
8. Remove warnings from 64-bits gcc.

>>> PATCH v4: mwlwifi version 10.3.0.5

1. Add newline to all log messages.
2. Fix uncorrect error message.
3. Access dma mapped memeory directly instead of function writew().
4. Rewrite mwl_rx_ring_init() to take care of error exception.
5. Remove dependency on "MWIFIEX_PCIE=n".
6. Correct some messages.
7. Add note(warning) message to describe the difference between mwlwifi and mwifiex. The message also warns user to select one of these two drivers for 88W8897 module.

>>> PATCH v5: mwlwifi version 10.3.0.6

1. Correct wording for log messages.
2. Remove unnecessary code.
3. Change title to "Marvell 88W8864/88W8897 PCIe driver with AP support".
4. Remove "select OF".
5. Let the driver can still work even though CONFIG_OF is not defined.
6. Fix typo error.
7. Remove unused field.
8. Change type of cdd from u32 to bool.
9. Move request_irq from mwl_mac80211_start() to mwl_wl_init().
10. Move free_irq from mwl_mac80211_stop() to mwl_wl_deinit().
11. Modify "struct mwl_priv *priv; priv = hw->priv" to "struct mwl_priv *priv = hw->priv;" to reduce the length of code.
12. Change type of jiffies related variables from u32 to unsigned long.
13. Change wiphy_info() to wiphy_debug() for some debug messages.
14. Remove "#if SYSADPT_NUM_OF_DESC_DATA > 3 ...#endif".
15. Use correct PCI constant DMA_MASK_BIT(32).
16. Delete mac80211.h.
17. Move exported variable in mac80211.h to dev.h.
18. Check if macid is zero before decreasing it.
19. Modify some code segment to use switch instead of if..else.
20. Remove fields vif and is_sta from structure mwl_vif.
21. Remove field sta from structure mwl_sta.
22. Change MAX_WAIT_FW_COMPLETE_ITERATIONS from 10000 to 500.
23. Rewrite mwl_tx_init() and mwl_rx_init() according to suggestion from Linux wireless community.
24. Fix ccmp header endian problem.
25. Use pci_dma_mapping_error() to check mapping result of pci_map_single().
26. Correct PCI mapping constant.
27. Let code can pass the checking of checkpatch.pl.
28. Remove unnecessary code.
29. Change title of Kconfig from "Marvell 88W8864/88W8897 PCIe driver with AP support" to "Marvell Avastar 88W8864/88W8897 PCIe driver (mac80211 compatible)".

>>> PATCH v6: mwlwifi version 10.3.0.7


Last words, The mwl8k series had its history, since 2009 with the Topdog series (Beyond our scope to comment). The mwlwifi have been developed over past year or so for the Avastar AP chips, being nurtured on openwrt github - we will continue to maintain. We thought this submission could benefit broader community in mainline linuxwireless.

If the gate-keeper and community really feel that we cannot submit a new driver for new chipsets due to some history, we will respect the decision. But as said, we are not setting precedence. Those who knows other drivers can inspect their histories and variance.


Regards
Chor-Teck
--
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
Maxime Bizon Aug. 5, 2015, 2:21 p.m. UTC | #19
On Wednesday 05 Aug 2015 à 08:39:28 (+0000), Chor Teck Law wrote:

> Last words, The mwl8k series had its history, since 2009 with the
> Topdog series (Beyond our scope to comment). The mwlwifi have been
> developed over past year or so for the Avastar AP chips, being

incorrect, mwl8k also support Avastar AP (88W8764)

one of the mwlwifi feedback was that the Kconfig description was
inaccurate, and because of overlap with mwifiex, everyone did scratch
their heads trying to describe what each driver actually supports.

feel free to correct me on that, but from the outside it looks like
there are multiple wifi teams at Marvell, each one working on its own
firmware/driver set, sometimes for the same chipset as the other
teams, and they don't talk to each other.

so anyway I'm in no position to NAK your driver submission since I'm
not a linux wireless maintainer, I just want them to be aware of what
they are merging.
Chor Teck Law Aug. 5, 2015, 3:29 p.m. UTC | #20
> -----Original Message-----
> From: Maxime Bizon [mailto:mbizon@freebox.fr]
> Sent: Wednesday, August 05, 2015 7:22 AM
> 
> On Wednesday 05 Aug 2015 à 08:39:28 (+0000), Chor Teck Law wrote:
> 
> > Last words, The mwl8k series had its history, since 2009 with the
> > Topdog series (Beyond our scope to comment). The mwlwifi have been
> > developed over past year or so for the Avastar AP chips, being
> 
> incorrect, mwl8k also support Avastar AP (88W8764)
> 

OK, I had used a wrong word. The mwlwifi currently supports 88W8864 and 88W8897 as softmac/hostmac. 

> one of the mwlwifi feedback was that the Kconfig description was
> inaccurate, and because of overlap with mwifiex, everyone did scratch
> their heads trying to describe what each driver actually supports.
> 

The discussion was around choice of words to distinguish 88W8897. The difference with mwifiex (fullmac vs softmac) and has been explained. 

--
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 Sept. 7, 2015, 10:37 a.m. UTC | #21
Maxime Bizon <mbizon@freebox.fr> writes:

> but from my POV, your pattern is: build new chipset, obsolete previous
> chipset, fork current firmware, make uncompatible changes to both
> firmware & host driver to accomodate new chipset peculiarities.
>
> and eventually, if a customer requests a mainline linux driver, then
> write one... this is what I'd call "dumping"

BTW, this is exactly what I meant with "dumping" as well. Having
multiple drivers with similar functionality is difficult for everyone.
Creating a new driver with new bugs is much worse than making smaller,
incremental changes to the existing driver. And of course code sharing
is always preferred, but not a hard requirement for drivers.
Kalle Valo Sept. 7, 2015, 11:20 a.m. UTC | #22
Chor Teck Law <ctlaw@marvell.com> writes:

>> On Tuesday 04 Aug 2015 à 20:02:23 (+0300), Kalle Valo wrote:
>> 
>> > I haven't looked at the driver myself yet. Do you have any estimates
>> > how much duplication there is?
>> 
>
> I do think that someone interested in this thread should compare the
> two drivers to decide if they want to accept mwlwifi (or we let it
> develop closed door).
>
> You will find the differences with some leverage. To make your
> comparison easier, here is the summary (excluding the patch1-patch6
> improvements as per review feedbacks from Johannes and community):
>
> mwlwifi functions leveraged from mwl8k:
> - 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 (but added with 11ac rate map/reports)
>
> All others are different/new. Including some listed below:
> - Different/new 11ac chipsets, including combo BT devices
> - Added support for 11ac related settings and functions
> - Added support for concurrent AP+STA functionalities with single firmware per chip
> - Additional firmware APIs 
> - Added functions to convert newly supported settings from mac80211 to firmware
> - Different Tx datapath 
> - Different Rx low level datapath (though data structure for receiving is same)
> - Reorganized the files for future scalability and features addition (more to come)
> - Addressed all (sort of) review feedbacks from patch1-patch6 of this submission

You have named the new driver as mwlwifi, which is a really generic
name. Does that imply that mwlwifi will start supporting more Marvell
devices in the future? Or will be in the situation that you fork the
driver again and we end up having mwlwifi2, mwlwifi3 and so on?
Chor Teck Law Sept. 11, 2015, 1:43 p.m. UTC | #23
Hi Kalle,

> -----Original Message-----

> From: Kalle Valo [mailto:kvalo@codeaurora.org]

> Sent: Monday, September 07, 2015 4:20 AM

> 

> > Chor Teck Law <ctlaw@marvell.com> writes:

> 

> >> On Tuesday 04 Aug 2015 à 20:02:23 (+0300), Kalle Valo wrote:

> >>

> >> > I haven't looked at the driver myself yet. Do you have any

> >> > estimates how much duplication there is?

> >>

> >

> > I do think that someone interested in this thread should compare the

> > two drivers to decide if they want to accept mwlwifi (or we let it

> > develop closed door).

> >

> > You will find the differences with some leverage. To make your

> > comparison easier, here is the summary (excluding the patch1-patch6

> > improvements as per review feedbacks from Johannes and community):

> >

> > mwlwifi functions leveraged from mwl8k:

> > - 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 (but added with 11ac rate

> > map/reports)

> >

> > All others are different/new. Including some listed below:

> > - Different/new 11ac chipsets, including combo BT devices

> > - Added support for 11ac related settings and functions

> > - Added support for concurrent AP+STA functionalities with single

> > firmware per chip

> > - Additional firmware APIs

> > - Added functions to convert newly supported settings from mac80211

> to

> > firmware

> > - Different Tx datapath

> > - Different Rx low level datapath (though data structure for

> receiving

> > is same)

> > - Reorganized the files for future scalability and features addition

> > (more to come)

> > - Addressed all (sort of) review feedbacks from patch1-patch6 of this

> > submission

> 

> You have named the new driver as mwlwifi, which is a really generic

> name. Does that imply that mwlwifi will start supporting more Marvell

> devices in the future? 


Yes, that is the plan on same generation of 11ac chipset that support host-based mac80211.

> Or will be in the situation that you fork the

> driver again and we end up having mwlwifi2, mwlwifi3 and so on?

> 


We do intend to add interface HAL spec version to mwlwifi in case the lower HAL becomes unavoidably changed in future, so that we can continue to maintain mwlwifi. But for different generation of future chipsets/new technology, when the hardware no longer permit us to use same driver, we will have to re-evaluate our options. We cannot guarantee something way out into the future.

Thanks
Chor-Teck
Kalle Valo Sept. 29, 2015, 9:03 a.m. UTC | #24
Chor Teck Law <ctlaw@marvell.com> writes:

>> You have named the new driver as mwlwifi, which is a really generic
>> name. Does that imply that mwlwifi will start supporting more Marvell
>> devices in the future? 
>
> Yes, that is the plan on same generation of 11ac chipset that support
> host-based mac80211.
>
>> Or will be in the situation that you fork the
>> driver again and we end up having mwlwifi2, mwlwifi3 and so on?
>> 
>
> We do intend to add interface HAL spec version to mwlwifi in case the
> lower HAL becomes unavoidably changed in future, so that we can
> continue to maintain mwlwifi.

Very good.

> But for different generation of future chipsets/new technology, when
> the hardware no longer permit us to use same driver, we will have to
> re-evaluate our options. We cannot guarantee something way out into
> the future.

Yeah, that's understandable. But the community really dislikes having
duplicated code/functionality, so please keep this in mind.

If you can submit the next version soon we could try to get this driver
to 4.4. We have few more weeks.
Kalle Valo Sept. 29, 2015, 10:11 a.m. UTC | #25
Kalle Valo <kvalo@codeaurora.org> writes:

> Chor Teck Law <ctlaw@marvell.com> writes:
>
>> But for different generation of future chipsets/new technology, when
>> the hardware no longer permit us to use same driver, we will have to
>> re-evaluate our options. We cannot guarantee something way out into
>> the future.
>
> Yeah, that's understandable. But the community really dislikes having
> duplicated code/functionality, so please keep this in mind.
>
> If you can submit the next version soon we could try to get this driver
> to 4.4. We have few more weeks.

I forgot to ask about the firmware image for this driver. Is that
available in linux-firmware.git?
David Lin Sept. 30, 2015, 2:05 a.m. UTC | #26
> 
> Kalle Valo <kvalo@codeaurora.org> writes:
> 
> > Chor Teck Law <ctlaw@marvell.com> writes:
> >
> >> But for different generation of future chipsets/new technology, when
> >> the hardware no longer permit us to use same driver, we will have to
> >> re-evaluate our options. We cannot guarantee something way out into
> >> the future.
> >
> > Yeah, that's understandable. But the community really dislikes having
> > duplicated code/functionality, so please keep this in mind.
> >
> > If you can submit the next version soon we could try to get this
> > driver to 4.4. We have few more weeks.
> 
> I forgot to ask about the firmware image for this driver. Is that available in
> linux-firmware.git?
>

I had already sent firmware patch for linux-firmware.git. However, it looks like it had not yet been added to linux-firmware.git. After sending out Patch v7, I will check to see if this patch can be added to linux-firmware.git.

> --
> Kalle Valo

Thanks,
David

From: David Lin <dlin@...>
Subject: [PATCH] Add FW binary used by mwlwifi driver.
Newsgroups: gmane.linux.kernel.wireless.general
Date: 2015-08-22 03:54:38 GMT (5 weeks, 3 days, 22 hours and 7 minutes ago)
Signed-off-by: David Lin <dlin-eYqpPyKDWXRBDgjK7y7TUQ <at> public.gmane.org>
---
 WHENCE              |  12 ++++++++++++
 mwlwifi/88W8864.bin | Bin 0 -> 116356 bytes
 mwlwifi/88W8897.bin | Bin 0 -> 489084 bytes
 3 files changed, 12 insertions(+)
 mode change 100644 => 100755 WHENCE
 create mode 100755 mwlwifi/88W8864.bin
 create mode 100755 mwlwifi/88W8897.bin
--
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
diff mbox

Patch

diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index a63ab2e..1c60845 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -284,5 +284,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/mwlwifi/Kconfig"
 
 endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 6b9e729..1fe0f0d 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -62,3 +62,5 @@  obj-$(CONFIG_BRCMSMAC)	+= brcm80211/
 
 obj-$(CONFIG_CW1200)	+= cw1200/
 obj-$(CONFIG_RSI_91X)	+= rsi/
+
+obj-$(CONFIG_MWLWIFI)	+= mwlwifi/
diff --git a/drivers/net/wireless/mwlwifi/Kconfig b/drivers/net/wireless/mwlwifi/Kconfig
new file mode 100644
index 0000000..3732223
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/Kconfig
@@ -0,0 +1,24 @@ 
+config MWLWIFI
+	tristate "Marvell Wireless Wi-Fi driver (mwlwifi)"
+	depends on PCI && MAC80211
+	select FW_LOADER
+	select OF
+	---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/mwlwifi/MAINTAINERS b/drivers/net/wireless/mwlwifi/MAINTAINERS
new file mode 100644
index 0000000..5706ce9
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/MAINTAINERS
@@ -0,0 +1,5 @@ 
+MARVELL MWLWIFI WIRELESS DRIVER
+M:	David Lin <dlin@marvell.com>
+L:	linux-wireless@vger.kernel.org
+S:	Maintained
+F:	drivers/net/wireless/mwlwifi/
diff --git a/drivers/net/wireless/mwlwifi/Makefile b/drivers/net/wireless/mwlwifi/Makefile
new file mode 100644
index 0000000..49bd311
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/Makefile
@@ -0,0 +1,11 @@ 
+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
+
+ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/net/wireless/mwlwifi/dev.h b/drivers/net/wireless/mwlwifi/dev.h
new file mode 100644
index 0000000..9289a1d
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/dev.h
@@ -0,0 +1,435 @@ 
+/*
+ * 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/interrupt.h>
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <net/mac80211.h>
+
+/* 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 definitio 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_QUEUE_EMPTY       BIT(10)
+#define MACREG_A2HRIC_BIT_QUEUE_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_QUEUE_EMPTY | \
+			     MACREG_A2HRIC_BA_WATCHDOG | \
+			     MACREG_A2HRIC_CONSEC_TXFAIL)
+
+#define MACREG_A2HRIC_BIT_MASK      ISR_SRC_BITS
+
+/* Bit definitio 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 {
+	char *part_name;
+	char *fw_image;
+};
+
+struct mwl_tx_pwr_tbl {
+	u8 channel;
+	u8 setcap;
+	u16 tx_power[SYSADPT_TX_POWER_LEVEL_TOTAL];
+	u8 cdd;                      /* 0: off, 1: on */
+	u16 txantenna2;
+};
+
+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;
+	u8 *sta_info;
+	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;
+};
+
+struct mwl_priv {
+	struct ieee80211_hw *hw;
+	const 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];
+	u32 cdd;                     /* 0: off, 1: on */
+	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       */
+	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;
+	int txq_limit;
+	bool is_tx_schedule;
+	int recv_limit;
+	bool is_rx_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;
+};
+
+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];
+	u16 ie_wmm_len;               /* Keep WMM IE */
+	u8 *ie_wmm_ptr;
+	u16 ie_rsn_len;               /* Keep WPA IE */
+	u8 *ie_rsn_ptr;
+	u16 ie_rsn48_len;             /* Keep WPA2 IE */
+	u8 *ie_rsn48_ptr;
+	u16 ie_ht_len;                /* Keep HT IE */
+	u8 *ie_ht_ptr;
+	u8 ie_list_ht[148];
+	u16 ie_vht_len;               /* Keep VHT IE */
+	u8 *ie_vht_ptr;
+	u8 ie_list_vht[24];
+};
+
+struct mwl_vif {
+	struct list_head list;
+	struct ieee80211_vif *vif;
+	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 */
+	bool is_sta;
+	struct beacon_info beacon_info;
+	u16 iv16;
+	u32 iv32;
+	s8 keyidx;
+};
+
+struct mwl_tx_info {
+	u32 start_time;
+	u32 pkts;
+};
+
+struct mwl_amsdu_frag {
+	struct sk_buff *skb;
+	u8 pad;
+	u8 *cur_pos;
+	u8 num;
+	u32 jiffies;
+};
+
+struct mwl_amsdu_ctrl {
+	struct mwl_amsdu_frag frag[SYSADPT_TX_WMM_QUEUES];
+	u8 cap;
+};
+
+struct mwl_sta {
+	struct list_head list;
+	struct ieee80211_sta *sta;
+	bool is_ampdu_allowed;
+	struct mwl_tx_info tx_stats[MWL_MAX_TID];
+	bool is_amsdu_allowed;
+	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 *sta;
+	void *vif;
+	void *k_conf;
+	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;
+}
+
+#endif /* _dev_h_ */
diff --git a/drivers/net/wireless/mwlwifi/fwcmd.c b/drivers/net/wireless/mwlwifi/fwcmd.c
new file mode 100644
index 0000000..1b6539d
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/fwcmd.c
@@ -0,0 +1,2278 @@ 
+/*
+ * 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         10000
+#define MAX_WAIT_GET_HW_SPECS_ITERATONS         3
+
+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)
+{
+	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_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_SET_FIXED_RATE, "SetFixedRate" },
+		{ HOSTCMD_CMD_SET_IES, "SetInformationElements" },
+		{ HOSTCMD_CMD_SET_RATE_ADAPT_MODE, "SetRateAdaptationMode" },
+		{ HOSTCMD_CMD_SET_MAC_ADDR, "SetMacAddr" },
+		{ 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_DWDS_ENABLE, "DwdsEnable" },
+		{ HOSTCMD_CMD_FW_FLUSH_TIMER, "FwFlushTimer" },
+		{ HOSTCMD_CMD_SET_CDD, "SetCDD" },
+	};
+
+	max_entries = sizeof(cmds) / sizeof(cmds[0]);
+
+	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 {
+		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));
+		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\n");
+			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(&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(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	priv->radio_on = enable;
+
+	spin_unlock(&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(&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(&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(&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(&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(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+		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;
+
+		wiphy_info(priv->hw->wiphy,
+			   "wmm:%d, rsn:%d, rsn48:%d, ht:%d, vht:%d\n",
+			   beacon_info->ie_wmm_len,
+			   beacon_info->ie_rsn_len,
+			   beacon_info->ie_rsn48_len,
+			   beacon_info->ie_ht_len,
+			   beacon_info->ie_vht_len);
+	}
+}
+
+static int mwl_fwcmd_set_ies(struct mwl_priv *priv, struct mwl_vif *mwl_vif)
+{
+	struct hostcmd_cmd_set_ies *pcmd;
+
+	if (!mwl_vif->beacon_info.valid)
+		return -EINVAL;
+
+	if (mwl_vif->beacon_info.ie_ht_len > sizeof(pcmd->ie_list_ht))
+		goto einval;
+
+	if (mwl_vif->beacon_info.ie_vht_len > sizeof(pcmd->ie_list_vht))
+		goto einval;
+
+	pcmd = (struct hostcmd_cmd_set_ies *)&priv->pcmd_buf[0];
+
+	spin_lock(&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);
+
+	pcmd->ie_list_len_ht = cpu_to_le16(mwl_vif->beacon_info.ie_ht_len);
+	memcpy(pcmd->ie_list_ht, mwl_vif->beacon_info.ie_ht_ptr,
+	       mwl_vif->beacon_info.ie_ht_len);
+
+	pcmd->ie_list_len_vht = cpu_to_le16(mwl_vif->beacon_info.ie_vht_len);
+	memcpy(pcmd->ie_list_vht, mwl_vif->beacon_info.ie_vht_ptr,
+	       mwl_vif->beacon_info.ie_vht_len);
+
+	if (priv->chip_type == MWL8897) {
+		pcmd->ie_list_len_proprietary =
+			cpu_to_le16(mwl_vif->beacon_info.ie_wmm_len);
+		memcpy(pcmd->ie_list_proprietary,
+		       mwl_vif->beacon_info.ie_wmm_ptr,
+		       mwl_vif->beacon_info.ie_wmm_len);
+	}
+
+	if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_IES)) {
+		spin_unlock(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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(&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(&priv->fwcmd_lock);
+		wiphy_err(priv->hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+
+	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;
+
+	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;
+
+	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;
+	struct hostcmd_cmd_get_hw_spec *pcmd;
+	int retry;
+	int i;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_get_hw_spec *)&priv->pcmd_buf[0];
+
+	spin_lock(&priv->fwcmd_lock);
+
+	wiphy_debug(hw->wiphy, "pcmd = %p\n", pcmd);
+	memset(pcmd, 0x00, sizeof(*pcmd));
+	memset(&pcmd->permanent_addr[0], 0xff, ETH_ALEN);
+	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(&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;
+#if SYSADPT_NUM_OF_DESC_DATA > 3
+	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+		priv->desc_data[i].wcb_base =
+			le32_to_cpu(pcmd->wcb_base[i - 1]) & 0x0000ffff;
+#endif
+	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(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_hw_specs(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_set_hw_spec *pcmd;
+	int i;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_hw_spec *)&priv->pcmd_buf[0];
+
+	spin_lock(&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);
+#if SYSADPT_NUM_OF_DESC_DATA > 3
+	for (i = 1; i < SYSADPT_TOTAL_TX_QUEUES; i++)
+		pcmd->wcb_base[i] =
+			cpu_to_le32(priv->desc_data[i].pphys_tx_ring);
+#endif
+	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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_get_stat(struct ieee80211_hw *hw,
+		       struct ieee80211_low_level_stats *stats)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_802_11_get_stat *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_802_11_get_stat *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&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(&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;
+	int rc;
+
+	priv = hw->priv;
+
+	priv->radio_short_preamble = short_preamble;
+	rc = mwl_fwcmd_802_11_radio_control(priv, true, true);
+
+	return rc;
+}
+
+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;
+	int reduce_val = 0;
+	u16 band = 0, width = 0, sub_ch = 0;
+	u16 maxtxpow[SYSADPT_TX_POWER_LEVEL_TOTAL];
+	int i, tmp;
+	int rc;
+
+	priv = hw->priv;
+
+	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;
+	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;
+
+	priv = hw->priv;
+
+	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;
+	struct hostcmd_cmd_802_11_rf_antenna *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_802_11_rf_antenna *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_broadcast_ssid_enable *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_broadcast_ssid_enable *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct hostcmd_cmd_set_rf_channel *pcmd;
+	u32 chnl_flags, freq_band, chnl_width, act_primary;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_rf_channel *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&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(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_aid *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_aid *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_infra_mode(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif)
+{
+	struct mwl_priv *priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_infra_mode *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_infra_mode *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_rts_threshold(struct ieee80211_hw *hw, int threshold)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_802_11_rts_thsd *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_802_11_rts_thsd *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct hostcmd_cmd_set_edca_params *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_edca_params *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_wmm_mode(struct ieee80211_hw *hw, bool enable)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_set_wmm_mode *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_wmm_mode *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_use_fixed_rate(struct ieee80211_hw *hw, int mcast, int mgmt)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_set_fixed_rate *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_fixed_rate *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_rate_adapt_mode(struct ieee80211_hw *hw, u16 mode)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_set_rate_adapt_mode *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_rate_adapt_mode *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_mac_addr *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_get_watchdog_bitmap(struct ieee80211_hw *hw, u8 *bitmap)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_get_watchdog_bitmap *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_get_watchdog_bitmap *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	*bitmap = pcmd->watchdog_bitmap;
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_mac_addr *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_mac_addr *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_bss_start(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif, bool enable)
+{
+	struct mwl_priv *priv;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_bss_start *pcmd;
+
+	priv = hw->priv;
+	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(&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(&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(&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;
+	struct mwl_vif *mwl_vif;
+
+	priv = hw->priv;
+	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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_new_stn *pcmd;
+	u32 rates;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+	spin_lock(&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 (mwl_vif->is_sta) {
+		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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (mwl_vif->is_sta) {
+		ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+
+		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+			spin_unlock(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_new_stn *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_new_stn *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_new_stn *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if  (mwl_vif->is_sta) {
+		ether_addr_copy(pcmd->mac_addr, mwl_vif->sta_mac);
+
+		if (mwl_fwcmd_exec_cmd(priv, HOSTCMD_CMD_SET_NEW_STN)) {
+			spin_unlock(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_apmode(struct ieee80211_hw *hw, u8 apmode)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_set_apmode *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_apmode *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_update_encryption *pcmd;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_update_encryption *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (mwl_vif->is_sta) {
+		if (memcmp(mwl_vif->bssid, addr, ETH_ALEN) == 0)
+			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(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_key *pcmd;
+	int rc;
+	int keymlen;
+	u32 action;
+	u8 idx;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&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(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (mwl_vif->is_sta) {
+		if (memcmp(mwl_vif->bssid, addr, ETH_ALEN) == 0)
+			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(&priv->fwcmd_lock);
+			wiphy_err(hw->wiphy, "failed execution\n");
+			return -EIO;
+		}
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_set_key *pcmd;
+	int rc;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_set_key *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_bastream *pcmd;
+	u32 ba_flags, ba_type, ba_direction;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+	spin_lock(&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);
+	memcpy(&pcmd->ba_info.create_params.peer_mac_addr[0],
+	       stream->sta->addr, ETH_ALEN);
+	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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (pcmd->cmd_hdr.result != 0) {
+		spin_unlock(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "result error\n");
+		return -EINVAL;
+	}
+
+	spin_unlock(&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;
+	struct mwl_vif *mwl_vif;
+	struct hostcmd_cmd_bastream *pcmd;
+	u32 ba_flags, ba_type, ba_direction;
+
+	priv = hw->priv;
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+	spin_lock(&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);
+	memcpy(&pcmd->ba_info.create_params.peer_mac_addr[0],
+	       stream->sta->addr, ETH_ALEN);
+	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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	if (pcmd->cmd_hdr.result != 0) {
+		spin_unlock(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "result error\n");
+		return -EINVAL;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_destroy_ba(struct ieee80211_hw *hw,
+			 u8 idx)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_bastream *pcmd;
+	u32 ba_flags, ba_type, ba_direction;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_bastream *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&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;
+	struct mwl_ampdu_stream *stream;
+	int i;
+
+	priv = hw->priv;
+
+	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;
+}
+
+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;
+	struct mwl_ampdu_stream *stream;
+	int i;
+
+	priv = hw->priv;
+
+	for (i = 0; i < SYSADPT_TX_AMPDU_QUEUES; i++) {
+		stream = &priv->ampdu[i];
+
+		if (stream->state == AMPDU_NO_STREAM)
+			continue;
+
+		if (!memcmp(stream->sta->addr, addr, ETH_ALEN) &&
+		    stream->tid == tid)
+			return stream;
+	}
+
+	return NULL;
+}
+
+bool mwl_fwcmd_ampdu_allowed(struct ieee80211_sta *sta, u8 tid)
+{
+	struct mwl_sta *sta_info;
+	struct mwl_tx_info *tx_stats;
+
+	sta_info = mwl_dev_get_sta(sta);
+
+	BUG_ON(tid >= SYSADPT_MAX_TID);
+
+	tx_stats = &sta_info->tx_stats[tid];
+
+	return (sta_info->is_ampdu_allowed &&
+		tx_stats->pkts > SYSADPT_AMPDU_PACKET_THRESHOLD);
+}
+
+int mwl_fwcmd_set_dwds_stamode(struct ieee80211_hw *hw, bool enable)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_dwds_enable *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_dwds_enable *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_fw_flush_timer(struct ieee80211_hw *hw, u32 value)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_fw_flush_timer *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_fw_flush_timer *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
+
+int mwl_fwcmd_set_cdd(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv;
+	struct hostcmd_cmd_set_cdd *pcmd;
+
+	priv = hw->priv;
+
+	pcmd = (struct hostcmd_cmd_set_cdd *)&priv->pcmd_buf[0];
+
+	spin_lock(&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(&priv->fwcmd_lock);
+		wiphy_err(hw->wiphy, "failed execution\n");
+		return -EIO;
+	}
+
+	spin_unlock(&priv->fwcmd_lock);
+
+	return 0;
+}
diff --git a/drivers/net/wireless/mwlwifi/fwcmd.h b/drivers/net/wireless/mwlwifi/fwcmd.h
new file mode 100644
index 0000000..6b8ec74
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/fwcmd.h
@@ -0,0 +1,175 @@ 
+/*
+ * 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
+
+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_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_use_fixed_rate(struct ieee80211_hw *hw,
+			     int mcast, int mgmt);
+
+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);
+
+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_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);
+
+#endif /* _fwcmd_h_ */
diff --git a/drivers/net/wireless/mwlwifi/fwdl.c b/drivers/net/wireless/mwlwifi/fwdl.c
new file mode 100644
index 0000000..d394efd
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/fwdl.c
@@ -0,0 +1,183 @@ 
+/*
+ * 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"
+#include "fwdl.h"
+
+#define FW_DOWNLOAD_BLOCK_SIZE          256
+#define FW_CHECK_MSECS                  1
+
+#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;
+	const struct firmware *fw;
+	u32 curr_iteration = 0;
+	u32 size_fw_downloaded = 0;
+	u32 int_code = 0;
+	u32 len = 0;
+
+	priv = hw->priv;
+	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;
+	do {
+		curr_iteration--;
+		writel(HOSTCMD_SOFTAP_MODE, priv->iobase1 + MACREG_REG_GEN_PTR);
+		mdelay(FW_CHECK_MSECS);
+		int_code = readl(priv->iobase1 + MACREG_REG_INT_CODE);
+		if (!(curr_iteration % 0xff))
+			wiphy_err(hw->wiphy, "%x;", int_code);
+	} while ((curr_iteration) &&
+		 (int_code != HOSTCMD_SOFTAP_FWRDY_SIGNATURE));
+
+	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/mwlwifi/fwdl.h b/drivers/net/wireless/mwlwifi/fwdl.h
new file mode 100644
index 0000000..a6f3a1fb
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/fwdl.h
@@ -0,0 +1,27 @@ 
+/*
+ * 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_
+
+#include <net/mac80211.h>
+
+int mwl_fwdl_download_firmware(struct ieee80211_hw *hw);
+
+#endif /* _fwdl_h_ */
diff --git a/drivers/net/wireless/mwlwifi/hostcmd.h b/drivers/net/wireless/mwlwifi/hostcmd.h
new file mode 100644
index 0000000..d9db0ee
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/hostcmd.h
@@ -0,0 +1,753 @@ 
+/*
+ * 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_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_SET_FIXED_RATE              0x0126
+#define HOSTCMD_CMD_SET_IES                     0x0127
+#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 /* pre-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_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_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_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_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_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_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_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/mwlwifi/isr.c b/drivers/net/wireless/mwlwifi/isr.c
new file mode 100644
index 0000000..59d0ac3
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/isr.c
@@ -0,0 +1,148 @@ 
+/*
+ * 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;
+	void __iomem *int_status_mask;
+	unsigned int int_status, clr_status;
+	u32 status;
+
+	priv = hw->priv;
+
+	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_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);
+	struct ieee80211_hw *hw = priv->hw;
+	u32 status;
+
+	rc = mwl_fwcmd_get_watchdog_bitmap(priv->hw, &bitmap);
+
+	if (rc)
+		goto done;
+
+	spin_lock(&priv->stream_lock);
+
+	/* the bitmap is the hw queue number.  Map it to the ampdu queue. */
+	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);
+				spin_unlock(&priv->stream_lock);
+				mwl_fwcmd_destroy_ba(hw, stream_index);
+				spin_lock(&priv->stream_lock);
+			}
+		} 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(&priv->stream_lock);
+				mwl_fwcmd_destroy_ba(hw, stream_index);
+				spin_lock(&priv->stream_lock);
+			}
+		}
+	}
+
+	spin_unlock(&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/mwlwifi/isr.h b/drivers/net/wireless/mwlwifi/isr.h
new file mode 100644
index 0000000..2598115
--- /dev/null
+++ b/drivers/net/wireless/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/mwlwifi/mac80211.c b/drivers/net/wireless/mwlwifi/mac80211.c
new file mode 100644
index 0000000..67a2693
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/mac80211.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 mac80211 related functions. */
+
+#include <linux/etherdevice.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwcmd.h"
+#include "tx.h"
+#include "mac80211.h"
+#include "isr.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;
+
+	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;
+	int rc;
+
+	priv = hw->priv;
+
+	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");
+		return rc;
+	}
+	priv->irq = priv->pdev->irq;
+
+	/* Enable TX reclaim and RX tasklets. */
+	tasklet_enable(&priv->tx_task);
+	tasklet_enable(&priv->rx_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_set_dwds_stamode(hw, true);
+	if (rc)
+		goto fwcmd_fail;
+	rc = mwl_fwcmd_set_fw_flush_timer(hw, 0);
+	if (rc)
+		goto fwcmd_fail;
+
+	ieee80211_wake_queues(hw);
+	return 0;
+
+fwcmd_fail:
+	mwl_fwcmd_int_disable(hw);
+	free_irq(priv->pdev->irq, hw);
+	priv->irq = -1;
+	tasklet_disable(&priv->tx_task);
+	tasklet_disable(&priv->rx_task);
+
+	return rc;
+}
+
+static void mwl_mac80211_stop(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv;
+
+	priv = hw->priv;
+
+	mwl_fwcmd_radio_disable(hw);
+
+	ieee80211_stop_queues(hw);
+
+	/* Disable interrupts */
+	mwl_fwcmd_int_disable(hw);
+
+	if (priv->irq != -1) {
+		free_irq(priv->pdev->irq, hw);
+		priv->irq = -1;
+	}
+
+	cancel_work_sync(&priv->watchdog_ba_handle);
+
+	/* Disable TX reclaim and RX tasklets. */
+	tasklet_disable(&priv->tx_task);
+	tasklet_disable(&priv->rx_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:
+		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;
+	}
+
+	/* Setup driver private area. */
+	mwl_vif = mwl_dev_get_vif(vif);
+	memset(mwl_vif, 0, sizeof(*mwl_vif));
+	mwl_vif->vif = vif;
+	mwl_vif->macid = macid;
+	mwl_vif->seqno = 0;
+	mwl_vif->is_hw_crypto_enabled = false;
+	mwl_vif->is_sta = false;
+	mwl_vif->beacon_info.valid = false;
+	mwl_vif->iv16 = 1;
+	mwl_vif->iv32 = 0;
+	mwl_vif->keyidx = 0;
+
+	if (vif->type == NL80211_IFTYPE_STATION) {
+		ether_addr_copy(mwl_vif->sta_mac, vif->addr);
+		mwl_vif->is_sta = true;
+		mwl_fwcmd_bss_start(hw, vif, true);
+		mwl_fwcmd_set_infra_mode(hw, vif);
+		mwl_fwcmd_set_mac_addr_client(hw, vif, vif->addr);
+	}
+
+	if (vif->type == NL80211_IFTYPE_AP) {
+		ether_addr_copy(mwl_vif->bssid, vif->addr);
+		mwl_fwcmd_set_new_stn_add_self(hw, vif);
+	}
+
+	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 mwl_vif *vif)
+{
+	int num;
+	struct sk_buff *skb, *tmp;
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_tx_ctrl *tx_ctrl;
+
+	if (!priv->macids_used)
+		return;
+
+	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) {
+				__skb_unlink(skb, &priv->txq[num]);
+				dev_kfree_skb_any(skb);
+			}
+		}
+		spin_unlock_bh(&priv->txq[num].lock);
+	}
+
+	priv->macids_used &= ~(1 << vif->macid);
+	spin_lock_bh(&priv->vif_lock);
+	list_del(&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;
+	struct mwl_vif *mwl_vif;
+
+	mwl_vif = mwl_dev_get_vif(vif);
+
+	if (vif->type == NL80211_IFTYPE_STATION)
+		mwl_fwcmd_remove_mac_addr(hw, vif, vif->addr);
+
+	if (vif->type == NL80211_IFTYPE_AP)
+		mwl_fwcmd_set_new_stn_del(hw, vif, vif->addr);
+
+	mwl_mac80211_remove_vif(priv, mwl_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);
+			rate = mwl_rates_24[0].hw_value;
+		} else if (conf->chandef.chan->band == IEEE80211_BAND_5GHZ) {
+			mwl_fwcmd_set_apmode(hw, AP_MODE_11AC);
+			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)
+{
+	wiphy_debug(hw->wiphy, "interface: %d, change: 0x%x\n",
+		    vif->type, changed);
+
+	if (vif->type == NL80211_IFTYPE_STATION)
+		mwl_mac80211_bss_info_changed_sta(hw, vif, info, changed);
+
+	if (vif->type == NL80211_IFTYPE_AP)
+		mwl_mac80211_bss_info_changed_ap(hw, vif, info, changed);
+}
+
+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 (mwl_vif->is_sta)
+			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 (!mwl_vif->is_sta)
+					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));
+	sta_info->sta = sta;
+	if (sta->ht_cap.ht_supported) {
+		sta_info->is_ampdu_allowed = true;
+		sta_info->is_amsdu_allowed = true;
+		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(&priv->sta_lock);
+	list_add_tail(&sta_info->list, &priv->sta_list);
+	spin_unlock(&priv->sta_lock);
+
+	if (mwl_vif->is_sta)
+		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;
+	struct mwl_sta *sta_info;
+	int num;
+	struct sk_buff *skb, *tmp;
+	struct ieee80211_tx_info *tx_info;
+	struct mwl_tx_ctrl *tx_ctrl;
+	int rc;
+
+	sta_info = mwl_dev_get_sta(sta);
+
+	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) {
+				__skb_unlink(skb, &priv->txq[num]);
+				dev_kfree_skb_any(skb);
+			}
+		}
+		spin_unlock_bh(&priv->txq[num].lock);
+	}
+
+	rc = mwl_fwcmd_set_new_stn_del(hw, vif, sta->addr);
+
+	spin_lock(&priv->sta_lock);
+	list_del(&sta_info->list);
+	spin_unlock(&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;
+
+	BUG_ON(queue > SYSADPT_TX_WMM_QUEUES - 1);
+
+	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)
+{
+	int i, 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(&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:
+		/* By the time we get here the hw queues may contain outgoing
+		 * packets for this RA/TID that are not part of this BA
+		 * session.  The hw will assign sequence numbers to these
+		 * packets as they go out.  So if we query the hw for its next
+		 * sequence number and use that for the SSN here, it may end up
+		 * being wrong, which will lead to sequence number mismatch at
+		 * the recipient.  To avoid this, we reset the sequence number
+		 * to O for the first MPDU in this BA stream.
+		 */
+		*ssn = 0;
+
+		if (!stream) {
+			/* This means that somebody outside this driver called
+			 * ieee80211_start_tx_ba_session.  This is unexpected
+			 * because we do our own rate control.  Just warn and
+			 * move on.
+			 */
+			stream = mwl_fwcmd_add_stream(hw, sta, tid);
+		}
+
+		if (!stream) {
+			wiphy_warn(hw->wiphy, "no stream found\n");
+			rc = -EBUSY;
+			break;
+		}
+
+		stream->state = AMPDU_STREAM_IN_PROGRESS;
+
+		/* Release the lock before we do the time consuming stuff */
+		spin_unlock(&priv->stream_lock);
+
+		for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) {
+			/* Check if link is still valid */
+			if (!sta_info->is_ampdu_allowed) {
+				spin_lock(&priv->stream_lock);
+				mwl_fwcmd_remove_stream(hw, stream);
+				spin_unlock(&priv->stream_lock);
+				wiphy_warn(hw->wiphy,
+					   "link is no valid now\n");
+				return -EBUSY;
+			}
+
+			rc = mwl_fwcmd_check_ba(hw, stream, vif);
+
+			if (!rc)
+				break;
+
+			mdelay(1000);
+		}
+
+		spin_lock(&priv->stream_lock);
+
+		if (rc) {
+			mwl_fwcmd_remove_stream(hw, stream);
+			wiphy_err(hw->wiphy, "error code: %d\n", rc);
+			rc = -EBUSY;
+			break;
+		}
+
+		ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
+		break;
+	case IEEE80211_AMPDU_TX_STOP_CONT:
+	case IEEE80211_AMPDU_TX_STOP_FLUSH:
+	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
+		if (stream) {
+			if (stream->state == AMPDU_STREAM_ACTIVE) {
+				idx = stream->idx;
+				spin_unlock(&priv->stream_lock);
+				mwl_fwcmd_destroy_ba(hw, idx);
+				spin_lock(&priv->stream_lock);
+			}
+
+			mwl_fwcmd_remove_stream(hw, stream);
+		}
+
+		ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid);
+		break;
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS);
+		spin_unlock(&priv->stream_lock);
+		rc = mwl_fwcmd_create_ba(hw, stream, buf_size, vif);
+		spin_lock(&priv->stream_lock);
+
+		if (!rc) {
+			stream->state = AMPDU_STREAM_ACTIVE;
+		} else {
+			idx = stream->idx;
+			spin_unlock(&priv->stream_lock);
+			mwl_fwcmd_destroy_ba(hw, idx);
+			spin_lock(&priv->stream_lock);
+			mwl_fwcmd_remove_stream(hw, stream);
+		}
+		break;
+	default:
+		rc = -ENOTSUPP;
+	}
+
+	spin_unlock(&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/mwlwifi/mac80211.h b/drivers/net/wireless/mwlwifi/mac80211.h
new file mode 100644
index 0000000..0267dd85
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/mac80211.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 mac80211 related functions. */
+
+#ifndef _mac80211_h_
+#define _mac80211_h_
+
+#include <net/mac80211.h>
+
+extern const struct ieee80211_ops mwl_mac80211_ops;
+
+#endif /* _mac80211_h_ */
diff --git a/drivers/net/wireless/mwlwifi/main.c b/drivers/net/wireless/mwlwifi/main.c
new file mode 100644
index 0000000..119bcf7
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/main.c
@@ -0,0 +1,856 @@ 
+/*
+ * 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>
+#include <linux/moduleparam.h>
+
+#include "sysadpt.h"
+#include "dev.h"
+#include "fwdl.h"
+#include "fwcmd.h"
+#include "tx.h"
+#include "rx.h"
+#include "mac80211.h"
+#include "isr.h"
+
+#define MWL_DESC         "Marvell 802.11ac Wireless Network Driver"
+#define MWL_DEV_NAME     "Marvell 802.11ac Adapter"
+#define MWL_DRV_NAME     KBUILD_MODNAME
+#define MWL_DRV_VERSION	 "10.3.0.6"
+
+#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",
+	},
+	[MWL8897] = {
+		.part_name	= "88W8897",
+		.fw_image	= "mwlwifi/88W8897.bin",
+	},
+};
+
+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) },
+	{ .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;
+	u32 phys_addr = 0;
+	u32 flags;
+	void __iomem *phys_addr1[2];
+	void __iomem *phys_addr2[2];
+
+	pdev = priv->pdev;
+
+	phys_addr = pci_resource_start(pdev, 0);
+	flags = pci_resource_flags(pdev, 0);
+
+	priv->next_bar_num = 1;	/* 32-bit */
+
+	if (flags & 0x04)
+		priv->next_bar_num = 2;	/* 64-bit */
+
+	if (!request_mem_region(phys_addr, pci_resource_len(pdev, 0),
+				MWL_DRV_NAME)) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot reserve PCI memory region 0\n",
+			  MWL_DRV_NAME);
+		goto err_reserve_mem_region_bar0;
+	}
+
+	phys_addr1[0] = ioremap(phys_addr, pci_resource_len(pdev, 0));
+	phys_addr1[1] = NULL;
+
+	priv->iobase0 = phys_addr1[0];
+	if (!priv->iobase0) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot remap PCI memory region 0\n",
+			  MWL_DRV_NAME);
+		goto err_release_mem_region_bar0;
+	}
+
+	wiphy_info(priv->hw->wiphy, "priv->iobase0 = %p\n", priv->iobase0);
+
+	phys_addr = pci_resource_start(pdev, priv->next_bar_num);
+
+	if (!request_mem_region(phys_addr,
+				pci_resource_len(pdev, priv->next_bar_num),
+				MWL_DRV_NAME)) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot reserve PCI memory region 1\n",
+			  MWL_DRV_NAME);
+		goto err_iounmap_iobase0;
+	}
+
+	phys_addr2[0] = ioremap(phys_addr,
+				pci_resource_len(pdev, priv->next_bar_num));
+	phys_addr2[1] = NULL;
+	priv->iobase1 = phys_addr2[0];
+
+	if (!priv->iobase1) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot remap PCI memory region 1\n",
+			  MWL_DRV_NAME);
+		goto err_release_mem_region_bar1;
+	}
+
+	wiphy_info(priv->hw->wiphy, "priv->iobase1 = %p\n", priv->iobase1);
+
+	priv->pcmd_buf =
+		(unsigned short *)dma_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_iounmap_iobase1;
+	}
+
+	wiphy_info(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_iounmap_iobase1:
+
+	iounmap(priv->iobase1);
+
+err_release_mem_region_bar1:
+
+	release_mem_region(pci_resource_start(pdev, 1),
+			   pci_resource_len(pdev, 1));
+
+err_iounmap_iobase0:
+
+	iounmap(priv->iobase0);
+
+err_release_mem_region_bar0:
+
+	release_mem_region(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+
+err_reserve_mem_region_bar0:
+
+	wiphy_err(priv->hw->wiphy, "pci alloc fail\n");
+
+	return -EIO;
+}
+
+static void mwl_free_pci_resource(struct mwl_priv *priv)
+{
+	struct pci_dev *pdev;
+
+	pdev = priv->pdev;
+
+	iounmap(priv->iobase0);
+	iounmap(priv->iobase1);
+	release_mem_region(pci_resource_start(pdev, priv->next_bar_num),
+			   pci_resource_len(pdev, priv->next_bar_num));
+	release_mem_region(pci_resource_start(pdev, 0),
+			   pci_resource_len(pdev, 0));
+	dma_free_coherent(&priv->pdev->dev, CMD_BUF_SIZE,
+			  priv->pcmd_buf, priv->pphys_cmd_buf);
+}
+
+static int mwl_init_firmware(struct mwl_priv *priv, char *fw_name)
+{
+	struct pci_dev *pdev;
+	int rc = 0;
+
+	pdev = priv->pdev;
+
+	rc = request_firmware(&priv->fw_ucode, fw_name, &priv->pdev->dev);
+	if (rc) {
+		wiphy_err(priv->hw->wiphy,
+			  "%s: cannot load firmware image <%s>\n",
+			  MWL_DRV_NAME, fw_name);
+		goto err_load_fw;
+	}
+
+	rc = mwl_fwdl_download_firmware(priv->hw);
+	if (rc) {
+		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:
+
+	release_firmware(priv->fw_ucode);
+
+err_load_fw:
+
+	wiphy_err(priv->hw->wiphy, "firmware init fail\n");
+
+	return rc;
+}
+
+static void mwl_reg_notifier(struct wiphy *wiphy,
+			     struct regulatory_request *request)
+{
+	struct ieee80211_hw *hw;
+	struct mwl_priv *priv;
+	struct property *prop;
+	struct property *fcc_prop = NULL;
+	struct property *etsi_prop = NULL;
+	struct property *specific_prop = NULL;
+	u32 prop_value;
+	int i, j, k;
+
+	hw = (struct ieee80211_hw *)wiphy_priv(wiphy);
+	priv = hw->priv;
+
+	if (priv->pwr_node) {
+		for_each_property_of_node(priv->pwr_node, prop) {
+			if (strcmp(prop->name, "FCC") == 0)
+				fcc_prop = prop;
+			if (strcmp(prop->name, "ETSI") == 0)
+				etsi_prop = prop;
+			if ((prop->name[0] == request->alpha2[0]) &&
+			    (prop->name[1] == request->alpha2[1]))
+				specific_prop = prop;
+		}
+
+		prop = NULL;
+
+		if (specific_prop) {
+			prop = specific_prop;
+		} else {
+			if (request->dfs_region == NL80211_DFS_ETSI)
+				prop = etsi_prop;
+			else
+				prop = fcc_prop;
+		}
+
+		if (prop) {
+			/* Reset the whole table */
+			for (i = 0; i < SYSADPT_MAX_NUM_CHANNELS; i++)
+				memset(&priv->tx_pwr_tbl[i], 0,
+				       sizeof(struct mwl_tx_pwr_tbl));
+
+			/* Load related power table */
+			i = 0;
+			j = 0;
+			while (i < prop->length) {
+				prop_value =
+					be32_to_cpu(*(__be32 *)
+						    (prop->value + i));
+				priv->tx_pwr_tbl[j].channel = prop_value;
+				i += 4;
+				prop_value =
+					be32_to_cpu(*(__be32 *)
+						    (prop->value + i));
+				priv->tx_pwr_tbl[j].setcap = prop_value;
+				i += 4;
+				for (k = 0; k < SYSADPT_TX_POWER_LEVEL_TOTAL;
+				     k++) {
+					prop_value =
+						be32_to_cpu(*(__be32 *)
+							    (prop->value + i));
+					priv->tx_pwr_tbl[j].tx_power[k] =
+						prop_value;
+					i += 4;
+				}
+				prop_value =
+					be32_to_cpu(*(__be32 *)
+						    (prop->value + i));
+				priv->tx_pwr_tbl[j].cdd = prop_value;
+				i += 4;
+				prop_value =
+					be32_to_cpu(*(__be32 *)
+						    (prop->value + i));
+				priv->tx_pwr_tbl[j].txantenna2 = prop_value;
+				i += 4;
+				j++;
+			}
+
+			/* Dump loaded power tabel */
+			wiphy_info(hw->wiphy, "%s: %s\n", dev_name(&wiphy->dev),
+				   prop->name);
+			for (i = 0; i < SYSADPT_MAX_NUM_CHANNELS; i++) {
+				struct mwl_tx_pwr_tbl *pwr_tbl;
+				char disp_buf[64];
+				char *disp_ptr;
+
+				pwr_tbl = &priv->tx_pwr_tbl[i];
+				if (pwr_tbl->channel == 0)
+					break;
+				wiphy_info(hw->wiphy,
+					   "Channel: %d: 0x%x 0x%x 0x%x\n",
+					   pwr_tbl->channel,
+					   pwr_tbl->setcap,
+					   pwr_tbl->cdd,
+					   pwr_tbl->txantenna2);
+				disp_ptr = disp_buf;
+				for (j = 0; j < SYSADPT_TX_POWER_LEVEL_TOTAL;
+				     j++) {
+					disp_ptr +=
+						sprintf(disp_ptr, "%x ",
+							pwr_tbl->tx_power[j]);
+				}
+				wiphy_info(hw->wiphy, "%s\n", disp_buf);
+			}
+		}
+	}
+}
+
+static int mwl_process_of_dts(struct mwl_priv *priv)
+{
+	struct property *prop;
+	u32 prop_value;
+
+	priv->disable_2g = false;
+	priv->disable_5g = false;
+	priv->antenna_tx = ANTENNA_TX_4_AUTO;
+	priv->antenna_rx = ANTENNA_RX_4_AUTO;
+
+	priv->dt_node =
+		of_find_node_by_name(pci_bus_to_OF_node(priv->pdev->bus),
+				     "mwlwifi");
+	if (!priv->dt_node)
+		return -EPERM;
+
+	/* 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");
+
+	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");
+
+	return 0;
+}
+
+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 to 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);
+	priv->txq_limit = SYSADPT_TX_QUEUE_LIMIT;
+	priv->is_tx_schedule = false;
+	priv->recv_limit = SYSADPT_RECEIVE_LIMIT;
+	priv->is_rx_schedule = false;
+
+	spin_lock_init(&priv->tx_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);
+#if SYSADPT_NUM_OF_DESC_DATA > 3
+	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);
+#endif
+	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);
+	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;
+	}
+
+	return rc;
+
+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;
+
+	ieee80211_unregister_hw(hw);
+	mwl_rx_deinit(hw);
+	mwl_tx_deinit(hw);
+	tasklet_kill(&priv->rx_task);
+	tasklet_kill(&priv->tx_task);
+	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;
+	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, 0xffffffff);
+	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;
+	}
+
+	/* hook regulatory domain change notification */
+	hw->wiphy->reg_notifier = mwl_reg_notifier;
+
+	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;
+
+	rc = mwl_alloc_pci_resource(priv);
+	if (rc)
+		goto err_alloc_pci_resource;
+
+	rc = mwl_init_firmware(priv, mwl_chip_tbl[priv->chip_type].fw_image);
+	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 */
+	release_firmware(priv->fw_ucode);
+
+	rc = mwl_process_of_dts(priv);
+	if (rc) {
+		wiphy_err(hw->wiphy, "%s: fail to load dts mwlwifi parameters\n",
+			  MWL_DRV_NAME);
+		goto err_process_of_dts;
+	}
+
+	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;
+	}
+
+	return rc;
+
+err_wl_init:
+err_process_of_dts:
+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);
+	mwl_free_pci_resource(priv);
+	pci_set_drvdata(pdev, NULL);
+	ieee80211_free_hw(hw);
+	pci_disable_device(pdev);
+}
+
+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/mwlwifi/rx.c b/drivers/net/wireless/mwlwifi/rx.c
new file mode 100644
index 0000000..f1e9b27
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/rx.c
@@ -0,0 +1,519 @@ 
+/*
+ * 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/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;
+			}
+
+			if (skb_linearize(rx_hndl->psk_buff)) {
+				dev_kfree_skb_any(rx_hndl->psk_buff);
+				rx_hndl->psk_buff = NULL;
+				wiphy_err(priv->hw->wiphy,
+					  "need linearize memory\n");
+				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);
+			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;
+
+			if (skb_shinfo(rx_hndl->psk_buff)->nr_frags)
+				skb_shinfo(rx_hndl->psk_buff)->nr_frags = 0;
+
+			if (skb_shinfo(rx_hndl->psk_buff)->frag_list)
+				skb_shinfo(rx_hndl->psk_buff)->frag_list = NULL;
+
+			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 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 (memcmp(bssid, mwl_vif->bssid, ETH_ALEN) == 0) {
+			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 int mwl_rx_refill(struct mwl_priv *priv, struct mwl_rx_hndl *rx_hndl)
+{
+	struct mwl_desc_data *desc;
+
+	desc = &priv->desc_data[0];
+
+	rx_hndl->psk_buff = dev_alloc_skb(desc->rx_buf_size);
+
+	if (!rx_hndl->psk_buff)
+		goto nomem;
+
+	if (skb_linearize(rx_hndl->psk_buff)) {
+		dev_kfree_skb_any(rx_hndl->psk_buff);
+		wiphy_err(priv->hw->wiphy, "need linearize memory\n");
+		goto nomem;
+	}
+
+	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);
+	rx_hndl->pdesc->pphys_buff_data =
+		cpu_to_le32(pci_map_single(priv->pdev,
+					   rx_hndl->psk_buff->data,
+					   desc->rx_buf_size,
+					   PCI_DMA_BIDIRECTIONAL));
+
+	return 0;
+
+nomem:
+
+	wiphy_err(priv->hw->wiphy, "no memory\n");
+
+	return -ENOMEM;
+}
+
+int mwl_rx_init(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv;
+	int rc;
+
+	priv = hw->priv;
+
+	rc = mwl_rx_ring_alloc(priv);
+	if (rc) {
+		wiphy_err(hw->wiphy, "allocating RX ring failed\n");
+	} else {
+		rc = mwl_rx_ring_init(priv);
+		if (rc) {
+			mwl_rx_ring_free(priv);
+			wiphy_err(hw->wiphy,
+				  "initializing RX ring failed\n");
+		}
+	}
+
+	return rc;
+}
+
+void mwl_rx_deinit(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *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;
+	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;
+
+	priv = hw->priv;
+
+	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");
+		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
+				 * mwl8k Firmware header is sent
+				 * to the host.
+				 *
+				 * We need to add four bytes of
+				 * key information.  In it
+				 * MAC80211 expects keyidx set to
+				 * 0 for triggering Counter
+				 * Measure of MMIC failure.
+				 */
+				if (status.flag & RX_FLAG_MMIC_ERROR) {
+					struct 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);
+		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;
+}
diff --git a/drivers/net/wireless/mwlwifi/rx.h b/drivers/net/wireless/mwlwifi/rx.h
new file mode 100644
index 0000000..d32c558
--- /dev/null
+++ b/drivers/net/wireless/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/mwlwifi/sysadpt.h b/drivers/net/wireless/mwlwifi/sysadpt.h
new file mode 100644
index 0000000..384ec34
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/sysadpt.h
@@ -0,0 +1,67 @@ 
+/*
+ * 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         1024
+
+#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_MAX_SIZE         3300
+
+#define SYSADPT_AMSDU_ALLOW_SIZE       1540
+
+#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/mwlwifi/tx.c b/drivers/net/wireless/mwlwifi/tx.c
new file mode 100644
index 0000000..979a2f6
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/tx.c
@@ -0,0 +1,834 @@ 
+/*
+ * 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
+};
+
+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 = (u8 *)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)
+{
+	*((u16 *)pccmp_hdr) = iv16;
+	pccmp_hdr[2] = 0;
+	pccmp_hdr[3] = EXT_IV | (key_id << 6);
+	*((u32 *)&pccmp_hdr[4]) = iv32;
+}
+
+static inline int mwl_tx_tid_queue_mapping(u8 tid)
+{
+	BUG_ON(tid > 7);
+
+	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;
+
+	BUG_ON(tid >= SYSADPT_MAX_TID);
+
+	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 mwl_vif *mwl_vif;
+	struct ieee80211_key_conf *k_conf;
+	bool ccmp = false;
+	struct mwl_dma_data *dma_data;
+	struct ieee80211_hdr *wh;
+
+	BUG_ON(!tx_skb);
+
+	tx_info = IEEE80211_SKB_CB(tx_skb);
+	tx_ctrl = (struct mwl_tx_ctrl *)&tx_info->status;
+	sta = (struct ieee80211_sta *)tx_ctrl->sta;
+	mwl_vif = (struct mwl_vif *)tx_ctrl->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 (mwl_vif->is_sta) {
+					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;
+	priv->desc_data[desc_num].pnext_tx_hndl = tx_hndl->pnext;
+	tx_hndl->psk_buff = tx_skb;
+	tx_hndl->sta_info = tx_ctrl->sta;
+	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;
+	tx_desc->pkt_ptr =
+		cpu_to_le32(pci_map_single(priv->pdev, tx_skb->data,
+					   tx_skb->len, PCI_DMA_TODEVICE));
+	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->fw_desc_cnt[desc_num]++;
+}
+
+static inline void mwl_tx_skbs(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv;
+	int num = SYSADPT_NUM_OF_DESC_DATA;
+	struct sk_buff *tx_skb;
+
+	priv = hw->priv;
+
+	spin_lock_bh(&priv->tx_desc_lock);
+	while (num--) {
+		while (skb_queue_len(&priv->txq[num]) > 0) {
+			if (mwl_tx_available(priv, num) == false)
+				break;
+			tx_skb = skb_dequeue(&priv->txq[num]);
+			mwl_tx_skb(priv, num, tx_skb);
+		}
+	}
+	spin_unlock_bh(&priv->tx_desc_lock);
+}
+
+int mwl_tx_init(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *priv;
+	int rc;
+
+	priv = hw->priv;
+
+	skb_queue_head_init(&priv->delay_q);
+
+	rc = mwl_tx_ring_alloc(priv);
+	if (rc) {
+		wiphy_err(hw->wiphy, "allocating TX ring failed\n");
+	} else {
+		rc = mwl_tx_ring_init(priv);
+		if (rc) {
+			mwl_tx_ring_free(priv);
+			wiphy_err(hw->wiphy, "initializing TX ring failed\n");
+		}
+	}
+
+	return rc;
+}
+
+void mwl_tx_deinit(struct ieee80211_hw *hw)
+{
+	struct mwl_priv *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;
+	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;
+
+	priv = hw->priv;
+	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) {
+		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)) {
+			u16 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);
+		}
+	}
+
+	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(&priv->stream_lock);
+		stream = mwl_fwcmd_lookup_stream(hw, sta->addr, tid);
+
+		if (stream) {
+			if (stream->state == AMPDU_STREAM_ACTIVE) {
+				WARN_ON(!(qos & MWL_QOS_ACK_POLICY_BLOCKACK));
+
+				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(&priv->stream_lock);
+				dev_kfree_skb_any(skb);
+				return;
+			}
+		} else {
+			/* Defer calling mwl8k_start_stream so that the current
+			 * skb can go out before the ADDBA request.  This
+			 * prevents sequence number mismatch at the recipient
+			 * as described above.
+			 */
+			if (mwl_fwcmd_ampdu_allowed(sta, tid)) {
+				stream = mwl_fwcmd_add_stream(hw, sta, tid);
+
+				if (stream)
+					start_ba_session = true;
+			}
+		}
+
+		spin_unlock(&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->tx_priority = txpriority;
+	tx_ctrl->qos_ctrl = qos;
+	tx_ctrl->type = (mgmtframe ? IEEE_TYPE_MANAGEMENT : IEEE_TYPE_DATA);
+	tx_ctrl->xmit_control = xmitcontrol;
+	tx_ctrl->sta = (void *)sta;
+	tx_ctrl->vif = (void *)mwl_vif;
+	tx_ctrl->k_conf = (void *)k_conf;
+
+	if (skb_queue_len(&priv->txq[index]) > priv->txq_limit)
+		dev_kfree_skb_any(skb);
+	else
+		skb_queue_tail(&priv->txq[index], skb);
+
+	mwl_tx_skbs(hw);
+
+	/* Initiate the ampdu session here */
+	if (start_ba_session) {
+		spin_lock(&priv->stream_lock);
+		if (mwl_fwcmd_start_stream(hw, stream))
+			mwl_fwcmd_remove_stream(hw, stream);
+		spin_unlock(&priv->stream_lock);
+	}
+}
+
+void mwl_tx_done(unsigned long data)
+{
+	struct ieee80211_hw *hw = (struct ieee80211_hw *)data;
+	struct mwl_priv *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, format, bandwidth, short_gi, rate_id;
+	struct mwl_dma_data *tr;
+	struct ieee80211_tx_info *info;
+	int hdrlen;
+
+	priv = hw->priv;
+
+	spin_lock_bh(&priv->tx_desc_lock);
+	for (num = 0; num < SYSADPT_NUM_OF_DESC_DATA; 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 */
+
+			tr = (struct mwl_dma_data *)done_skb->data;
+			info = IEEE80211_SKB_CB(done_skb);
+			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 (ieee80211_is_data(tr->wh.frame_control) ||
+			    ieee80211_is_data_qos(tr->wh.frame_control)) {
+				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));
+
+				/* 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;
+			}
+
+			/* 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_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;
+
+		status = readl(priv->iobase1 +
+			       MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+		writel(status | MACREG_A2HRIC_BIT_TX_DONE,
+		       priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK);
+
+		mwl_tx_skbs(hw);
+	}
+
+	priv->is_tx_schedule = false;
+}
diff --git a/drivers/net/wireless/mwlwifi/tx.h b/drivers/net/wireless/mwlwifi/tx.h
new file mode 100644
index 0000000..175437c
--- /dev/null
+++ b/drivers/net/wireless/mwlwifi/tx.h
@@ -0,0 +1,28 @@ 
+/*
+ * 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_done(unsigned long data);
+
+#endif /* _tx_h_ */